Implied substitution?

proposal

#1

Make a reserved identifier for incoming values,
Say that the reserved value were _,

map(x -> any(ismissing, x), itr)

would become

map(any(ismissing, _), itr)

or for piping

data = data |> (df -> categorical!(df, 1:3))
data = data |> categorical!(_, 1:3)

This is similar to tidyverse’s .. Any package that has an implementation?


#2

I think you’re looking for this. :wink:


#3

I knew I had seen it somewhere. I think there was a package that attempted something like that as well.


#4

at least for piping there is https://github.com/FNj/Hose.jl and https://github.com/oxinabox/Pipe.jl


#5

There is

but I guess it needs updating. With MacroTools.jl, this is rather easy to write from scratch, too.

I am skeptical about the utility though, compare

@p f(x, ...)
# and
x -> f(x, ...)

which is just 2 more keystrokes, nests well, and is free of corner cases. But I am skeptical about the whole tidyverse approach, it is extremely convenient for small examples but does not compose well.


#6

Well, 5 more keystrokes if it became a proper language feature (as in the PR), which would be a third of the whole expression. I would actually really like to see this feature as it adds, I think, a lot of convenience when sticking to a more functional programming style.


#7

This is only true in usage examples with a few short argument names—which is what people in the “numerical computing” side of the Julia community tend to encounter. On the “data analysis” side, which we have traditional not served as well, it’s very common to have more and longer names. For example:

(long_descriptive_name_1, long_descriptive_name_2, long_descriptive_name_3) -> f(long_descriptive_name_1, long_descriptive_name_2, long_descriptive_name_3)

verus

@p f(long_descriptive_name_1, long_descriptive_name_2, long_descriptive_name_3)

The point being that every time someone claims that this “only saves a few characters” they’re apparently willfully ignoring the fact that the savings is proportional to the number and length of the argument names, which is not fixed and will be much larger in some kinds of use cases than others.


#8

I may be missing something, but in a closure, the argument names are up to the user, aren’t they? Longer names can of course be more descriptive, but if that is desired, _ and _1 and the various other proposals for partial application syntax are probably not wanted anyway.

(Also, I am not willfully ignoring anything, just disagree about a technical point.)


#9

Even with one-letter names, the extra typing still scales with the number of arguments and there are many situations where one doesn’t particularly want to use single-letter names for clarity. Which is what makes the “it’s only two more keystrokes” response disingenuous—it’s only two extra keystrokes in the minimal case. It’s easily dozens of keystrokes in completely reasonable and realistic situations. There’s also the related issue of passing keyword arguments with the same name as the variable that’s passed, e.g. f(x=x) which is also dismissed as being “only two more keystrokes” but of course, that’s just the minimal case and it’s many more keystrokes in perfectly reasonable situations like:

f(keyword1 = keyword1, keyword2 = keyword2, keyword3 = keyword3)

versus

f(; keyword1, keyword2, keyword3)

Similarly for constructing named tuples with (; name1, name2, name3) syntax.


#10

I was actually looking for this sort of thing last night.

This is a big deal in data science because I have to have multiple people read my scripts without having a great idea of how Julia works. I need the names to be very explicit so they at least now what I’m acting on. I could always just make a comment in my script but then whoever reads it would have to learn how I code certain variables and Julia syntax. Unlike other mathematics I can’t just use sigma and then everyone will know what I’m talking about


#11

I agree with this. But I still don’t understand how this goal meshes well with the _ and _1 syntax of partial application. It is my understanding that one can have either informative (longer) variable names, or the partial application syntax (for those variables), but not both at the same time. If I misunderstand, please clarify.

You are right. I would frame it differently then: for the very simple cases which are most commonly used (closures with one or two arguments), the cost is small. For more complex cases, a the partial application syntax saves more keystrokes, but runs into other issues of complex/unclear semantics (as demonstrated by the discussion of #24990 and the related PRs/issues).

Just to clarify: I was initially very enthusiastic about partial application syntax because think it would save quite a bit of typing. Then having carefully read all of the above discussions, I now think that all the proposed rules have to be quite complex because of various corner cases.

It is possible to come up with well-defined semantics for these, but it would mean that I would need to remember some nontrivial rules about the _ syntax. This would make me treat it like precedence: I know that the rules exist, I know that they are well-defined, but I still don’t rely on all them since I would have to keep looking them up. So I would probably limit myself to the simple cases (like _ + y), in which case a closure is not that much of a bother.


#12

I guess for some functions that don’t support one argument methods, they could isequal(val) it.

julia> magic(x, y) = abs2(x - y) - 2
magic (generic function with 1 method)

julia> magic(y) = (x -> magic(x, y))
magic (generic function with 2 methods)

julia> 3 |> (x -> magic(x, 2))
-1

julia> 3 |> magic(2)
-1