lambdant v0.6.0
lambdant
A Javascript dialect for modern functional programming.
Table of Contents
Get
Globally install the cli tools:
# npm i -g lambdantLocally install the standard library:
$ npm i stdlmAdd the following line to your .vimrc for syntax highlighting:
au BufEnter,BufNewFile,BufRead *.lm set filetype=javascriptHello World
hello.lm
&'Hello, world!'$ lm e hello.lm
'Hello, world!'Purpose
Lambdant is a Javascript dialect that...
- is geared towards general-purpose functional programming
- encourages purity but does not require it
- provides easy and intuitive Javascript ffi
Ecosystem
- stdlm - standard library
- lambdant-loader - webpack loader
Files
./lang/grammar.gcontains the Jison grammar./lang/lexer.lcontains the Jison lex file./src/parser.jsis the parser script, compiled byjison ./lang/grammar.g ./lang/lexer.l -o .src/parser.js./src/gen.jsgenerates an ESTree compliant Javascript ast given a Lambdant ast./src/index.jsexposes three API calls:parse(LambdantSource) -> LambdantASTparses a Lambdant source string and returns a Lambdant astgenerate(LambdantAST) -> ESTreeASTconverts a Lambdant ast into an ESTree astserialize(ESTreeAST) -> JavascriptSourcegenerates Javascript source from an ESTree ast
Scripts
make build generates ./src/parser.js from ./lang/grammar.g and ./lang/lexer.l.make suite evaluates each of the scripts in ./examples.
Usage
CLI
dump: dump Lambdant source, Lambdant ast, ESTree ast, and Javascript source for a Lambdant file
$ lm d file.lmcompile: transform Lambdant source file into Javascript and write it to stdout
$ lm c file.lmeval: transform Lambdant source file into Javascript and run it in a new node process
$ lm e file.lmeval-arg: transform Lambdant source string into Javascript and run it in a new node process
$ lm a '& Math.log10 1000'
3Node
import { parse, generate, serialize } from 'lambdant'
import { readFileSync as read, writeFileSync as write } from 'fs'
const source = read('script.lm', 'utf8')
const lm_ast = parse(source)
const es_ast = generate(lm_ast)
const js_source = serialize(es_ast)
write('script.js', js_source)Note
These commands will automatically prepend an import statement for the standard library (@$ = require 'stdlm';) to each Lambdant source file. This is useful for testing and development, but not recommended for production code. To opt out, suffix the mode argument with a dash:
$ lm d- file.lm # dump file.lm without implicit prelude
$ lm c- file.lm # compile file.lm without implicit prelude
$ lm e- file.lm # evaluate file.lm without implicit prelude
$ lm a- 'code' # evaluate code without implicit preludeLanguage
Note: for all examples below, assume that standard library functions have been preassigned e.g.
@{ add, sub } = require 'stdlm';
add 2 (sub 2 1) -> 3Operator Associativity/Precedence
()
.
!
application
* !!
:
?/
&Values
2 -> 2
'abc' -> 'abc'
() -> null
<1, 2, 3> -> [1, 2, 3]
{ a: 5, b: 6, c } -> { a: 5, b: 6, c: c }Expressions
add 2 3 -> 5
console.log() // null
console.log! // (newline)Comments
- single comments begin with a
#and end with a newline - multiline comments begin with a
##and end with##or a single comment
# single comment
## begin comment
&'this code will not be reached'
# end comment
# begin comment
&'this code will be reached'
# end commentLambdas
# single lambda
[x: add 2 x] 3 -> 5
# nested lambda
[x:[y: add x y]] 2 3 -> 5
# nested lambda shorthand
[x y: add x y] 2 3 -> 5
# multivariate lambda
[x y, add x y] (2, 3) -> 5
# thunk
[:'in a thunk!']! -> in a thunk!
# noop
[:] -> (noop)
# destructuring lambda
[{ add } <a, b>: add a b] $ <1, 2> -> 3Blocks
Every statement in a block, except for the last, must be semicolon-terminated.
If the last expression is terminated, the function will return undefined, otherwise, it will return the expression.
@write = process.stdout.write : String;
write 'This will not return: ';
write [: 42;]!;
console.log!;
write 'This will return: ';
write [: 42]!;
console.log!
// This will not return: undefined
// This will return: 42Declaration
@some_varDefinition
# basic
@name = 'John';
name -> 'John'
# destructuring
@{ x } = { x: 40 };
@<y> = <2>;
add x y -> 42Assignment
# basic
@w;
w = 5;
w -> 5
# destructuring
@a; @b;
<a, b> = <1, 2>;
add a b -> 3;
# destructuring
@obj = { a: 5 };
obj.a = 42;
obj.a -> 42Conditionals
Conditionals take the form of test ? consequent : alternate. If you need to make multiple statements within a branch, wrap the branch in an immediately-invoked thunk.
is
not
mod
@odd = [x: (not : is 0) (mod x 2)];
@result = odd 3 ? 'odd!' / 'even!' -> 'odd!'Members
# native access
Date.now! -> 1481926731041
# computed access
<1, 9, 8, 4>.(2) -> 8Javascript FFI
Constructors
Javascript new has unconventional function call semantics; its arity changes depending on the context of the expression.
new (Date) // <Date>
new (Date)() // <Date>
const now = new Date
now() // Error!It is best to use a function with better defined arity, like create:
(create Array <5>).fill 0 -> [0, 0, 0, 0, 0]Multivariate Functions
The standard library provides currying and uncurrying facilities up to 5-arity.add
uncurry2
curry3
uncurry2 [x y: add x y] !! <1, 2> -> 3
uncurry2 [x y: add x y] (1, 2) -> 3
curry3 Date.UTC 1982 9 1 -> 402278400000
((Array 5).fill 0).map (uncurry2 [- i: i]) -> [0, 1, 2, 3, 4]You can also use multivariate lambda literals and spreads for multivariate calls.sum
[x y z, sum <x, y, z>] !! <1, 2, 3> -> 6
((Array 5).fill 0).map [- i, i] -> [0, 1, 2, 3, 4]Native Combinators
T-combinator (*)
reverse applicationadd
(2 * add) 1 -> 3infix expressionsadd
add 2 3 -> 5
2 *add 3 -> 5
3 *[x: add 2 x] -> 5reverse postfix expressionsadd
3 *(2 *add) -> 5B-combinator (:)
Composes functions.
add
(add 2 : add 1) 3 -> 6P-combinator (&)
Prints and returns the argument.
This combinator has the lowest precedence -- to avoid confusion, place a space between the combinator and the expression if the expression contains a space and is not delimited.
Calls log under the hood; reassigning $ will break this functionality.
add
&42 -> 42 // 42
& add 40 2 -> 42 // 42
&(add 40 2) -> 42 // 42E-combinator (!)
Evaluates a thunk. (Calls a function with zero arguments.)
This is preferable to calling with null (()).
process.exit!A'-combinator (!!)
Applies a sequence to a multivariate function. (Spreads an array into a function.)
Useful for Javascript FFI.
@date_args = <1982, 9, 1>;
Date.UTC !! date_args -> 402278400000
Date.UTC(1982, 9, 1) -> 402278400000TODO
- loops (optional, can be implemented with conditionals and recursion)
- dedicated syntax highlighting
- add tests