Functions

Though lists are a nice way of bundling code, they aren't perfect. For example, when creating your own named function, you will have to call it manually.

'[1 +] 'add-one def

1 add-one
;; Pushes the list to the stack, but doesn't call it
;; [1] -> [1 [1 +]]

call
;; Calls the list
;; [1 [1 +]] -> [2]

The fn Expression

For this reason, Stack provides parenthetical syntax to create functions. They function similar to lists, except they start with a fn or fn! identifier and use parenthesis instead of square brackets.

'(fn 1 +) 'add-one def

1 add-one
;; Pushes the list to the stack and calls it
;; [1] -> [2]

Notice how we didn't need to call the list manually? That's because the fn expression tells Stack to call the list automatically. This is known as auto-calling.

Note: The evaluation pattern of functions is the same as lists: left to right, evaluating each expression and pushing it to the stack.

Functions are Lists

Though functions are different from lists, you can still use most of the list methods on them (more info here). This means that you can build and modify functions at runtime.

'(fn)
1 push
'+ push
;; [] -> [(fn 1 +)]

'add-one def

1 add-one
;; Pushes the list to the stack and calls it
;; [1] -> [2]

Note: The fn expression needs to be made lazy (with ' -> 'fn) in order for it to not be evaluated. The engine does evaluate the fn expression, but it does nothing on its own (without being wrapped in a list).

Function Calling Behavior

When a function is pulled from scope, it will be auto-called when pushed to the stack. This is the behavior observed above, where add-one called the variable from scope, which Stack evaluated and called the function automatically.

Functions can also be called manually, producing the same behavior that auto-calling does.

For anonymous functions:

'(fn 2 2 +) call

;; Pushes 4 to the stack
;; [] -> [4]

For named functions

'(fn 2 2 +) 'add-two def

'add-two get
;; Pushes the list to the stack
;; [] -> [(fn 2 2 +)]

call
;; Calls the list
;; [(fn 2 2 +)] -> [4]

The get Operator

To get the function itself from the scope, to bypass auto-calling, you can use the get operator.

'(fn a) 'my-fn def

'my-fn get

;; Results in (fn a) being pushed to the stack
;; [] -> [(fn a)]

Scopeless Functions

Normal functions have their own isolated scope, it is not possible to define variables outside of the function's scope. Because of this, Stack includes a "mode" of function called a scopeless function. This is a powerful feature that enables for more dynamic meta-programming.

Scopeless functions don't have their own isolated scope and run in the scope that they are called in. This allows them to define or redefine variables directly in the scope that they were called in.

'(fn! 0 'a def) call

a

;; Pushes 0 to the stack
;; [] -> [0]
;; Create a scopeless function
'(fn! 0 'a def)

;; Create a normal function that calls the scopeless function
'(fn
  call

  ;; `a` is 0
)

;; Call the normal function
call

;; `a` doesn't exist here, since it was part of the previous function's scope

See Scopes for more information on how scoping works and how it relates to normal functions.