In particular I don't know what's meant by "curried" and "uncurried" languages
In particular I don't know what's meant by "curried" and "uncurried" languages
add :: Int -> Int -> Int
This is a function that takes one integer and returns a function which itself takes another integer and then finally returns an integer. Syntax sugar allows you to define it and call it like a single function.An uncurried function in these languages is a function that takes multiple arguments as a tuple.
add :: (Int, Int) -> Int
You don't see a lot of functions defined this way in Haskell because there isn't really any advantage to doing so in a lazy, pure language. It's more common in Standard ML from what I've seen, because SML is strict and impure.Currying is the conversion of an n-ary function into a chain of 1-ary function e.g. `fn(a, b, c) -> d` becomes `fn(a) -> fn(b) -> fn(c) -> d`.
Partial application is what it says on the tin, it only applies k parameters to the function returning an (n-k) parameters function, without actually running the function.
Currying is one method allowing partial application, but not necessarily the only one, or usually the best fit as it tends to not interact well with features like overloading, default parameters, keyword parameters, …
> In particular I don't know what's meant by "curried" and "uncurried" languages
Languages like Haskell, OCaml, or Elm are "curried" by default: when you define an n-ary function it's really just sugar for a chain of 1-ary functions; and the application of multiple arguments is similarly the implicit application of single arguments multiple times e.g.
map : (a -> b) -> List a -> List b
map f l =
case l of
[] -> []
(h::t) -> (f h) :: (map f t)
can be called as map fn [1, 2, 3]
or (map fn) [1, 2, 3]
the semantics are identical, which is not the case in an uncurried language like Javascript or Python: map(fn, [1, 2, 3])
and map(fn)([1, 2, 3])
will behave very differently unless the implementer has taken special steps to handle this case e.g. dynamic behaviour based on an optional second parameter, overloading, etc…What would be the use of this? Just delaying the execution of the function's side-effects?
I'll tip my hand: I'm working on a JS-like (and JS-targeted) language that I want to support partial-application/currying by default. Under the hood everything is represented in a curried way, but syntactically you can both define and invoke/partially-invoke functions with the more familiar syntax (commas). But this is fully superficial right now; it all happens at the parser level. Is there a downside to doing things this way?
For defaults/optionals, given "fn f(x, y, z=10)", if "f(1, 2)" evaluates to "f(1)(2)", does that return a function or the result of "f(1)(2)(10)"? What if you have "fn f(x, y = 10, z)"? All of this is a lot easier if you can evaluate the full argument list at the time of function invocation.
As for keyword arguments, they just don't play well with any kind of enforced ordering.
I wonder if that ambiguity could be resolved using contextual type info? I.e. if one of the two interpretations fits the expected type, use that. Otherwise, type-error.
Maybe that would be confusing, not sure.
I read your post yesterday. Good luck with your language!
To be honest this comments section has me seriously reconsidering the "everything is partially-applicable by default" feature, haha.