Showing with 150 additions and 120 deletions.
  1. +73 −43 lib/nothing.rb
  2. +77 −77 spec/nothing_spec.rb
116 changes: 73 additions & 43 deletions lib/nothing.rb
Original file line number Diff line number Diff line change
@@ -1,71 +1,101 @@
module Nothing
# Natural numbers

# ZERO =
# ONE =
# TWO =
# THREE =

# TIMES =
# INCREMENT =
# ADD =
# MULTIPLY =
# POWER =
# DECREMENT =
# SUBTRACT =
ZERO = -> f { -> x { x } }
ONE = -> f { -> x { f[x] } }
TWO = -> f { -> x { f[f[x]] } }
THREE = -> f { -> x { f[f[f[x]]] } }

TIMES = -> n { -> f { -> x { n[f][x] } } }
INCREMENT = -> n { -> f { -> x { f[n[f][x]] } } }

ADD = -> m { -> n { n[INCREMENT][m] } }
MULTIPLY = -> m { -> n { n[ADD[m]][ZERO] } }
POWER = -> m { -> n { n[MULTIPLY[m]][ONE] } }
DECREMENT = -> n { -> f { -> x { n[-> g { -> h { h[g[f]] } }][-> y { x }][-> y { y }] } } }
SUBTRACT = -> m { -> n { n[DECREMENT][m] } }

# Booleans

# TRUE =
# FALSE =
# IF =
TRUE = -> x { -> y { x } }
FALSE = -> x { -> y { y } }
IF = -> b { b }

# NOT =
# AND =
# OR =
NOT = -> b { IF[b][FALSE][TRUE] }
AND = -> a { -> b { IF[a][IF[b][TRUE][FALSE]][FALSE] } }
OR = -> a { -> b { IF[a][TRUE][IF[b][TRUE][FALSE]] } }

# Natural numbers with booleans

# IS_ZERO =
# IS_LESS_OR_EQUAL =
# IS_EQUAL =
IS_ZERO = -> n { n[-> x { FALSE }][TRUE] }
IS_LESS_OR_EQUAL = -> m { -> n { IS_ZERO[SUBTRACT[m][n]] } }
IS_EQUAL = -> m { -> n { AND[IS_LESS_OR_EQUAL[m][n]][IS_LESS_OR_EQUAL[n][m]] } }

# Combinators

Y = -> f { -> x { f[ x[x] ] }[-> x { f[ x[x] ] }] } # the famous one, for lazy languages like Haskell
Z = -> f { -> x { f[-> _ { x[x][_] }] }[-> x { f[-> _ { x[x][_] }] }] } # eta-expanded Y combinator, for eager languages like Ruby

# Natural numbers with recursion

# FACTORIAL =
# DIV =
# MOD =
FACTORIAL = Z[-> f { -> n { IF[IS_ZERO[n]][ONE][-> _ { MULTIPLY[n][f[DECREMENT[n]]][_] }] } }]
DIV = Z[-> f { -> m { -> n { IF[IS_LESS_OR_EQUAL[n][m]][-> _ { INCREMENT[f[SUBTRACT[m][n]][n]][_] }][ZERO] } } }]
MOD = Z[-> f { -> m { -> n { IF[IS_LESS_OR_EQUAL[n][m]][-> _ { f[SUBTRACT[m][n]][n][_] }][m] } } }]

# Pairs

# PAIR =
# LEFT =
# RIGHT =
PAIR = -> x { -> y { -> f { f[x][y] } } }
LEFT = -> p { p[-> x { -> y { x } } ] }
RIGHT = -> p { p[-> x { -> y { y } } ] }

# Lists

# EMPTY =
# UNSHIFT =
# IS_EMPTY =
# FIRST =
# REST =
EMPTY = PAIR[TRUE][TRUE]
UNSHIFT = -> l { -> x { PAIR[FALSE][PAIR[x][l]] } }
IS_EMPTY = LEFT
FIRST = -> l { LEFT[RIGHT[l]] }
REST = -> l { RIGHT[RIGHT[l]] }

INJECT = Z[-> f { -> l { -> x { -> g { IF[IS_EMPTY[l]][x][-> _ { f[REST[l]][g[x][FIRST[l]]][g][_] }] } } } }]
FOLD = Z[-> f { -> l { -> x { -> g { IF[IS_EMPTY[l]][x][-> _ { g[f[REST[l]][x][g]][FIRST[l]][_] }] } } } }]
MAP = -> k { -> f { FOLD[k][EMPTY][-> l { -> x { UNSHIFT[l][f[x]] } }] } }

# RANGE =
# SUM =
# PRODUCT =
# CONCAT =
# PUSH =
# REVERSE =
RANGE = Z[-> f { -> m { -> n { IF[IS_LESS_OR_EQUAL[m][n]][-> _ { UNSHIFT[f[INCREMENT[m]][n]][m][_] }][EMPTY] } } }]
SUM = -> l { INJECT[l][ZERO][ADD] }
PRODUCT = -> l { INJECT[l][ONE][MULTIPLY] }
CONCAT = -> k { -> l { FOLD[k][l][UNSHIFT] } }
PUSH = -> l { -> x { CONCAT[l][UNSHIFT[EMPTY][x]] } }
REVERSE = -> l { FOLD[l][EMPTY][PUSH] }

# INCREMENT_ALL =
# DOUBLE_ALL =
INCREMENT_ALL = -> l { MAP[l][INCREMENT] }
DOUBLE_ALL = -> l { MAP[l][MULTIPLY[TWO]] }

# Natural numbers with lists

# TO_DIGITS =
# TO_STRING =
TEN = INCREMENT[MULTIPLY[THREE][THREE]]
RADIX = TEN
TO_DIGITS = Z[-> f { -> n { PUSH[IF[IS_LESS_OR_EQUAL[n][DECREMENT[RADIX]]][EMPTY][ -> _ { f[DIV[n][RADIX]][_] } ]][MOD[n][RADIX]] } }]
TO_CHAR = -> n { n } # assume string encoding where 0 encodes '0', 1 encodes '1' etc
TO_STRING = -> n { MAP[TO_DIGITS[n]][TO_CHAR] }

# FizzBuzz

# FIZZBUZZ =
FOUR = INCREMENT[THREE]
FIVE = INCREMENT[FOUR]
FIFTEEN = MULTIPLY[THREE][FIVE]
FIZZ = MAP[UNSHIFT[UNSHIFT[UNSHIFT[UNSHIFT[EMPTY][FOUR]][FOUR]][TWO]][ONE]][ADD[RADIX]]
BUZZ = MAP[UNSHIFT[UNSHIFT[UNSHIFT[UNSHIFT[EMPTY][FOUR]][FOUR]][THREE]][ZERO]][ADD[RADIX]]

FIZZBUZZ =
-> m { MAP[RANGE[ONE][m]][-> n {
IF[IS_ZERO[MOD[n][FIFTEEN]]][
CONCAT[FIZZ][BUZZ]
][IF[IS_ZERO[MOD[n][THREE]]][
FIZZ
][IF[IS_ZERO[MOD[n][FIVE]]][
BUZZ
][
TO_STRING[n]
]]]
}] }
end
154 changes: 77 additions & 77 deletions spec/nothing_spec.rb
Original file line number Diff line number Diff line change
@@ -1,103 +1,103 @@
require 'spec_helper'

describe Nothing do
include Nothing

describe 'natural numbers' do
specify { pending { ZERO.should represent 0 } }
specify { pending { ONE.should represent 1 } }
specify { pending { TWO.should represent 2 } }
specify { pending { THREE.should represent 3 } }

specify { pending { TIMES[representation_of 3][-> s { s + 'o' }]['hell'].should == 'hellooo' } }
specify { pending { INCREMENT[representation_of 2].should represent 2 + 1 } }
specify { pending { ADD[representation_of 2][representation_of 3].should represent 2 + 3 } }
specify { pending { MULTIPLY[representation_of 2][representation_of 3].should represent 2 * 3 } }
specify { pending { POWER[representation_of 2][representation_of 3].should represent 2 ** 3 } }
specify { pending { DECREMENT[representation_of 3].should represent 3 - 1 } }
specify { pending { SUBTRACT[representation_of 3][representation_of 2].should represent 3 - 2 } }

context 'with booleans' do
(0..3).each do |n|
specify { pending { IS_ZERO[representation_of n].should represent n.zero? } }
specify { pending { IS_LESS_OR_EQUAL[representation_of n][representation_of 2].should represent n <= 2 } }
specify { pending { IS_EQUAL[representation_of n][representation_of 2].should represent n == 2 } }
module Nothing
describe Nothing do
describe 'natural numbers' do
specify { ZERO.should represent 0 }
specify { ONE.should represent 1 }
specify { TWO.should represent 2 }
specify { THREE.should represent 3 }

specify { TIMES[representation_of 3][-> s { s + 'o' }]['hell'].should == 'hellooo' }
specify { INCREMENT[representation_of 2].should represent 2 + 1 }
specify { ADD[representation_of 2][representation_of 3].should represent 2 + 3 }
specify { MULTIPLY[representation_of 2][representation_of 3].should represent 2 * 3 }
specify { POWER[representation_of 2][representation_of 3].should represent 2 ** 3 }
specify { DECREMENT[representation_of 3].should represent 3 - 1 }
specify { SUBTRACT[representation_of 3][representation_of 2].should represent 3 - 2 }

context 'with booleans' do
(0..3).each do |n|
specify { IS_ZERO[representation_of n].should represent n.zero? }
specify { IS_LESS_OR_EQUAL[representation_of n][representation_of 2].should represent n <= 2 }
specify { IS_EQUAL[representation_of n][representation_of 2].should represent n == 2 }
end
end
end

context 'with recursion' do
(0..5).zip([1, 1, 2, 6, 24, 120]) do |n, n_factorial|
specify { pending { FACTORIAL[representation_of n].should represent n_factorial } }
end
context 'with recursion' do
(0..5).zip([1, 1, 2, 6, 24, 120]) do |n, n_factorial|
specify { FACTORIAL[representation_of n].should represent n_factorial }
end

[0, 1, 11, 27].product([1, 3, 11]) do |m, n|
specify { pending { DIV[representation_of m][representation_of n].should represent m / n } }
specify { pending { MOD[representation_of m][representation_of n].should represent m % n } }
[0, 1, 11, 27].product([1, 3, 11]) do |m, n|
specify { DIV[representation_of m][representation_of n].should represent m / n }
specify { MOD[representation_of m][representation_of n].should represent m % n }
end
end
end

context 'with lists' do
specify { pending { TO_DIGITS[representation_of 42].should represent [4, 2] } }
end
context 'with lists' do
specify { TO_DIGITS[representation_of 42].should represent [4, 2] }
end

context 'with strings' do
specify { pending { TO_STRING[representation_of 42].should represent '42' } }
context 'with strings' do
specify { TO_STRING[representation_of 42].should represent '42' }
end
end
end

describe 'booleans' do
specify { pending { TRUE.should represent true } }
specify { pending { FALSE.should represent false } }
describe 'booleans' do
specify { TRUE.should represent true }
specify { FALSE.should represent false }

let(:foo) { Object.new }
let(:bar) { Object.new }
let(:foo) { Object.new }
let(:bar) { Object.new }

[true, false].each do |b|
specify { pending { IF[representation_of b][foo][bar].should equal(if b then foo else bar end) } }
specify { pending { NOT[representation_of b].should represent !b } }
[true, false].each do |b|
specify { IF[representation_of b][foo][bar].should equal(if b then foo else bar end) }
specify { NOT[representation_of b].should represent !b }

[true, false].each do |a|
specify { pending { AND[representation_of a][representation_of b].should represent a && b } }
specify { pending { OR[representation_of a][representation_of b].should represent a || b } }
[true, false].each do |a|
specify { AND[representation_of a][representation_of b].should represent a && b }
specify { OR[representation_of a][representation_of b].should represent a || b }
end
end
end
end

describe 'pairs' do
specify { pending { PAIR[representation_of 3][representation_of 5].should represent Pair.new(3, 5) } }
describe 'pairs' do
specify { PAIR[representation_of 3][representation_of 5].should represent Pair.new(3, 5) }

let(:foo) { Object.new }
let(:bar) { Object.new }
let(:foo) { Object.new }
let(:bar) { Object.new }

specify { pending { LEFT[PAIR[foo][bar]].should equal(foo) } }
specify { pending { RIGHT[PAIR[foo][bar]].should equal(bar) } }
end

describe 'lists' do
specify { pending { EMPTY.should represent [] } }
specify { pending { UNSHIFT[representation_of [2, 3]][representation_of 1].should represent [1, 2, 3] } }
specify { LEFT[PAIR[foo][bar]].should equal(foo) }
specify { RIGHT[PAIR[foo][bar]].should equal(bar) }
end

specify { pending { IS_EMPTY[representation_of []].should represent true } }
specify { pending { IS_EMPTY[representation_of [1]].should represent false } }
specify { pending { FIRST[representation_of [1, 2, 3]].should represent 1 } }
specify { pending { REST[representation_of [1, 2, 3]].should represent [2, 3] } }
describe 'lists' do
specify { EMPTY.should represent [] }
specify { UNSHIFT[representation_of [2, 3]][representation_of 1].should represent [1, 2, 3] }

specify { pending { RANGE[representation_of 2][representation_of 8].should represent [2, 3, 4, 5, 6, 7, 8] } }
specify { pending { SUM[representation_of [2, 2, 3]].should represent 7 } }
specify { pending { PRODUCT[representation_of [2, 2, 3]].should represent 12 } }
specify { pending { CONCAT[representation_of [1, 2]][representation_of [3, 2]].should represent [1, 2, 3, 2] } }
specify { pending { PUSH[representation_of [1, 2]][representation_of 3].should represent [1, 2, 3] } }
specify { pending { REVERSE[representation_of [1, 2, 3]].should represent [3, 2, 1] } }
specify { IS_EMPTY[representation_of []].should represent true }
specify { IS_EMPTY[representation_of [1]].should represent false }
specify { FIRST[representation_of [1, 2, 3]].should represent 1 }
specify { REST[representation_of [1, 2, 3]].should represent [2, 3] }

specify { pending { INCREMENT_ALL[representation_of [1, 2, 3]].should represent [2, 3, 4] } }
specify { pending { DOUBLE_ALL[representation_of [1, 2, 3]].should represent [2, 4, 6] } }
end
specify { RANGE[representation_of 2][representation_of 8].should represent [2, 3, 4, 5, 6, 7, 8] }
specify { SUM[representation_of [2, 2, 3]].should represent 7 }
specify { PRODUCT[representation_of [2, 2, 3]].should represent 12 }
specify { CONCAT[representation_of [1, 2]][representation_of [3, 2]].should represent [1, 2, 3, 2] }
specify { PUSH[representation_of [1, 2]][representation_of 3].should represent [1, 2, 3] }
specify { REVERSE[representation_of [1, 2, 3]].should represent [3, 2, 1] }

describe 'FizzBuzz' do
def fizzbuzz(m)
(1..m).map { |n| (n % 15).zero? ? 'FizzBuzz' : (n % 3).zero? ? 'Fizz' : (n % 5).zero? ? 'Buzz' : n.to_s }
specify { INCREMENT_ALL[representation_of [1, 2, 3]].should represent [2, 3, 4] }
specify { DOUBLE_ALL[representation_of [1, 2, 3]].should represent [2, 4, 6] }
end

specify { pending { FIZZBUZZ[representation_of 30].should represent fizzbuzz(30) } }
describe 'FizzBuzz' do
def fizzbuzz(m)
(1..m).map { |n| (n % 15).zero? ? 'FizzBuzz' : (n % 3).zero? ? 'Fizz' : (n % 5).zero? ? 'Buzz' : n.to_s }
end

specify { FIZZBUZZ[representation_of 30].should represent fizzbuzz(30) }
end
end
end