[ANN] Uncurried mode in ReScript v11

Hey folks,

We published another sneak-peak article on a new feature that is coming to ReScript v11:

Uncurried mode!

https://rescript-lang.org/blog/uncurried-mode

This feature is actually part of ReScript 11 since about half a year now. It is also the new default going forward. As the article mentions, you will still be able to use auto-currying in ReScript 11, but uncurried is a better fit for new developers with mainly JS/TS experience.

Please try out ReScript 11 in your codebases and help us with feedback, that would be really great!

How to use uncurried mode

  1. Install ReScript 11 in your project
    npm i rescript@next
    
  2. Don’t opt out from uncurried mode :slight_smile:

Happy Hacking!

15 Likes

Is the add(5, _) (or add(_, 5)) syntax discouraged? It enables some patterns that ... doesn’t, so maybe it’s worth mentioning as yet another tool for point-free programming?

3 Likes

No, it’s not discouraged at all. I forgot about it and so did my reviewers, apparently.
You are right, it needs some mention in the blogpost.

3 Likes

Ok, so to be clear, the _ syntax got a bit nerfed. If you want to partially apply everything but one parameter, it works the same. But if there are multiple parameters, you need to use the spread syntax now.

So while this works in both curried and uncurried mode:

let add = (a, b) => a + b
let addFive = add(5, _)

this only works in curried mode

let sum = (a, b, c) => a + b + c
let sumWith5 = sum(5, _)
let _ = sumWith5(1, 2)

and this only uncurried

let sum = (a, b, c) => a + b + c
let sumWith5 = sum(5, ...)
let _ = sumWith5(1, 2)

It was always the case that _ can only be used once.
Or rather, the implementation was never intended for multiple _ and replaces each one with the same value.

2 Likes

Would it be a nice addition in later versions?

I just want to say the syntax ya’ll have chosen represents currying so perfectly. Looking forward to 11, I know I’ve been burned a couple times with the let _ = forgotSecondArg(x)

2 Likes

Overal I like currying but I did experience unexpected behavior several times while writing bindings without disabling it.
I have mixed fillings about uncurried mode but on other hand partial application is still possible and annotation is more explicit… and final unit was sometimes weird.
I’m happy to give the new mode a try :smile:

3 Likes

I played a bit with it. I was afraid it would break a typical Rescript application, but it seems it won’t.

If functions are not curried by default I wonder if these are still going to work:

[1, 2, 3]->Js.Array2.map(x => x + 1)

But fortunately, it works, so thankfully the biggest Rescript feature is preserved :smiley:
To be honest, after depreciating pipe operators other than -> I always feel fearful about changes that may affect the remaining piping capabilities that in my opinion are essential to Rescript appeal. Especially because many modules are not rewritten to “data first” and it’s more practical to use Js.Array2 then Js.Array, but in many situations, it requires additional “boilerplate” with the underscore (ie. Some("test")->(Js.Option.map(x => x ++ "2", _))).
Having these underscore everywhere would be a nightmare.

If defined like this:

let p = Js.Array2.map(_, x => x + 1)
let res2 = [1, 2, 3]->p

Is kinda of fine, but I wonder how it works, that for pipes it’s somehow curried while for partially executed functions it doesn’t? :thinking:

By the way, I recommend you to start using rescript-core instead of the Js module.

2 Likes

As the uncurried mode side effect, passing a function as a callback is no longer possible.
This doesn’t work (with message: This function is a curried function where an uncurried function is expected):

Option.map(Js.Json.decodeString)

It has to be defined this way:

Option.map(Js.Json.decodeString(_))

This is a specific of built-in modules, which will probably be fixed in later versions (v11.x or v12).
As a work around I suggest you to use rescript-core instead of the Js/Belt modules.

2 Likes