Just to be clear, we have been discussing the uncurried-by-default topic because it really is a pain for many of our users to troubleshoot buggy behavior that was caused by unintentional currying. Just as @bsansouci said:
There are a lot of annoying traits that come with auto-currying. My personal nitpick is the final unit
argument for functions that use optional labeled arguments (let myFun = (~name=?, ())
). It’s hard to explain, and it doesn’t make any sense at all.
Also, you get into really weird type signature situations whenever you try to write externals to JS functions that simulate currying like this:
const myFunc = (context) => (moreContext) => {...}
I ask you to write bindings for this particular case. The problem is that ironically we can’t reflect that properly, because our functions are automatically curried, so:
// some_binding.resi
type context
type moreContext
let myFunc: context => moreContext => (unit => unit)
will be reformatted to:
let myFunc: (context, moreContext, unit) => unit
Yes, there are tricks to make the compiler understand our actual intent by introducing an intermediate type, but IMO this is mostly a hack:
type context
type moreContext
type retFn = unit => unit
type moreContextFn = moreContext => retFn
let add: context => moreContextFn
And again, we want to be able to sufficiently map to JS, and higher order functions are a common pattern. Auto-currying makes this harder for us.
Another issue is callback functions. Callback functions are pretty much everywhere, and theoretically, if we want to map to clean JS, we need to uncurry every single callback function that is passed to an external JS function:
external myCoolFn: ((.string) => unit) => unit
For these cases we need to educate our users that they need to explicitly uncurry those, otherwise they pull in the Curry runtime module (less idiomatic JS output, more infrastructural costs, and I am not even arguing about performance here).
Really unfortunate we have to explain the theory of currying / partial application just to make ppl emit the right JS code.
IF uncurried-by-default would actually be a thing, we wouldn’t remove curry functionality completely. It would just be an opt-in syntax, just like the (.) => {}
is an uncurried function right now.
I know where you are coming from. You have a FP background and you want to leverage all the FP patterns… but we are not necessarily advertising ReScript for its FP features, but more as a language to build type safe applications that compile to clean JS (with almost instant compilation times). I’d argue that we don’t care as much about subjectively clean looking code, but more about building solid products without much hassle or scientific type wrangling.
I guess we have enough users from the trenches that got beaten up by weird currying behavior so that it’s definitely worth to have these discussions. Not sure if it’s even technically feasible to make functions unurried by default, so everything we discuss here is pretty theoretical.