RFC: `&.` within a block

Here’s my 2 cents from someone seasoned in Ruby but fresh to Crystal.

If we really want to overload the & symbol for this then I strongly prefer that we require that a number is always suffixed, i.e., &1, &2, &3, &N.

The & can already find itself in some hairy readability situations today like aa.bb(&->cc.dd(T1, T2(TN))). When parsing this symbol thicket I don’t want to even remotely think that maybe, somehow, an ampersand in there can also refer to the first passed argument of a function.

Enforcing a number suffix would at least allow me to mentally parse code faster by instantly recognizing & + digit groups and shoving them aside in my brain instead of worrying about deciphering which particular use case of & is this.

Right now & has very clear purposes related to capturing and forwarding procs. Aliasing variable names is a very different and much simpler thing that I think deserves a more instantly recognizable visual shape.

I suggest we overload a less common symbol, like %N, 'N or ^N if feasible. Even @N would be okay IMO.

But definitely anything other than _1, _2, _N. There’s just something terribly unbalanced about it in my eyes :)

On the usefulness of this sort of aliasing I suspect this is less useful in Crystal due to what you can already accomplish with &->. In Ruby I do find myself needing it fairly often to avoid ballooning otherwise very short blocks. It’s definitely a sharp knife. Drink responsibly. Words over symbols :v:

4 Likes

Ruby introduced numbered parameters in 2.7 (released December 25, 2019).

A real use case: Fermat numbers - Rosetta Code

1 Like

I second this. (I would even go a step further, and suggest some new symbol, chosen from some unicode set. But that is obviously currently not very useful to type)

Just throwing my two cents in. Personally I like the idea of keeping the current syntax when it comes to simple things like arr.map(&.to_s), but within an actual block a number suffix should be enforced to reduce ambiguity.

So

foo do
  &0.first
  &1.second
end

It feels like it would be a fairly minimal addition, and wouldn’t break existing code. It is certainly less explicit though.

4 Likes

I would love exactly what you proposed. Sure it can be abused, but if used well it can be really handy IMO

kotlin it

Agreed so much here. I think the idea is great for writing the code but it makes reading it worse. Like Perl.

3 Likes

LOL!

The Safe Navigation Operator (&.) is the most highly unreadable syntax in this language, but Crystal devs love it. But but… we can’t have it inside methods though! :woman_shrugging: :man_shrugging:

Not sure why you call &. a “Safe Navigation Operator” though, it’s not an operator, more like a shorthand notation for blocks.
And it’s not about safe/unsafe navigation, the behavior is defined by the method you’re calling and has nothing to do with the &. syntax

1 Like

For reference: https://crystal-lang.org/reference/syntax_and_semantics/blocks_and_procs.html#short-one-argument-syntax.

I think it depends on the context. It can be abused and look bad, but in other instances it is quite nice because the variable is clear and coming up with a name just makes things more indirect. To me this is a sharp tool. Helpful, but can be misused. I think that can be ok if

Here’s an example of a pattern often used in Lucky Operations:

password.value.try do |value|
  copy_and_encrypt_password value, to: encrypted_password
end

The problem here is now “value” isn’t that clear, but I also can’t name it password. Basically “value” is a meaningly variable, but there isn’t really a better one

password.value.try do
  copy_and_encrypt_password &1, to: encrypted_password
end

To me this is fantastic. &1 is password.value. Nice! No need for a meaningless variable name like “value” or an unnecessarily long one like “password_value”

It’s a small win, but since this pattern is used all over Lucky (and most of my Crystal) projects to handle nil this really does save a lot of little papercuts.

Definitely not necessary, but would be a nice little win for the people that would like it. But I understand we can’t get everything so I get it if this is not added :)

3 Likes

Because that’s what it is called in Ruby. Once you figure out a official name for it in Crystal, let me know, instead of being pedantic.

&. in Crystal is &: in Ruby. There is no such thing as a Safe Navigation Operator in Crystal. So the official name would be Short one-argument syntax I suppose.

1 Like

Okay. I’ll call it Short one-argument syntax.

It has awful readability and creates confusing code. So why is it allowed to exist, but not the example in the original post?

3 posts were merged into an existing topic: Ban users that are hostile and destructive to the community

Not decided yet (in fact I implemented it without consulting or asking anyone), but it’s easy to implement it in the compiler:

4 Likes

Ruby seems to be getting it on top of _1, _2, _3 etc.

3 Likes

why not just _0?
come to think about it I also thought about this issue just yesterday after seeing ruby code with _1, _2.

IIRC there were some problems with this proposal: nesting blocks creating ambiguous references. I think we can do it this way instead: requiring another _ to refer to outer block anonymous block variables:

hash.each { # hash is Hash(Int, Array(Int322)
_2.map { _0 + __1 } # _1 here mapping to `value`, _0 is element of array, __1 refers to outer `key`
}

yeah this looks silly but you get the idea, dirty codes should look dirty so you want to clean it up later.

edit: personally I like \ more, eg, \1, \2. the reason is regex is already using this convention + \ is easier to type.

For what? it is equivalent to _1.
The reason to introduce it in Ruby seems to be that the underscore syntax comes off as weird for some people and that way you get at least a nice accessor for the first parameter :person_shrugging:

Please no. That’s going over the top. If you have to count the number of leading underscores to understand the code, you’ll soon get lost. Better use named arguments for this.
Multiple nested blocks doesn’t fit the use case for this feature, being a simple inline block.

Hm, an interesting fresh idea! Readability wise I would certainly prefer this over _1, _2 etc.

1 Like

For what? it is equivalent to _1.

I misunderstood the thread so I assumed that they want another keyword to capture all the block variables.

anyway, since crystal support automatic tuple unpack, this might cause real issues, plus sometime I want to capture all the block variables to pass it to other methods, too. having a capture all variable will be pretty convenience and also avoid a lot of confusion.

Please no. That’s going over the top.

that’s the point, for me _1, _2 mostly use for one time script so it should look dirty so you will have the urge to modify it when you decide to keep it longer.

Hm, an interesting fresh idea! Readability wise I would certainly prefer this over _1, _2 etc.

too bad \\1 will look even more silly, guess if we decide to use this then only one level would be allowed (also \1 looks less dirty so I will probably keep it in my code even after cleaning up)