From 1c231e31457b1f6e5c6ed1697f54219a9dff0e9d Mon Sep 17 00:00:00 2001 From: Ben Hall Date: Fri, 16 Feb 2018 14:11:35 +0000 Subject: [PATCH] feat(numerals): Add encodeNumeral and decodeNumeral --- .eslintrc | 3 +- dist/index.js | 36 +++++++++++++++---- src/numerals.js | 22 +++++++++--- test/_tools.js | 1 - test/lists.js | 91 +++++++++++++++++++++++----------------------- test/numerals.js | 94 ++++++++++++++++++++++++++---------------------- 6 files changed, 146 insertions(+), 101 deletions(-) diff --git a/.eslintrc b/.eslintrc index 7911627..4802cd6 100644 --- a/.eslintrc +++ b/.eslintrc @@ -2,6 +2,7 @@ "extends": "benji6", "rules": { "no-unused-vars": 0, - "no-use-before-define": 0 + "no-use-before-define": 0, + "prefer-spread": 0 } } diff --git a/dist/index.js b/dist/index.js index 7bda24c..1522075 100644 --- a/dist/index.js +++ b/dist/index.js @@ -109,11 +109,11 @@ var encodeBoolean = function encodeBoolean(a) { // This is how numerals are encoded. They take a function and a value then apply that function to the value or the previous result of application n times where n is the number being encoded. In JavaScript we can decode numerals simply like this: // ```javascript -// const decodeNumber = a => a(b => b + 1)(0) -// decodeNumber(zero) // => 0 -// decodeNumber(one) // => 1 -// decodeNumber(two) // => 2 -// decodeNumber(three) // => 3 +// const decodeNumeral = a => a(b => b + 1)(0) +// decodeNumeral(zero) // => 0 +// decodeNumeral(one) // => 1 +// decodeNumeral(two) // => 2 +// decodeNumeral(three) // => 3 // ``` // zero is the KI combinator just like False - not very type safe! @@ -260,6 +260,28 @@ var exp = function exp(a) { }; }; +// `decodeNumeral` takes a Church encoded numeral and returns the corresponding JS number +// ```javascript +// decodeNumeral(three) // => 3 +// ``` +var decodeNumeral = function decodeNumeral(a) { + return a(function (b) { + return b + 1; + })(0); +}; + +// `encodeNumeral` takes a JS number and returns the corresponding Church encoded numeral +// ```javascript +// encodeNumeral(3) // => three +// ``` +var encodeNumeral = function encodeNumeral(n) { + return function (f) { + return function (x) { + return Array.apply(null, { length: n }).reduce(f, x); + }; + }; +}; + // `isZero` takes a value and returns Church encoded `True` if it is a Church encoded `zero` and `False` otherwise // ```javascript // isZero(zero) // => True @@ -702,8 +724,6 @@ var reject = function reject(f) { })(nil); }; -// Welcome to the docs! - exports.True = True; exports.False = False; exports.If = If; @@ -730,6 +750,8 @@ exports.add = add; exports.sub = sub; exports.mult = mult; exports.exp = exp; +exports.decodeNumeral = decodeNumeral; +exports.encodeNumeral = encodeNumeral; exports.isZero = isZero; exports.lte = lte; exports.gte = gte; diff --git a/src/numerals.js b/src/numerals.js index 651a2a0..b482809 100644 --- a/src/numerals.js +++ b/src/numerals.js @@ -1,10 +1,10 @@ // This is how numerals are encoded. They take a function and a value then apply that function to the value or the previous result of application n times where n is the number being encoded. In JavaScript we can decode numerals simply like this: // ```javascript -// const decodeNumber = a => a(b => b + 1)(0) -// decodeNumber(zero) // => 0 -// decodeNumber(one) // => 1 -// decodeNumber(two) // => 2 -// decodeNumber(three) // => 3 +// const decodeNumeral = a => a(b => b + 1)(0) +// decodeNumeral(zero) // => 0 +// decodeNumeral(one) // => 1 +// decodeNumeral(two) // => 2 +// decodeNumeral(three) // => 3 // ``` // zero is the KI combinator just like False - not very type safe! @@ -64,3 +64,15 @@ export const mult = a => b => c => a(b(c)) // exp(three)(two) // => nine // ``` export const exp = a => b => b(a) + +// `decodeNumeral` takes a Church encoded numeral and returns the corresponding JS number +// ```javascript +// decodeNumeral(three) // => 3 +// ``` +export const decodeNumeral = a => a(b => b + 1)(0) + +// `encodeNumeral` takes a JS number and returns the corresponding Church encoded numeral +// ```javascript +// encodeNumeral(3) // => three +// ``` +export const encodeNumeral = n => f => x => Array.apply(null, {length: n}).reduce(f, x) diff --git a/test/_tools.js b/test/_tools.js index 1ca802e..3829338 100644 --- a/test/_tools.js +++ b/test/_tools.js @@ -3,4 +3,3 @@ import {foldl} from '../src' const append = xs => x => xs.concat([x]) export const decodeList = foldl(append)([]) -export const decodeNumber = a => a(b => b + 1)(0) diff --git a/test/lists.js b/test/lists.js index 78d920a..2acad84 100644 --- a/test/lists.js +++ b/test/lists.js @@ -1,5 +1,5 @@ import test from 'tape' -import {decodeList, decodeNumber} from './_tools' +import {decodeList} from './_tools' import { add, all, @@ -7,6 +7,7 @@ import { concat, cons, decodeBoolean, + decodeNumeral, drop, filter, foldl, @@ -66,52 +67,52 @@ test('Lists - all', t => { }) test('Lists - append', t => { - t.deepEqual(decodeList(append(four)(l123)).map(decodeNumber), [1, 2, 3, 4]) + t.deepEqual(decodeList(append(four)(l123)).map(decodeNumeral), [1, 2, 3, 4]) t.end() }) test('Lists - concat', t => { - t.deepEqual(decodeList(concat(l123)(l246)).map(decodeNumber), [1, 2, 3, 2, 4, 6]) + t.deepEqual(decodeList(concat(l123)(l246)).map(decodeNumeral), [1, 2, 3, 2, 4, 6]) t.end() }) test('Lists - cons', t => { const testList = cons(one)(cons(two)(cons(three)(nil))) - t.equal(decodeNumber(head(testList)), 1) - t.equal(decodeNumber(head(tail(testList))), 2) - t.equal(decodeNumber(head(tail(tail(testList)))), 3) + t.equal(decodeNumeral(head(testList)), 1) + t.equal(decodeNumeral(head(tail(testList))), 2) + t.equal(decodeNumeral(head(tail(tail(testList)))), 3) t.end() }) test('Lists - drop', t => { - t.deepEqual(decodeList(drop(one)(l123)).map(decodeNumber), [2, 3]) - t.deepEqual(decodeList(drop(two)(l123)).map(decodeNumber), [3]) + t.deepEqual(decodeList(drop(one)(l123)).map(decodeNumeral), [2, 3]) + t.deepEqual(decodeList(drop(two)(l123)).map(decodeNumeral), [3]) t.end() }) test('Lists - filter', t => { - t.deepEqual(decodeList(filter(lt(three))(l123)).map(decodeNumber), []) - t.deepEqual(decodeList(filter(lt(two))(l123)).map(decodeNumber), [3]) - t.deepEqual(decodeList(filter(lt(one))(l123)).map(decodeNumber), [2, 3]) + t.deepEqual(decodeList(filter(lt(three))(l123)).map(decodeNumeral), []) + t.deepEqual(decodeList(filter(lt(two))(l123)).map(decodeNumeral), [3]) + t.deepEqual(decodeList(filter(lt(one))(l123)).map(decodeNumeral), [2, 3]) t.end() }) test('Lists - foldl', t => { - t.equal(decodeNumber(foldl(add)(three)(l123)), 9) - t.deepEqual(foldl(push)([])(l123).map(decodeNumber), [1, 2, 3]) + t.equal(decodeNumeral(foldl(add)(three)(l123)), 9) + t.deepEqual(foldl(push)([])(l123).map(decodeNumeral), [1, 2, 3]) t.end() }) test('Lists - foldr', t => { - t.equal(decodeNumber(foldr(add)(three)(l123)), 9) - t.deepEqual(foldr(pushFlipped)([])(l123).map(decodeNumber), [3, 2, 1]) + t.equal(decodeNumeral(foldr(add)(three)(l123)), 9) + t.deepEqual(foldr(pushFlipped)([])(l123).map(decodeNumeral), [3, 2, 1]) t.end() }) test('Lists - head', t => { - t.equal(decodeNumber(head(l123)), 1) - t.equal(decodeNumber(head(tail(l123))), 2) - t.equal(decodeNumber(head(tail(tail(l123)))), 3) + t.equal(decodeNumeral(head(l123)), 1) + t.equal(decodeNumeral(head(tail(l123))), 2) + t.equal(decodeNumeral(head(tail(tail(l123)))), 3) t.end() }) @@ -122,18 +123,18 @@ test('Lists - isNil', t => { }) test('Lists - last', t => { - t.equals(decodeNumber(last(l123)), 3) + t.equals(decodeNumeral(last(l123)), 3) t.end() }) test('Lists - length', t => { - t.equals(decodeNumber(length(l123)), 3) - t.equals(decodeNumber(length(tail(l123))), 2) + t.equals(decodeNumeral(length(l123)), 3) + t.equals(decodeNumeral(length(tail(l123))), 2) t.end() }) test('Lists - map', t => { - t.deepEqual(decodeList(map(mult(two))(l123)).map(decodeNumber), [2, 4, 6]) + t.deepEqual(decodeList(map(mult(two))(l123)).map(decodeNumeral), [2, 4, 6]) t.end() }) @@ -148,28 +149,28 @@ test('Lists - none', t => { }) test('Lists - nth', t => { - t.equal(decodeNumber(nth(zero)(l123)), 1) - t.equal(decodeNumber(nth(one)(l123)), 2) - t.equal(decodeNumber(nth(two)(l246)), 6) + t.equal(decodeNumeral(nth(zero)(l123)), 1) + t.equal(decodeNumeral(nth(one)(l123)), 2) + t.equal(decodeNumeral(nth(two)(l246)), 6) t.end() }) test('Lists - prepend', t => { - t.deepEqual(decodeList(prepend(zero)(l123)).map(decodeNumber), [0, 1, 2, 3]) + t.deepEqual(decodeList(prepend(zero)(l123)).map(decodeNumeral), [0, 1, 2, 3]) t.end() }) test('Lists - range', t => { - t.deepEqual(decodeList(range(zero)(three)).map(decodeNumber), [0, 1, 2, 3]) - t.deepEqual(decodeList(range(one)(three)).map(decodeNumber), [1, 2, 3]) - t.deepEqual(decodeList(range(two)(three)).map(decodeNumber), [2, 3]) + t.deepEqual(decodeList(range(zero)(three)).map(decodeNumeral), [0, 1, 2, 3]) + t.deepEqual(decodeList(range(one)(three)).map(decodeNumeral), [1, 2, 3]) + t.deepEqual(decodeList(range(two)(three)).map(decodeNumeral), [2, 3]) t.end() }) test('Lists - reject', t => { - t.deepEqual(decodeList(reject(gte(three))(l123)).map(decodeNumber), []) - t.deepEqual(decodeList(reject(gte(two))(l123)).map(decodeNumber), [3]) - t.deepEqual(decodeList(reject(gte(one))(l123)).map(decodeNumber), [2, 3]) + t.deepEqual(decodeList(reject(gte(three))(l123)).map(decodeNumeral), []) + t.deepEqual(decodeList(reject(gte(two))(l123)).map(decodeNumeral), [3]) + t.deepEqual(decodeList(reject(gte(one))(l123)).map(decodeNumeral), [2, 3]) t.end() }) @@ -179,38 +180,38 @@ test('Lists - repeat', t => { }) test('Lists - reverse', t => { - t.deepEqual(decodeList(reverse(l123)).map(decodeNumber), [3, 2, 1]) + t.deepEqual(decodeList(reverse(l123)).map(decodeNumeral), [3, 2, 1]) t.end() }) test('Lists - slice', t => { - t.deepEqual(decodeList(slice(one)(two)(l123)).map(decodeNumber), [2]) - t.deepEqual(decodeList(slice(one)(three)(l123)).map(decodeNumber), [2, 3]) + t.deepEqual(decodeList(slice(one)(two)(l123)).map(decodeNumeral), [2]) + t.deepEqual(decodeList(slice(one)(three)(l123)).map(decodeNumeral), [2, 3]) t.end() }) test('Lists - sum', t => { - t.deepEqual(decodeNumber(sum(l123)), 6) - t.deepEqual(decodeNumber(sum(l246)), 12) + t.deepEqual(decodeNumeral(sum(l123)), 6) + t.deepEqual(decodeNumeral(sum(l246)), 12) t.end() }) test('Lists - take', t => { - t.deepEqual(decodeList(take(one)(l123)).map(decodeNumber), [1]) - t.deepEqual(decodeList(take(two)(l123)).map(decodeNumber), [1, 2]) - t.deepEqual(decodeList(take(three)(l123)).map(decodeNumber), [1, 2, 3]) + t.deepEqual(decodeList(take(one)(l123)).map(decodeNumeral), [1]) + t.deepEqual(decodeList(take(two)(l123)).map(decodeNumeral), [1, 2]) + t.deepEqual(decodeList(take(three)(l123)).map(decodeNumeral), [1, 2, 3]) t.end() }) test('Lists - tail', t => { - t.equal(decodeNumber(head(tail(l123))), 2) - t.equal(decodeNumber(head(tail(tail(l123)))), 3) + t.equal(decodeNumeral(head(tail(l123))), 2) + t.equal(decodeNumeral(head(tail(tail(l123)))), 3) t.end() }) test('Lists - zip', t => { t.deepEqual( - decodeList(zip(l123)(l246)).map(decodeList).map(xs => xs.map(decodeNumber)), + decodeList(zip(l123)(l246)).map(decodeList).map(xs => xs.map(decodeNumeral)), [ [1, 2], [2, 4], @@ -218,7 +219,7 @@ test('Lists - zip', t => { ] ) t.deepEqual( - decodeList(zip(l1234)(l246)).map(decodeList).map(xs => xs.map(decodeNumber)), + decodeList(zip(l1234)(l246)).map(decodeList).map(xs => xs.map(decodeNumeral)), [ [1, 2], [2, 4], @@ -230,7 +231,7 @@ test('Lists - zip', t => { test('Lists - zipWith', t => { t.deepEqual( - decodeList(zipWith(add)(l123)(l246)).map(decodeNumber), + decodeList(zipWith(add)(l123)(l246)).map(decodeNumeral), [3, 6, 9] ) t.end() diff --git a/test/numerals.js b/test/numerals.js index 3256871..c89f84d 100644 --- a/test/numerals.js +++ b/test/numerals.js @@ -1,5 +1,4 @@ import test from 'tape' -import {decodeNumber} from './_tools' import { zero, one, @@ -12,6 +11,8 @@ import { eight, nine, ten, + decodeNumeral, + encodeNumeral, succ, pred, add, @@ -21,70 +22,79 @@ import { } from '../src' test('Numerals - values', t => { - t.equal(decodeNumber(zero), 0) - t.equal(decodeNumber(one), 1) - t.equal(decodeNumber(two), 2) - t.equal(decodeNumber(three), 3) - t.equal(decodeNumber(four), 4) - t.equal(decodeNumber(five), 5) - t.equal(decodeNumber(six), 6) - t.equal(decodeNumber(seven), 7) - t.equal(decodeNumber(eight), 8) - t.equal(decodeNumber(nine), 9) - t.equal(decodeNumber(ten), 10) + t.equal(decodeNumeral(zero), 0) + t.equal(decodeNumeral(one), 1) + t.equal(decodeNumeral(two), 2) + t.equal(decodeNumeral(three), 3) + t.equal(decodeNumeral(four), 4) + t.equal(decodeNumeral(five), 5) + t.equal(decodeNumeral(six), 6) + t.equal(decodeNumeral(seven), 7) + t.equal(decodeNumeral(eight), 8) + t.equal(decodeNumeral(nine), 9) + t.equal(decodeNumeral(ten), 10) t.end() }) test('Numerals - successor', t => { - t.equal(decodeNumber(succ(zero)), 1) - t.equal(decodeNumber(succ(one)), 2) - t.equal(decodeNumber(succ(two)), 3) - t.equal(decodeNumber(succ(three)), 4) + t.equal(decodeNumeral(succ(zero)), 1) + t.equal(decodeNumeral(succ(one)), 2) + t.equal(decodeNumeral(succ(two)), 3) + t.equal(decodeNumeral(succ(three)), 4) t.end() }) test('Numerals - predecessor', t => { - t.equal(decodeNumber(pred(zero)), 0) - t.equal(decodeNumber(pred(one)), 0) - t.equal(decodeNumber(pred(two)), 1) - t.equal(decodeNumber(pred(three)), 2) + t.equal(decodeNumeral(pred(zero)), 0) + t.equal(decodeNumeral(pred(one)), 0) + t.equal(decodeNumeral(pred(two)), 1) + t.equal(decodeNumeral(pred(three)), 2) t.end() }) test('Numerals - add', t => { - t.equal(decodeNumber(add(zero)(zero)), 0) - t.equal(decodeNumber(add(two)(zero)), 2) - t.equal(decodeNumber(add(zero)(two)), 2) - t.equal(decodeNumber(add(one)(one)), 2) - t.equal(decodeNumber(add(two)(one)), 3) - t.equal(decodeNumber(add(one)(two)), 3) + t.equal(decodeNumeral(add(zero)(zero)), 0) + t.equal(decodeNumeral(add(two)(zero)), 2) + t.equal(decodeNumeral(add(zero)(two)), 2) + t.equal(decodeNumeral(add(one)(one)), 2) + t.equal(decodeNumeral(add(two)(one)), 3) + t.equal(decodeNumeral(add(one)(two)), 3) t.end() }) test('Numerals - sub', t => { - t.equal(decodeNumber(sub(three)(zero)), 3) - t.equal(decodeNumber(sub(three)(one)), 2) - t.equal(decodeNumber(sub(three)(two)), 1) - t.equal(decodeNumber(sub(three)(three)), 0) - t.equal(decodeNumber(sub(one)(three)), 0) + t.equal(decodeNumeral(sub(three)(zero)), 3) + t.equal(decodeNumeral(sub(three)(one)), 2) + t.equal(decodeNumeral(sub(three)(two)), 1) + t.equal(decodeNumeral(sub(three)(three)), 0) + t.equal(decodeNumeral(sub(one)(three)), 0) t.end() }) test('Numerals - mult', t => { - t.equal(decodeNumber(mult(zero)(zero)), 0) - t.equal(decodeNumber(mult(zero)(ten)), 0) - t.equal(decodeNumber(mult(one)(one)), 1) - t.equal(decodeNumber(mult(one)(two)), 2) - t.equal(decodeNumber(mult(two)(five)), 10) - t.equal(decodeNumber(mult(three)(four)), 12) + t.equal(decodeNumeral(mult(zero)(zero)), 0) + t.equal(decodeNumeral(mult(zero)(ten)), 0) + t.equal(decodeNumeral(mult(one)(one)), 1) + t.equal(decodeNumeral(mult(one)(two)), 2) + t.equal(decodeNumeral(mult(two)(five)), 10) + t.equal(decodeNumeral(mult(three)(four)), 12) t.end() }) test('Numerals - exp', t => { - t.equal(decodeNumber(exp(zero)(zero)), 1) - t.equal(decodeNumber(exp(ten)(zero)), 1) - t.equal(decodeNumber(exp(ten)(one)), 10) - t.equal(decodeNumber(exp(three)(two)), 9) - t.equal(decodeNumber(exp(three)(three)), 27) + t.equal(decodeNumeral(exp(zero)(zero)), 1) + t.equal(decodeNumeral(exp(ten)(zero)), 1) + t.equal(decodeNumeral(exp(ten)(one)), 10) + t.equal(decodeNumeral(exp(three)(two)), 9) + t.equal(decodeNumeral(exp(three)(three)), 27) + t.end() +}) + +test('Numerals - encodeNumeral', t => { + t.equal(decodeNumeral(encodeNumeral(0)), 0) + t.equal(decodeNumeral(encodeNumeral(1)), 1) + t.equal(decodeNumeral(encodeNumeral(2)), 2) + t.equal(decodeNumeral(encodeNumeral(3)), 3) + t.equal(decodeNumeral(encodeNumeral(127)), 127) t.end() })