(* Function-Closure Idioms *)
fun compose (f,g) = fn x => f (g x)
fun sqrt_of_abs i = Math.sqrt(Real.fromInt (abs i))
fun sqrt_of_abs i = (Math.sqrt o Real.fromInt o abs) i
val sqrt_of_abs = Math.sqrt o Real.fromInt o abs
(* tells the parser !> is a function that appears between its two arguments *)
infix !>
(* operator more commonly written |>, but that confuses the current version
of SML Mode for Emacs, leading to bad editing and formatting *)
(* definition of the pipeline operator *)
fun x !> f = f x
fun sqrt_of_abs i = i !> abs !> Real.fromInt !> Math.sqrt
(* old way to get the effect of multiple arguments *)
fun sorted3_tupled (x,y,z) = z >= y andalso y >= x
val t1 = sorted3_tupled (7,9,11)
(* new way: currying *)
val sorted3 = fn x => fn y => fn z => z >= y andalso y >= x
(* equivalent: *)
fun sorted3 x = fn y => fn z => z >= y andalso y >= x
val t2 = ((sorted3 7) 9) 11
(* syntactic sugar for calling curried functions: optional parentheses *)
val t3 = sorted3 7 9 11
(* syntactic sugar for defining curried functions: space between arguments *)
fun sorted3_nicer x y z = z >= y andalso y >= x
(* more calls that work: *)
val t4 = sorted3_nicer 7 9 11
val t5 = ((sorted3_nicer 7) 9) 11
(* calls that do not work: cannot mix tupling and currying *)
(*val wrong1 = ((sorted3_tupled 7) 9) 11*)
(*val wrong2 = sorted3_tupled 7 9 11*)
(*val wrong3 = sorted3 (7,9,11)*)
(*val wrong4 = sorted3_nicer (7,9,11)*)
(* a more useful example: fold left *)
fun fold f acc xs = (* means fun fold f = fn acc => fn xs => *)
case xs of
[] => acc
| x::xs' => fold f (f (acc,x)) xs'
fun fold f acc [] = acc
| fold f acc (x::xs') = fold f (f (acc,x)) xs'
(* a call to curried fold: will improve with partial application next *)
fun sum xs = fold (fn (x,y) => x+y) 0 xs
(* If a curried function is applied to "too few" arguments, that just returns
a closure, which is often useful -- a powerful idiom (no new semantics) *)
val is_nonnegative = sorted3 0 0
val yes = is_nonnegative 7
val sum = fold (fn (x,y) => x+y) 0
val sum = fold (op+) 0
(* In fact, not doing this is often a harder-to-notice version of
unnecessary function wrapping, as in these inferior versions *)
fun is_nonnegative_inferior x = sorted3 0 0 x
fun sum_inferior xs = fold (fn (x,y) => x+y) 0 xs
(* another example *)
fun range i j = if i > j then [] else i :: range (i+1) j
val countup = range 1
fun countup_inferior x = range 1 x
(* common style is to curry higher-order functions with function arguments
first to enable convenient partial application *)
fun exists predicate [] = false
| exists predicate (x::xs') = predicate x orelse exists predicate xs'
val no = exists (fn x => x=7) [4,11,23]
val hasZero = exists (fn x => x=0)
val incrementAll = List.map (fn x => x + 1)
(* library functions foldl, List.filter, etc. also generally curried: *)
val removeZeros = List.filter (fn x => x <> 0)
(* but if you get a strange message about "value restriction", just put back
in the actually-necessary wrapping or an explicit non-polymorphic type *)
(* does not work for reasons we will not explain here (more later) *)
(* (only an issue will polymorphic functions) *)
(* Does not work: *)
(* val pairWithOne = List.map (fn x => (x,1)) *)
(* workarounds: *)
fun pairWithOne xs = List.map (fn x => (x,1)) xs
val pairWithOne : string list -> (string * int) list = List.map (fn x => (x,1))
(* this different function works fine because result is not polymorphic *)
val incrementAndPairWithOne = List.map (fn x => (x+1,1))
(* generic functions to switch how/whether currying is used *)
(* in each case, the type tells you a lot *)
fun curry f x y = f (x,y)
fun uncurry f (x,y) = f x y
fun other_curry1 f = fn x => fn y => f y x
fun other_curry2 f x y = f y x
(* example *)
(* tupled but we wish it were curried *)
fun range (i,j) = if i > j then [] else i :: range(i+1, j)
(* no problem *)
val countup = curry range 1
val xs = countup 7
(* callbacks -- OPTIONAL *)
(* these two bindings would be internal (private) to the library *)
val cbs : (int -> unit) list ref = ref []
fun onEvent i =
let fun loop fs =
case fs of
[] => ()
| f::fs' => (f i; loop fs')
in loop (!cbs) end
(* clients call only this function (public interface to the library) *)
fun onKeyEvent f = cbs := f::(!cbs)
(* some clients where closures are essential
notice different environments use bindings of different types
*)
val timesPressed = ref 0
val _ = onKeyEvent (fn _ => timesPressed := (!timesPressed) + 1)
fun printIfPressed i =
onKeyEvent (fn j => if i=j
then print ("you pressed " ^ Int.toString i ^ "\n")
else ())
(*
val _ = printIfPressed 4
val _ = printIfPressed 11
val _ = printIfPressed 23
val _ = printIfPressed 4
*)