Functions

Defining Functions

defn declares a named function. Type annotations are positional — the type follows its argument:

(defn add [a int b int] -> int
  (+ a b))

(defn greet [name string] -> string
  (str "Hello, " name "!"))

Call them like any other expression:

(add 3 4)          ; 7
(greet "Alice")    ; "Hello, Alice!"

Multiple Return Values

Functions can return multiple values — this is how Go error handling works:

(defn divide [a float64 b float64] -> [float64 error]
  (if (= b 0.0)
    (values 0.0 (error "division by zero"))
    (values (/ a b) nil)))

Bind multiple return values with let:

(let [[result err] (divide 10.0 3.0)]
  (if err
    (println "error:" err)
    (println "result:" result)))

Anonymous Functions

fn creates an anonymous function:

(fn [x int] -> int (* x x))

; Used inline:
(map (fn [x] (* x 2)) [1 2 3 4])    ; [2 4 6 8]

Closures

Anonymous functions close over their enclosing scope:

(defn make-adder [n int] -> any
  (fn [x int] -> int (+ x n)))

(let [add5 (make-adder 5)]
  (add5 10))    ; 15

Variadic Functions

Use & rest to accept variable arguments:

(defn sum [& nums] -> int
  (reduce (fn [acc n] (+ acc (int n))) 0 nums))

(sum 1 2 3 4 5)    ; 15

Tail Recursion with loop/recur

GoLisp has no general TCO, but loop/recur gives you explicit tail calls:

(defn fib [n int] -> int
  (loop [i int n  a int 0  b int 1]
    (if (<= i 0)
      a
      (recur (- i 1) b (+ a b)))))

(fib 10)    ; 55

loop establishes the bindings; recur jumps back to the top with new values. The type annotations in loop keep arithmetic concrete — no boxing to any.

No-Argument and Void Functions

Functions with no parameters use an empty vector:

(defn greet-world [] -> void
  (println "Hello, World!"))

void means the function returns nothing (Go's no-return-value convention).