However, you’re not forced to use &.. You’re always free to choose what’s better for readability in every particular situation. I have a use-case where &. is more preferable. You have case where explicit argument is more preferable.
The whole proposed thing is syntax sugar. We use &. when we know what the argument exactly is and assume that the reader knows it too. Multiple block arguments are usually harder to remember (as well as their exact order), so it’s better to name them explicitly anyway. I don’t like Ruby’s new @1, @2 thing, I think it brings much more confusion to the code rather than improving it.
I think I get what you are saying, but I guess my point is that if we use &<int> it solves your use case and the one I presented at the same time. I agree more than one block var can be confusing, but it can be helpful at times too (each with a key/value pair for example)
So your use_case would be:
posts = Onyx.query(Post
.join(:author) do
&1.join(:settings) do
&1.select(:foo)
&1.where(active: true)
end
end
)
IMO that is fairly readable. With that said, even if we do what you said and no &<int> I’d be 100% for it. I’ve also needed similar things in my code
Given the example use case above, I think I would prefer the conventional notation:
posts = Onyx.query(Post
.join(:author) do |x|
x.join(:settings) do |y|
y.select(:foo)
y.where(active: true)
end
end
)
It’s a little bit more verbose, but that added expressiveness is actually a benefit IMO. The alternative (be it &1 or only &) is more confusing. For example, it’s hard to tell on the first look that the first call (#join) actually has a different target than the other two because they’re in a nested block.
posts = Onyx.query(Post
.join(:author) do
&.join(:settings) do
&.select(:foo)
&.where(active: true)
end
end
)
The amount of code saved is not that much, and the expressive version is also more flexible.
In this particular use case I agree. I actually didn’t realize they were different blocks at first glance. However I think the proposal is still useful. For example, imagine there was just 1 block there
Well, the same rule is applicable to with yield – block is the only indicator that something’s happening. However, we’ve got used to it. In fact, I see my proposal as improvement to with yield, which tries to preserve the API syntax but gives flexibility in block argument explicitness:
I think that a core aspect of nice DSL is that the receiver is implicit.
Although allowing the usage of &. multiple times in the body can reduce 3 chars it’s not very different to me. It also introduces ambiguity in the current language.
foo do
&.first &.second
end
It is not clear in the above example sends #second in the same context as #first or in the object that can be yielded by #first.
On another note, the index versions remind me too much to De Brüijn and it makes me happy, but I don’t think there is a real benefit. If you need to interact with multiple yielded values the code is probably more complex. If the code is more complex I think is better to let it break when moving it around. If we use names that work everywhere that will not happen.
I think it is still too unclear. For example, what if you want to pass those arguments in a different order?
So I agree that doing &n is clearer AND you could use it to pass arguments as is, like in Elixir
usernames.map(slugify(&1)
This would be SOO handy. I think you should still be able to leave off the n if you call a method on it and it is the only argument passed to the block.
I would just LOVE to have this. Another example using try where often I don’t really have a good name for the value to pass. Would be great to do this
email.try do
send_email(&1)
end
So nice. Now I don’t need to come up with a name. I can just pass the argument
Good point @Blacksmoke16! Thanks for the tip. Would still be cool for other types of methods like SomeModule.do_something(&1) but I didn’t think of doing &-> for some of the simpler cases
I guess we could copy Ruby and go with _1 or &1. Maybe &1 is better because it reminds of a block. I think it shouldn’t be that hard to implement… I do wonder whether the end result is more readable or more cryptic.
Yeah I personally like the & more. it stands out visually and is kind of like the existing &. Also matches what Elixir does exactly and that has worked well in that community
I think if abused it could lead to some super hard to read code, but I think that’s an ok risk. People can abuse all kinds of things :P If used well I think it can reduce mental overhead and make writing/reading easier in many cases.
I think in Elixir it’s a bit different because to use &1 and &2 you have to use an extra & before the expression. Like &foo(&1) or &(&1 + &2). So & as a prefix means “convert this to an fn”. In our case… I don’t know how would that work. I guess something like
[1, 2, 3].each_with_index { puts &1 + &2 }
But it’s not exactly like in Elixir. Using & as a prefix can’t work because that already means “pass this as a block argument”.
Personally I never quite got the point of these anonymous block arg thingies, in any language. Also it seems like something to open the door to many style discussions, especially when supporting more than a single one, since whether using them in any particular instance or not being still clear code seems quite subjective And then there will be people like me which will just never use them and then get tons of “why don’t you” on the code reviews.
So I don’t mind too much but if I had the decision, I wouldn’t do this. I definitely don’t miss anything for this.