A functional programming language with primitive data and a naive Javascript interoperability
def identity = { a -> a }
def compose = { f g x -> f $ g x }
def pipe = let swap = { a b c -> a c b } in swap compose
def _ = pipe identity identity
def response = 42
def hello = "Hello"
def pi = native "pi" (* pi is a JS function *)
def leq? = { l r t f → native "leq" } // l <= r ? t : f def mult = { l r → native "mult" } def minus = { l r → native "minus" }
def cond = { c t f → c t f () }
def fact = { a → cond (leq? a 1) { _ → 1 } { _ → mult a (fact (minus a 1)) } }
def _ = fact 12
def leq? = { l r t f → native "leq" } // l <= r ? t : f def mult = { l r → native "mult" } def minus = { l r → native "minus" }
def cond = { c t f → c t f () }
def fact = { a → cond (leq? a 1) { 1 } { mult a $ fact $ minus a 1 } }
def _ = fact 12
def yield = { v c → c v } def then = { v c → yield $ c v } def do = { c → then () c }
def _ = do { plus 20 4 } then { minus _ 1 } then { minus _ 2 } yield $ mult 2
# let yield a c = c a ;; val yield : 'a → ('a → 'b) → 'b = <fun>
# let _then v c = yield @@ c v ;; val _then : 'a → ('a → 'b) → ('b → 'c) → 'c = <fun>
# let _do c = _then () c ;; val _do : (unit → 'a) → ('a → 'b) → 'b = <fun>
# _do (fun () → "3") (* (string → '_a) → '_a *) _then int_of_string (* (int → '_b) → '_b *) _then (( * ) 4 ) (* (int → '_c) → '_c *) yield (( + ) 30 );; (* int *) - : int = 42
a (b (c d)) | ≡ | a $ b $ c d | Infix Application |
{ a → { b → ... } } | ≡ | { a b → ... } | Currified form |
{ a → plus a 42 } | ≡ | { plus _ 42 } | Implicit argument |
{ f a } | ≢ | ( f a ) | Abstraction vs. Application |
{ a → { b → f a } } | ≢ | { { f _ } } | Implicit variable scope |
Id ::= [a-zA-Z_][a-zA-Z0-9_$?]* exp ::= Id -- Identifier | Number -- Float literal e.g. -1.3e2 | String -- String literal e.g. "o_O" | native String -- Native binding | () -- Unit | ( exp+ ) -- Block of application | $ exp+ -- Infix application | { (Id+ →)? exp+ } -- Abstraction à la Kotlin | let Id = exp+ in exp+ -- Binding def ::= def Id = exp+ s0 ::= def*
type Ast =
| ident of String
| constant of Number | String
| native of String
| application of Ast * Ast
| abstraction of String * Ast
type Native = Number ∪ String P⟨_⟩ :: Sequence char → Ast | Error P⟨i⟩ = Ast.ident(i) if i ∈ Id P⟨n⟩ = Ast.constant(n) if n ∈ Native P⟨native s⟩ = Ast.native(s) if s ∈ String P⟨()⟩ = Ast.constant(unit) P⟨( a )⟩ = P⟨a⟩ P⟨$ a⟩ = P⟨a⟩ P⟨{ a -> b }⟩ = Ast.abstraction(a, P⟨b⟩)) if a ∈ Id P⟨let a = c in b⟩ = P⟨{ a -> b } c⟩ if a ∈ Id P⟨a b⟩ = Ast.application(P⟨a⟩,P⟨b⟩)
λ-terms representation without
naming the bind variables
λf.((λx.x) (λy.y) (λz.(f z)))
↓ ↓ ↓ ↓
λ.( (λ.0) (λ.0) (λ.(1 0)))
Invariant with respect to α-conversion and direct access in an environment
type AstDB =
| ident of String
| constant of Number | String
| native of String
| application of Ast * Ast
| abstraction of Ast (* No variable binding *)
| variable of Number (* Direct access *)
A⟨_⟩ :: Ast → List String → AstDB A⟨Ast.constant(c)⟩e = AstDB.constant(c) A⟨Ast.native(c)⟩e = AstDB.native(c) A⟨Ast.application(a,b)⟩e = AstDB.application(A⟨a⟩e, A⟨b⟩e) A⟨Ast.abstraction(a,b)⟩e = AstDB.abstraction(A⟨b⟩a::e) A⟨Ast.ident(n)⟩e = AstDB.ident(n) if ∀ i / e[i]≠n A⟨Ast.ident(n)⟩e = AstDB.variable(i) if ∃ i / e[i]=n ∀ j<i / e[j]≠n
type ResultI = Number | String | (AstDB,Env) and Env = List ResultI I⟨_⟩ :: AstDB → Env → ResultI | Error I⟨AstDB.constant(c)⟩e = c I⟨AstDB.variable(i)⟩e = e[i] I⟨AstDB.abstraction(b)⟩e = (b,e) I⟨AstDB.application(a,b)⟩e = I⟨c⟩d::e' when I⟨b⟩e =* d and I⟨a⟩e =* (c,e')
AstDB ⟹ Objcode
type Objcode =
| ident of String
| constant of Number | String
| native of String
| access of Number
| closure of List Objcode
| apply
| returns
C⟨_⟩:: AstDB → List Objcode C⟨AstDB.ident(n)⟩ = [Objcode.ident(n)⟩ C⟨AstDB.constant(c)⟩ = [Objcode.constant(c)⟩ C⟨AstDB.native(c)⟩ = [Objcode.native(c)⟩ C⟨AstDB.variable(i)⟩ = [Objcode.access(i)⟩ C⟨AstDB.application(a,b)⟩ = C⟨a⟩ @ C⟨b⟩ @ [Objcode.apply] C⟨AstDB.abstraction(b)⟩ = [Objcode.closure( C⟨b⟩ @ [Objcode.returns] )]
type ResultE = Number | String | (Objcode,Env) and Env = List ResultE and Stack = List (ResultE | Env | Objcode) E⟨_⟩ :: Objcode list → Env → Stack → ResultE | Error E⟨Objcode.access(i)::c⟩e s = E⟨c⟩e e[i]::s E⟨Objcode.closure(c')::c⟩e s = E⟨c⟩e (c',e)::s E⟨Objcode.apply::c⟩e v::(c',e')::s = E⟨c'⟩v::e' c::e::s E⟨Objcode.returns::_⟩e v::c'::e'::s = E⟨c'⟩e' v::s ...
definition :: String → ResultE native :: String → Env → ResultE E⟨Objcode.constant(n)::c⟩e s = E⟨c⟩e n::s E⟨Objcode.ident(n)::c⟩e s = E⟨c⟩e (definition n)::s E⟨Objcode.native(n)::c⟩e s = E⟨c⟩e (native n e)::s
Function with multiple arguments?
f a b c | ≡ | ((f a) b) c | Left associativity |
{ a → { b → ... } } | ≡ | { a b → ... } | Currified form |
Short lived closures construction and calls that returns immediately!
"From Krivine’s machine to the Caml implementations" - Xavier Leroy
Sequence char ⟹ ResultE | Error
<script type="application/mfun">
def compute_then_display = { v → do { changeDom "val" v } then { changeDom "res" $ fact v } yield { () } } def _ = compute_then_display 100
</script>
fact <i id='val'>?</i> = <i id='res'>?</i>