|
S-expressions are more like the tupled argument form, but better. (f x y z)
Is equivalent to: (f . (x . (y . (z . ())))
Every function takes one argument - a list.Lists make partial application simpler than with tuples (at least Haskell style tuples), because we don't need to define a new form for each N-sized tuple. Eg, in Haskell you'd need: partial2 : (((a, b) -> z), a) -> (b -> z)
partial3 : (((a, b, c) -> z), a) -> ((b, c) -> z)
partial4 : (((a, b, c, d) -> z), a) -> ((b, c, d) -> z)
...
With S-expressions, we can just define a partial application which takes the first argument (the car of the original parameter list) and returns a new function taking a variadic number of arguments (the cdr of the original parameter list). Eg, using a Kernel operative: ($define! $partial
($vau (f first) env
($lambda rest
(eval (list* f first rest) env))))
($define! f ($lambda (x y z) (+ x y z)))
(f 3 2 1)
=> 6
($define! g ($partial f 3))
(g 2 1)
=> 6
($define! h ($partial g 2))
(h 1)
=> 6
($define! i ($partial h 1))
(i)
=> 6
We could perhaps achieve the equivalent in Haskell explicitly with a multi-parameter typeclass and a functional dependency. Something like: class Partial full first rest | full first -> rest where
partial :: (full -> z, first) -> (rest -> z)
instance Partial ((a,b)) a b where
partial (f, a) = \b -> f (a, b)
instance Partial ((a, b, c)) a ((b, c)) where
partial (f, a) = \(b, c) -> f (a, b, c)
instance Partial ((a, b, c, d)) a ((b, c, d)) where
partial (f, a) = \(b, c, d) -> f (a, b, c, d)
...
|
That's one way to look at it, but the major difference is that one can also pass a a list as one argument, as in `(f x y z)` and `(f (list x y z)` are not the same. The thing with tuples is that a tuple of one datum is the very same as that datum itself and the same is true with the currying situation. `(f x) y` and `f x y` are truly one and the same in Haskell and Ocaml, just as `f(x, y)` and `let val a = (x,y) in f x` are one and the same in SML. This is not the case in Rust where `f(x,y)`, a function called with two arguments, and `f((x,y))`, a function called with one argument that is a tuple of two arguments are two different things.
There is also a difference in Scheme between returning a single value that is a list containing multiple values, and actually returning multiple values, In Rust however there is no difference between returning two values and returning a pair of two values. So Rust functions actually do properly take multiple arguments but always return a single one which may or may not be a tuple. In SML, Haskell and OCaml all functions technically take only one argument and return one value.