Idea: allow `||` instead of `|_|` in closures that ignore all parameters

For fairness, you should boldface the opposing arguments, too. Furthermore, points 2-4 (“readable”, “more readable” and “removes noise”) are basically the same.

I feel we should summarize the other arguments against the feature too, as some of them point out how the pro arguments are not exactly obvious/unequivocal/uncontroversial:

  • Although it’s “consistent” with rest patterns, its usage is as dangerous as the features it is similar to. The reason for this is that there will be no notice when a parameter is added which is potentially to be used for correct semantics.
  • It’s not considered more readable by everyone. Some prefer to see each argument; typing and reading a _ isn’t a real issue.
  • "opt-in" is only true if you don’t use anyone else’s code that relies on this feature. As long as you start using such code, you will be exposed to the potential bugs the feature may cause, even if you don’t use it yourself.
1 Like

@Centril

  • Your “more readable” point (point 3) is the exactly the same as your point 4. Point 4 is phrased better
  • I’m not sure what the second to last point means. Maybe I know it under another name. Is it the rest parameter?
  • I don’t consider the last point, the “opt-in” point, minor. It means a lot for people who don’t want to use it.

@H2CO3

  • All three points you’re listing make sense.
  • The reason behind why it is dangerous should be added to your first point. “The compiler can’t detect when the number of parameters changes”

Hence the minor point :wink:

I do think it is best that I not summarize the opposition too much; It is more genuine coming from someone who believes them more.

Readability is not the same as writing ergonomics (we should opt for the former, but it is nice when you get both added readability and writability).

@MajorBreakfast variadics: https://github.com/rust-lang/rfcs/pull/1935

Thanks, fair enough. I’ve added the reasoning to that point.

This holds. However the reason you list in point 3 ("more readable") appears again in point 4 ("ergonomic"). Meaning point 2 doesn't have a unique reason at all. Also, I think the points are so similar that combining them to just "ergonomic" makes more sense.

About the whether the "opt-in" point is minor: If you want to state that it is minor, you need to provide a reason why you consider it minor. Edit: I see the reason in the other post. I mean you should add it

1 Like

For the reasons @H2CO3 mentions. (or rather than "minor", I should say: weak argument).

I don't agree here and I only ever use the word "ergonomic" for the writing side of things (reserving "readability" for the other case) and I want to avoid overloading it. But... whoever writes the RFC can decide how they want to frame the motivation; But I think you and I basically agree on the substantive issues on the feature itself beyond phrasing a motivation :wink:

EDIT: let's not get bogged down in meta too much ^,-

Yes we do. And we both like it. And it's why I'd like to make a good case for it: This means each argument needs to be based on unique reasons. Otherwise the reader asks him-/herself "Didn't I just read that?" and that's no good.

1 Like

I like the idea of making the language more consistent with itself. There is already pattern matching going on when calling a function (fn foo(Pair(x, y): Pair) is a thing), so it would be good if the pattern matching syntax can be uniformly applied.

I have some questions that I hope someone can answer :slight_smile: First, what is the type of f in

let f = |..| { println!("mystery!") };

I suppose I can pass such a f to any function that accepts a callback? So

set_log_handler(f);

will work regardless of how the logging framework in question calls my f?

What if I use f twice:

f(10, 20);
f("Hello!");

Will that also work? I believe that would be the first time you can imagine this kind of syntax in Rust. I guess it shouldn’t work – perhaps the f does actually have a single concrete type and type inference will determine the type when it is first used?

What should the type inference module say if you do

let f = |..| { "first" };
let g = |..| { "second" };

g(f);

Here f is never applied to anything, so it’s difficult to say which type it has. Perhaps that’s okay?

If we have a |x, y, ..| syntax, does this mean that there is a subtype relationship between f and g in

let f = |x, ..|    { "at least one argument" };
let g = |x, y, ..| { "at least two arguments" };

Here you would expect to be able to use f everywhere you can use g, which would be the same as saying that f is a subtype of g. This is not necessarily a problem, but I wonder if we have such a hierarchy between function types today?

This is entering variadic function territory.

No, the function has a single, particular number of arguments, just as its arguments have a single particular type already. That is, an analogous situation occurs:

let f = |x| println!("{}", x);
f(0);
f("hello");

This does not compile, because the types of all arguments to f have to unify. Similarly, the number of arguments to f must also be consistent.

7 Likes

That said, if this ever gets implemented, and .. makes type inference ambiguous for a particular closure, that should definitely be an error, rather than the compiler trying to come up with some type out of thin air. For example, I would expect your first example,

let f = |..| { println!("mystery!") };

to not compile in itself, unless it’s used in a context from where the number and types of its arguments can be unambiguously deduced. Likewise, the third example,

let f = |..| { "first" };
let g = |..| { "second" };

g(f);

also makes the type of f impossible to infer in itself, so it should be an error too.

The second snippet:

f(10, 20);
f("Hello!");

should not compile either, since each closure has one specific type (basically impl Fn{Once,Mut,}).

It is possible to imagine or implement generic closures, although I would consider it massively harmful if “rest” parameter syntax suddenly also made closures generic.

1 Like

Right, so you're basically saying that the (first) use of f makes the type inference module pick a concrete type for the function. Other usages must then conform to this inferred type. Closures that ignore some arguments should then naturally follow the same rule.

What I was getting at was what exactly "ignore some arguments" means. An easy and consistent answer seems to be: the values are ignored — their types must still match.

I guess any context where you use a function will determine the number of arguments expected, and thus effectively turn |..| { ... } back into a |_, _, _| { ... } style closure with a known number of arguments (which can be 3 like here or any other number).

So I would hope that this kind of syntax doesn't add more corner-cases to the language.

1 Like

(minor nitpick which is irrelevant to the outcome: I think rustc applies a modified Damas-Milner inference, which is a constraint-based algorithm, so strictly speaking it's not necessarily the first use that picks a type, instead many uses might lead to many inferred types that do not unify.)

Certainly that should be true in practice; what I was trying to suggest is merely that if this is not true in a degenerate case, such as a .. lambda declaration without use, for instance, then that should not compile. Of course in practice we wouldn't often encounter such a situation (unless e.g. one forgets to use such a lambda); it's just that in principle it's safer to disallow any ambiguity at the type level.

2 Likes

I also assumed that this would work like that. Kinda like when we define a function which returns impl Trait. It's also just a placeholder for some concrete and unambiguously defined type.

1 Like

Getting off topic: Is there a meta-RFC with reasons that people are allowed to use when discussing relative merits of a [mis]feature? e.g. Wikipedia has the five pillars.

Rust used to have a lot of glyphs and I think they were rightly removed. It made it look like another stab at Perl (which I’m not terribly fond of). Injecting more notation into the language triggers me into worrying that we will creep towards a Pathologically Eclectic Rubbish Systems Language. But then I try to lurk more.

2 Likes

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.