Functions are the fundamental data type of M. Every type, except for a few primitives such as integers and expressions, is represented as a function, even typically intrinsic types like booleans and tuples. Functions in M are pure, meaning that they cannot have side effects like IO or mutation, and only ever take one argument (multi-argument functions are provided as syntax sugar).
Function application is of the form
(<fn> <args...>), where
fn is the
function to be applied and
args is a list of arguments to the function.
# true (not false) # 4 (add 1 3)
Functions are applied eagerly, and their evaluation order is undefined.
Function abstraction is of the form
(fn <args> <val>), where
a list of argument names and
val is the value of the function.
# The identity function which always returns its argument (fn x x) # The constant function which ignores its second argument (fn [x y] x) # The increment function (fn x (add x 1))
Closures are a property of functions where the variables of an outside scope are captured by a function. The values of these variables are stored in the function’s closure, and persist for as long as the function persists.
# The increment function expressed with closures; the variable x is bound # to 1, and the function of y stores this value in its closure ((fn x (fn y (add x y))) 1)
Curried functions are functions which use closures to take their arguments one at a time, allowing for partial application of their arguments. In M, all functions are curried, including internal functions.
# The increment function (add 1)
Multi-argument functions in M are just syntax sugar for curried functions. Likewise, multi-argument application is just syntax sugar for curried application.
# The two-argument application function with syntax sugar (fn [f a b] (f a b)) # The two-argument application function without syntax sugar (fn f (fn a (fn b ((f a) b))))