-
Notifications
You must be signed in to change notification settings - Fork 0
Functions in EK
Functions in EK are defined using the fn keyword.
Most functions in EK can be categorized into one of four types:
- Prefix functions: These are functions that take arguments after the function name. For example,
add 1 2calls theadd _ _function with the arguments1and2. - Infix functions: These are functions that take arguments before and after the function name. For example,
1 + 2calls the_ + _function with the arguments1and2. - Postfix functions: These are functions that take arguments before the function name. For example,
1!calls the!function with the argument1. - Ternary functions: These are functions that take arguments before, after, and between the function name. For example,
if a then b else ccalls theif _ then _ else _function with the argumentsa,b, andc.
To define a simple prefix function, you can write:
import std
fn add (a) (b) = a + b
This defines a function called add that takes two arguments, a and b, and returns their sum. The + infix function is defined in the std module, so you must import it to use it.
On the left side of the = sign, you can see the function name and its arguments. The arguments are enclosed in parentheses, to distinguish them from the function name.
On the right side, you can see the function body. The function body is an expression that is evaluated when the function is called. The result of the expression is returned as the result of the function.
Functions in EK can take any number of arguments, from zero to infinity.
To define a function that takes no arguments, you can write:
fn one = 1
As you can see, the function name is written alone on the left side of the = sign, since it takes no arguments.
The main function of an EK program is an example of a function that takes no arguments.
Functions in EK can take placeholder arguments, which can be used to define a function with respect to another function. The function body will then need to be a function itself, that takes the placeholder arguments as arguments.
This can be used to define an alias for a function:
import std
fn add _ _ = _ + _
The function here is defined with two placeholder arguments, _ and _, and directly aliases the _ + _ function. The placeholders will be passed in the same order as they are defined.
All functions in EK have a precedence, which determines the order in which they are evaluated. This is similar to the precedence of operators in other languages.
The precedence of a function is a number between 0 and 10, with 9 being the default. The higher the number, the more tightly the function binds to its arguments.
For example, the + function has a precedence of 6, while the * function has a precedence of 7. This means that 1 + 2 * 3 is equivalent to 1 + (2 * 3), and not (1 + 2) * 3.
The precedence of a function can be changed by using parentheses. For example, (1 + 2) * 3 is equivalent to 3 * 3.
To define a function with a different precedence, you can write:
import std
fn (a) +- (b) precedence 6 = a + b - b
This defines a function called +- that takes two arguments. The function has a precedence of 6, which means that it has the same precedence as the + function.
Function arguments in EK are evaluated strictly by default. This means that the arguments are evaluated before the function is called.
This can be changed by using the lazy keyword. For example, to define a function that takes a lazy argument, you can write:
import std
fn debug (lazy b) = print "DEBUG: " >> b
fn main = debug print "Hello, world!"
This defines a function called debug that takes a lazy argument. The argument, which has side effects, is not evaluated until the argument is used in the function body, which means "DEBUG: " is printed before "Hello, world!".
The if function is an example of a function that takes lazy arguments, since it only evaluates one of its branches.
Functions in EK can be recursive, which means that they can call themselves. This is the only way to create loops in EK.
As an example, here is a function that calculates the factorial of a number:
import std
fn factorial (n) = if n == 0 then 1 else n * factorial (n - 1)
As you can see, the function calls itself in the function body. Recursive functions can only work if they have a base case, which is a case where the function does not call itself. In this case, the base case is when n is equal to 0. If the base case is not reached, the function will call itself infinitely, which may cause a crash or an infinite loop. This would also happen if the if function did not take lazy arguments, since the recursive call would be evaluated even if the base case was reached.
Function bodies in EK are expressions, which means that they can be any valid expression in EK. You can also use the can find more information about expressions in the Expressions section.