Currying Guide

Turmeric supports Haskell-style currying: every N-argument function is also a valid (N-1)-argument function that returns a closure waiting for the remaining argument. There is no special syntax — supply fewer arguments than declared and the elaborator synthesizes a partial-application closure for you.


Quick reference

Expression f type Result type Behavior
(f a) (-> A B) B fully saturated
(f a) (-> A B C) (-> B C) partial application
(f a b) (-> A B C) C fully saturated
(f a b c) (-> A B C) where C = (-> D E) E over-application
((f a) b) (-> A B C) C explicit two-step, same as (f a b)

Partial application

Supply fewer arguments than the function's arity; the result is a closure waiting for the rest.

(defn add [a : int b : int] : int
  (+ a b))

(let [inc (add 1)]
  (inc 41))     ; => 42

The same works for built-in operators:

;; Operator section -- (+ 1) is a unary increment.
(map (+ 1) xs)

;; Fold with a partial-applied combiner.
(fold (+ 0) xs)

The partially-applied closure carries the full type of its remaining parameters, including type annotations on rank-2 polymorphic slots and effect rows. A partial application of an #fx{IO} function is itself #fx{IO}.


Over-application

When a function returns another function, you can chain calls inline:

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

(make-adder 10 5)     ; => 15, equivalent to ((make-adder 10) 5)

The elaborator detects that the intermediate result is callable and threads the remaining arguments into it. Chains of arbitrary depth work the same way.

Over-applying past a non-function return type is a type error:

(add 1 2 3)
; TUR-E0002: function returns int, which is not callable --
;            did you mean to pass all 2 arguments?

The curry macro

For cases where explicit currying syntax reads better than partial application, stdlib/macros.tur provides:

;;; curry -- convert a 2-argument function to its curried form.
(defmacro curry [f]
  `(fn [a] (fn [b] (~f a b))))

Usage:

(defn add [a : int b : int] : int (+ a b))

(let [add1 ((curry add) 1)]
  (add1 41))    ; => 42

Most of the time you don't need curry — direct partial application ((add 1)) is shorter and produces the same closure.


How it works

Turmeric uses the worker/wrapper model (the same approach as OCaml and MLton), not GHC-style full currying:

So (add 1) allocates a small heap closure that remembers 1, but (add 1 2) compiles straight to a C call with two arguments. The only function type in the type system is (-> ...); partial-application closures share it with everything else.


Limitations and gotchas


See also