The mLite language is mostly based upon expression evaluation. Expressions are composed of function application, infix operators, and some additional, more specialized constructs.
Like in ML, function application is expressed by juxtaposition
(a --> b
means "a
evaluates to b
"):
sqrt 2 --> 1.414213562373095 gcd (12,8) --> 4 map (fn x=2^x) --> fn a
Note that you will have to type a semicolon to make the mLite interpreter evaluate an expression!
Function applications associates to the left, so
map (fn x = 2^x) [1, 2, 3, 4, 5]
would in fact be
(map (fn x = 2^x)) [1, 2, 3, 4, 5]
which is quite convenient, because it first applies map
to the function fn x=2^x
, giving a new function, and
then applies that function to the list:
map (fn x = 2^x) --> fn a map (fn x = 2^x) [2,3,5,7,11] --> [4, 8, 32, 128, 2048]
In other circumstances, left-to-right evaluation makes less sense, though. For instance
println rev iota 100
should really be
println (rev (iota 100))
but this is ugly due to all of the extra parentheses. This is why
the "apply" operator (`
) exists:
println ` rev ` iota 100
The backtick in mLite is basically the same as the dollar operator in Haskell. It evaluates the left- and righthand side and then applies the left side to the right one.
Also note that, like in ML, function application binds stronger than infix operations so, for instance,
f x + g y
actually means
(f x) + (g y)
If something else is intended, the apply operator can help even here:
f ` x + g y
would mean
f (x + (g y))
Currying is present in the mLite language, but not used as frequently
as in the ML language, mostly because it cannot be combined with alternative
patterns (i.e. the |
operator). Functions use tuples instead
of currying for multiple arguments. e.g.:
fun power2 (x, 0) = 1 | (x, y) = power2 (x, y - 1)
However, a curried version of the function can easily be created:
fun power a b = power2 (a, b)
As in ML, this is equal to
fun power a = fn b = power2 (a, b)
In a typical higher-order function, a staged function using tuples
would be used internally and a function using the staged function would be
returned. The below zipwith
function incidentally returns
a curried function, because it expects three arguments.
fun zipwith f = let fun zip (a, [], r) = rev r
| ([], b, r) = rev r
| (a :: as, b :: bs, r)
= zip (as, bs, f (a, b) :: r)
in fn a b = zip (a, b, [])
end
This is actually the one area where mLite is currently less flexible than ML, because its compiler currently cannot rewrite cases using common patterns.