Functions

Maksim I

Functions

Functions are first-class values. The fn literal (see Literals) produces a function; let binds it to a name.

let greet fn [name] "Hello, {name}!"
greet :Alice      ; Hello, Alice!

let add fn [x y] + x y
add 3 4           ; 7

Arguments can be destructured directly in the argument list:

fn [[x y]] + x y        ; expects a two-element list, binds x and y
fn [[head #rest]] head  ; expects a list, binds head and rest

; Common in higher-order functions:
list.iterate (fn [[key val]] print "{key}: {val}") (object.entries obj)
list.sort (fn [[_ a] [_ b]] - a b) pairs

The same #rest pattern used in match works here: [x #rest] binds the first element to x and the remainder to rest.

Multi-expression body with '(...)

When a function body needs multiple statements, wrap them in '(...). The ' prefix distinguishes a block from a parenthesized expression. The last expression is the return value.

let fac fn [x] '(
  if (< x 2) $
    1 $
    (* x (fac (- 1 x)))
)

let counter fn [base] '(
  let x base
  let bump fn [much] set x (+ much x)
  bump             ; returned: a function that mutates x
)

'(...) can also be used standalone, outside a function, to sequence expressions.

Currying

Every function supports partial application. Provide fewer arguments than expected — get a new function waiting for the rest.

let add-one (+ 1)            ; function: add 1 to its argument
add-one 5                    ; 6

let write-to (file.write :out.txt)   ; function: write to out.txt
write-to "hello"             ; writes "hello" to out.txt

let inc (+ 1)
[1 2 3] $> list.map inc      ; [2 3 4]

let has-todo (string.has :TODO)
file.glob :./src/**/*.rs $> list.filter (file.read #> has-todo)

let subtract-one (- 1)       ; function: subtract 1 from argument
[10 20 30] $> list.map subtract-one   ; [9 19 29]

Currying makes #> and $> pipelines possible without lambdas in most cases.

Functions that cannot be curried (variadic / special forms): if, help, number.rand, list.range, shell.ask.

Argument order philosophy

Argument order is designed to maximize the usefulness of partial application. The convention: fix what you know, receive what varies last.

Arithmetic: MODIFIER first, BASE last.

- 1 5 = 4 (subtract 1 from 5). / 2 10 = 5 (divide 10 by 2). ^ 3 5 = 125 (raise 5 to power 3).

This is unconventional but makes currying uniform:

lst $> list.map (+ 1)    ; add 1 to each
lst $> list.map (- 1)    ; subtract 1 from each
lst $> list.map (* 2)    ; multiply each by 2
lst $> list.map (^ 2)    ; square each

All four lines follow the same pattern. If - worked as “first minus second,” (- 1) would mean “1 minus something,” breaking the symmetry. You would need fn [x] - x 1 instead.

Read: SPECIFIER first, then TARGET.

list.at 0 lst            ; which index, from which list
list.map (+ 1) lst       ; which function, on which list
string.has :a :banana    ; what to find, in what string
object.get :name obj     ; which key, from which object

Write/mutation: DESTINATION first, then CONTENT.

file.write :out.txt "content"    ; where, then what
file.copy :dest :source          ; where to, then from
list.set 0 lst 10                ; index, list, value
object.set :key obj val          ; key, object, value

This allows:

files $> list.iterate (file.copy :backup/)   ; copy each file to backup/

Composition with #>

f #> g creates fn [x] g (f x).

let read-lines (file.read #> string.lines)
let count-lines (file.read #> string.lines #> list.len)

; Point-free predicate
let has-todo (file.read #> string.has :TODO)
file.glob :./src/**/*.rs $> list.filter has-todo

; In a pipeline
file.glob :./src/**/*.rs $>
  list.map (file.read #> string.lines #> list.len) $>
  list.sum $>
  print