In elm, a function's value is computed when the last argument is applied. In the example below, the diagnostic from
log will be printed when f is invoked with 3 arguments or a curried form of f is applied with the last argument.
import String import Debug exposing (log) f a b c = String.join "," (log "Diagnostic" [a,b,c]) -- <function> : String -> String -> String -> String f2 = f "a1" "b2" -- <function> : String -> String f "A" "B" "C" -- Diagnostic: ["A","B","C"] "A,B,C" : String f2 "c3" -- Diagnostic: ["a1","b2","c3"] "a1,b2,c3" : String
At times you'll want to prevent a function from being applied right away. A typical use in elm is
Lazy.lazy which provides an abstraction for controlling when functions are applied.
lazy : (() -> a) -> Lazy a
Lazy computations take a function of one
Unit type argument. The unit type is conventionally the type of a placeholder argument. In an argument list, the corresponding argument is specified as
_, indicating that the value isn't used. The unit value in elm is specified by the special symbol
In our example,
f can be protected from being evaluated immediately with a lambda:
doit f = f () -- <function> : (() -> a) -> a whatToDo = \_ -> f "a" "b" "c" -- <function> : a -> String -- f is not evaluated yet doit whatToDo -- Diagnostic: ["a","b","c"] "a,b,c" : String
Function evaluation is delayed any time a function is partially applied.
defer a f = \_ -> f a -- <function> : a -> (a -> b) -> c -> b delayF = f "a" "b" |> defer "c" -- <function> : a -> String doit delayF -- Diagnostic: ["a","b","c"] "a,b,c" : String
Elm has an
always function, which cannot be used to delay evaluation. Because elm evaluates all function arguments regardless of whether and when the result of the function application is used, wrapping a function application in
always won't cause a delay, because
f is fully applied as a parameter to
alwaysF = always (f "a" "b" "c") -- <function> : a -> String -- Diagnostic: ["a","b","c"] -- Evaluation wasn't delayed.