├── .gitignore
├── .travis.yml
├── README.md
├── lambda.js
├── lambda.min.js
├── package.json
└── test
├── helper.js
├── index.html
└── test.js
/.gitignore:
--------------------------------------------------------------------------------
1 | lib-cov
2 | *.seed
3 | *.log
4 | *.csv
5 | *.dat
6 | *.out
7 | *.pid
8 | *.gz
9 |
10 | pids
11 | logs
12 | results
13 |
14 | npm-debug.log
15 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - "node"
4 | - "iojs"
5 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](http://travis-ci.org/dankogai/js-lambda)
2 |
3 | js-lambda
4 | =========
5 |
6 | DSL for, but not limited to, the lambda calculus.
7 |
8 | USAGE
9 | -----
10 |
11 | ### In Browser
12 |
13 | ````html
14 |
15 | ````
16 |
17 | ### node.js
18 |
19 | ````javascript
20 | var lambda = require('./lambda.js').lambda,
21 | λ = lambda;
22 | ````
23 |
24 | SYNOPSIS
25 | --------
26 |
27 | ````javascript
28 | lambda("x:x")(42) === 42;
29 | λ("x:x")(42) === 42; // λ = lambda
30 | λ("n:n<=1?n:n*_0(n-1)")(10) === 3628800; // _0 for recursion
31 | λ("x,y:Math.sqrt(x*x+y*y)")(3,4) === 5; // multiple arguments,
32 | λ("x:λ(y:Math.sqrt(x*x+y*y))")(3)(4) === 5; // λ can be nested
33 | ````
34 | ````javascript
35 | // church numerals
36 | var cn2num = λ("f:f(λ(n:n+1))(0)"),
37 | succ = λ("n:λ(f:λ(x:f(n(f)(x))))"),
38 | zero = λ("f:λ(x:x)"),
39 | one = succ(zero),
40 | add = λ("m:λ(n:m("+succ+")(n))"),
41 | two = add(one)(one),
42 | mul = λ("m:λ(n:m("+add+"(n))("+zero+"))"),
43 | four = mul(two)(two),
44 | pow = λ("b:λ(e:e(b))"),
45 | sixteen = pow(two)(four);
46 | cn2num(sixteen) === 16;
47 | ````
48 |
49 | DESCRIPTION
50 | -----------
51 |
52 | This script exports `lambda()` and its alias `λ()`. As seen in the synopsis, it is a DSL compiler that returns a function.
53 |
54 | ### the lambda notation
55 |
56 | As seen in [SYNOPSYS](#synopsis),
57 |
58 | `lambda(`*arg0,arg1,...argn*:*expression*`)`
59 |
60 | Turns into:
61 |
62 | `function(`*arg0, arg1, ...argn*`){return ` *expression* `}`
63 |
64 | ### nesting and recursion
65 |
66 | As seen in [SYNOPSYS](#synopsis), the lambda can be nested.
67 |
68 | ````javascript
69 | λ('x:λ(y:Math.sqrt(x*x+y*y))');
70 | ````
71 |
72 | Turns into:
73 |
74 | ````javascript
75 | function _0(x){return function _1(y){return Math.sqrt(x*x+y*y)}}
76 | ````
77 |
78 | As seen above, the function is named accordingly to [De Bruijin index]. `_`*n* is the nth level function.
79 |
80 | [De Bruijin index]: http://en.wikipedia.org/wiki/De_Bruijn_index
81 |
82 | Use the name to implement self-recursion. The strict mode has deprived us of beloved `arguments.callee` but with lambda.js, it is as short as `_0`.
83 |
84 | ````javascript
85 | // function fact(n){ return n <= 1 ? n : n * fact(n-1) }
86 | λ("n:n<=1?n:n*_0(n-1)");
87 | ````
88 |
89 | ### limitation
90 |
91 | To use lexical functions, you have to "interpolate".
92 |
93 | ````javascript
94 | var succ = λ("n:λ(f:λ(x:f(n(f)(x))))"),
95 | add = λ("m:λ(n:m("+succ+")(n))"); // λ("m:λ(n:m(succ)(n))") does not work
96 | ````
97 |
98 | This is because `lambda()` needs to `eval()` to compile the function but lexicals are out of its scope.
99 |
100 | ### memoization
101 |
102 | By default, the compiled function is [memoized]. Suppose you have:
103 |
104 | [memoized]: http://en.wikipedia.org/wiki/Memoization
105 |
106 | ````javascript
107 | function rms(ary) {
108 | return Math.sqrt(ary.reduce(λ("t,x:t+x*x"), 0))
109 | }
110 | ````
111 |
112 | And you use `rms` over and over, the same function is used throughout the session. If that is not what you want, you can suppress it by passing truish value to the second argument:
113 |
114 | ````javascript
115 | var uncached = lambda("a,b,c:...", true);
116 | ````
117 |
118 | And if you wish, you can inspect cached functions via `lambda.memo`.
119 |
120 |
121 | SEE ALSO
122 | --------
123 |
124 | + http://docs.python.org/2/tutorial/controlflow.html#lambda-forms
125 | + http://www.ruby-doc.org/core-2.0/Kernel.html#method-i-lambda
126 | + http://wiki.ecmascript.org/doku.php?id=harmony:arrow_function_syntax
127 |
--------------------------------------------------------------------------------
/lambda.js:
--------------------------------------------------------------------------------
1 | /*
2 | * $Id: lambda.js,v 0.2 2013/04/04 16:19:55 dankogai Exp dankogai $
3 | *
4 | * lambda.js
5 | *
6 | * (c) 2013 Dan Kogai
7 | *
8 | * Licensed under the MIT license.
9 | * http://www.opensource.org/licenses/mit-license
10 | *
11 | */
12 | (function(ctx) {
13 | 'use strict';
14 | var parse = function(src, lv) {
15 | var body = '', idx, m, i, d, l;
16 | while (m = src.match(/^([\s\S]*?)(lambda|λ)\(/)) {
17 | body += m[1];
18 | src = src.substr(m[0].length-1);
19 | for (i = d = 1, l = src.length; i < l; ++i) {
20 | if (src.charAt(i) === '(') d++;
21 | else if (src.charAt(i) === ')') d--;
22 | if (!d) break;
23 | };
24 | if (i === l) throw new SyntaxError('() mismatch');
25 | var t = src.substr(1,i-1);
26 | body += lambda(t, true, lv+1);
27 | src = src.substr(i+1);
28 | }
29 | return body + src;
30 | };
31 | function lambda(src, nomemo, lv) {
32 | if (!nomemo && src in lambda.memo) return lambda.memo[src];
33 | if (!lv) lv = 0;
34 | var parts = src.match(/^([^:]*):([\s\S]+)/),
35 | head = parts[1],
36 | body = parse(parts[2], lv);
37 | var fun = eval(
38 | '(function _' + (lv) + '('+head+'){return '+body+'})'
39 | );
40 | if (!nomemo) lambda.memo[src] = fun;
41 | return fun;
42 | };
43 | lambda.memo = {};
44 | ctx.λ = ctx.lambda = lambda;
45 | })(this);
46 |
--------------------------------------------------------------------------------
/lambda.min.js:
--------------------------------------------------------------------------------
1 | (function(ctx){"use strict";var parse=function(src,lv){var body="",idx,m,i,d,l;while(m=src.match(/^([\s\S]*?)(lambda|λ)\(/)){body+=m[1];src=src.substr(m[0].length-1);for(i=d=1,l=src.length;i
2 |
3 |
4 | Tester
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
24 |
25 |
27 |
28 |