Partial Application and _


#1

This code, amazingly, works:

let doStuff = (a: int, b: int) : int => a * a + b;
let partialStuff = doStuff(_, 4);

Js.log(doStuff(3, 4));
Js.log(partialStuff(3));

This seems to indicate that you can use _ to specify where the missing parameter goes when “filling in” a partial application. Is this by design, or is this just an accidental misfeature that could go away in a future release of ReasonML? (I suspect this has something to do with all functions being curried, but what do I know?)


#2

This is a recent feature that was explicitly added! like a month ago I believe.


#3

Yes, but it’s mostly supposed to work with |. and |>. Don’t use it elsewhere; we might remove it. We don’t wanna encourage this style too much.


The right Option module (and the rigth getWithDefault)
#4

Because of efficiency concerns?


#5

Thanks. Turns out that an ordinary anonymous function works better in the context that I need, anyway.


#6

:frowning: might remove it? This could be our path forward to requiring explicit partial application!


#7

@ncthbrt readability concern. It sucks reading a large body and then suddenly see an underscore and needing to go back and re-imagine things. Also, confusing for newcomers.

@jaredly it is yeah…


#8

I saw this technique mentioned on p. 56 of the pdf version of Web Development with ReasonML, and I immediately loved it. So, I’m sad to see that it might be removed.

I do understand the point about it being confusing for newcomers in reading pre-existing code, but this particular newcomer still hopes it becomes an official part of the language. Maybe the solution would be to have a section on “underscore” in the docs which would be easy to Google.


#9

For what it’s worth named arguments are great if you want to just partially apply one argument.

let subtract = (~left: int, ~right: int) => left - right;

let minus5 = subtract(~right=5);

let result = print_int(minus5(~left=10));

Playground: https://reasonml.github.io/en/try?rrjsx=true&reason=DYUwLgBAzgrgRmATgQwMaQLwQBQD9QBmYAXBAJYB2YANBLomQOYAWJ5VAlBBgHwSGQAtBAYswAbgBQk0JAC2lGFACs3aPCRoweUawzKOUmeBEhYwTBAAODKgH1K2hRSXK8AjAEYADB0NA

Although this doesn’t help you much with pipelines :man_shrugging:


#10

Regarding newcomers, I think the following is fair to say: A newcomer from Javascript will probably not immediately understand what the _ is doing there. But the newcomer is not likely to draw incorrect conclusions about what’s happening. Rather they’ll be motivated to ask someone or google it, thereby learning the language in more depth.

If there is really a concern that any newcomer must be able to easily read the code in a particular codebase, then developers can be asked not to use that feature (or other features whose meaning is not immediately obvious to people whose only background is in JS). It’s not the approach I’d take in my company, because I think there are a number of Reason features that would not be immediately obvious to JS programmers that are worthy of active use.


#11

Care needs to be taken with the placeholder syntax because it has a couple of important limitations.

  • It always refers to the same parameter in a function call, e.g. f(x, _, _) desugars to __x => f(x, __x, __x). This can be very surprising if you were expecting something else e.g. if you’re coming from Scala.
  • It only works in function calls, not in any arbitrary expressions e.g. 1 / _ does not work even though (/)(1, _) does (and the former actually desugars to the latter).

Given these limitations, and the fact that lambda syntax is already quite concise in Reason, I think it’s correct to deprecate underscore placeholders.


#12

Oh, I don’t know. Personally, I never got bitten by those limitation. Also, the ecosystem is abound with both t-first and t-last libs. Also, cometimes you want to pass a partially applied function as a callback, but it’s t-first and not t-last. Labelled parameters might be a solution, but the only BS-targeting standard lib I know of that goes labelled is Tablecloth.

So yeah, I’m just one voice, but I’m going to miss the underscores.

(Then again, the t-first choice is very polarizing for the community as well, but maybe we should trust the Reason team to know what they’re doing :smile:)


#13

I agree with @hoichi, the underscore was a good way to make partial application of t-first function. t-first and t-last both have their pros and cons but we have to know them and to deal with their limitations, may it be labeled arguments or underscore for partial applications. So far, the former is unfortunately almost non existent in common libs and the latter is deprecated. Of course we can use lambdas, but partial application is an important part of functional programming and I guess FP is what attracted most of us to Reason.


#14

Partial application is (almost certainly) not going away.


#15

Not a bad fallback in some cases, but not really a replacement for placeholders in that I’m not going to add labeled arguments to all of my functions, nor does it help me when using 3rd party libraries. As a side note, I’ve actually found it useful in some cases to use both features, e.g.:

...some stuff...
|> f(~named=_, 1, 2, 3)

That is pretty confusing. How difficult would it be to make it so that it desugars to something like (__x, __y) => f(x, __x, __y)? Or to just disallow the occurrence of multiple _s altogether? The former would be more robust and make the most sense, but either would be preferable to removing the feature altogether.

While this is a limitation, it doesn’t take away from the usefulness of supporting placeholders in function calls.