Functions

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

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

Function abstraction is of the form (fn <args> <val>), where args is 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

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)

Currying

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

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))))