1. Such bad examples :( Tuples are data types you have to destruct, in every language. Somebody please show me a language where this doesn't require a tuple-to-function-argument translation:
sayHi name age = "Hi I'm " ++ name ++ " and I'm " ++ show age
people = [("Alice", 70), ("Bob", 30), ("Charlotte", 40)]
-- ERROR: sayHi is String -> Int -> String, a person is (String, Int)
conversation = intercalate "\n" (map sayHi people)
In python you have `*people` to destruct the tuple into separate arguments, or pattern matching. In C-languages you have structs you have to destruct.
2. And performance, you'd think a slow-down affecting every single function call would be high-up on the optimization wish list, right? That's why it's implemented in basically every compiler, including non-fp compilers. Here's GHC authors in 2004 declaring that obviously the optimization is in "any decent compiler". https://simonmar.github.io/bib/papers/eval-apply.pdf
3. Type errors, the only place where currying is actually bad, is not even mentioned directly. Accidentally passing a different number of arguments compared to what you expected will result in a compiler error.
Some very powerful and generic languages will happily support lots of weird code you throw at them instead of erroring out. Others will errors out on things you'd expect them to handle just fine.
Here's Haskell supporting something most people would never want to use, giving it a proper type, and causing a confusing type error in any surrounding code when you leave out a parentheis around `+`:
foldl (+) 0 [1,2,3] :: Num a => a
foldl + 0 [1,2,3]
:: (Foldable t, Num a1, Num ((b -> a2 -> b) -> b -> t a2 -> b),
Num ([a1] -> (b -> a2 -> b) -> b -> t a2 -> b)) =>
(b -> a2 -> b) -> b -> t a2 -> b
Is it bad that it has figured out that you (apparently) wanted to add things of type `(b -> a2 -> b) -> b -> t a2 -> b` as if they were numbers, and done what you told it to do? Drop it into any gpt of choice and it'll find the mistake for you right away.
Blikkentrekker
today at 8:07 PM
In SML I believe. I never used SML but from how I understand it in ML all functions technically take one argument, which may be a tuple. In Haskell and Ocaml, all functions technically take one argument and just return a function that takes one argument again.
I never understood why the latter was so popular. Just for automatic implitic partial application which honestly should just have explicit syntax. In Scheme one simply uses the `(cut f x y)` operator which does a partial application and returns a function that consumes the remaining arguments which is far more explicit. But since Scheme is dynamically typed implicit partial application would be a disaster but it's not like in OCaml and Haskell the error messages at times can't be confusing.
I don't get simulating it with tuples either to be honest. Nothing wrong with just letting functions take multiple arguments and that's it. In Rust they oddly take multiple arguments as expect, but they can return tuples to simulate returning multiple arguments whereas in Scheme they just return multiple arguments. There's a difference between returning one argument which is a tuple of multiple arguments, and actually returning multiple arguments.
I think automatic implicit partial application, like almost anything āimplicitā is bad. But in Haskell or Ocaml or even Rust it has to be a syntactic macro, it can't just be a normal function because no easy variadic functions which to be fair is incredibly difficult without dynamic typing and in practice just passing some kind of sequence is what you really want.