A project for Hindley-Milner Typed Lisp
Unlike Clojure, HMTL provides type system and type inference during compiling/interpreting.
There are 5 basic types represented in HMTL:
- Numeric
- Byte
- Character
- List
- Function
HTML allows passing functors as arguments and works with them as with variable of every other type.
HMTL does not provide any functional polymorphism, it always works with 1-arity functions. That allows to store and pass "partially" applied functions as arguments to other functions.
Only first 4 types are related to existing Java types.
Now there is no way to call java methods from HMTL without breaking type system. D:
HTML grammar is the same as Clojure's. HTML syntax is derived from Clojure's with changes that allow to type inference.
Suppose f
is a 2-arity function. Following source would apply variables a
and b
to it.
(f a b)
In fact, due to carrying, source above is the same as source below.
((f a) b)
This part of source allows compiler to deduce following:
Type of f
is
(A -> (B -> F))
where type A
is common with variable a
(it can also be a function),
type B
is common with variable b
and F
is a resulting type after applying variable a
and b
.
Following construction allows to create an abstraction:
fn ?name ?:Type [a ?:Type b ?:Type] (BODY)
There are 5 parts in this declaration:
- Declaration macros
- Maybe naming
- Maybe type
- Arguments
- Function body
Function may be 'binded' to some name to be recursively called inside its body.
For example:
(fn fact [x] (if (= x 0) 1 (fact (- x 1))))
If function should return specific type, it can be explicitly assigned to the function. If function is may not return some specific basic type it can be used to show that type should be equal to something else.
Some examples:
This function should return Numeric as the result
(fn :Numeric [a b] (+ a b))
This function maps function f to every element of list l
(fn map :[Y] [l :[X] f :(X -> Y)] (concat (f (head l)) (map (tail l) f)))
Arguments list is just the arguments list :P.
It is being split in multiple abstraction for currying.
For example:
(fn [a b] (...))
has type: (a -> (b-> f))
Every variable may be binded to the name to be used further.
(def add (fn [a b] (+ a b)))
Multiple assignment may be applied inside block, rewriting constants for this block only:
(let
[
(name1 value1)
(name2 value2)
.
.
.
]
(body)
)
Here values value1
, value2
, ... are binded to name1
, name2
, ... correspondingly.