115 comments

[ 2.9 ms ] story [ 171 ms ] thread
I too was not thrilled to see the “_1” style in Ruby. I avoid it.
Arrays start at 1
We should have started arrays with -2147483648.
With wrap around! -2147493649 is the same as 2147493646. This is true even if your array has more than 2^32 elements. Also, 2147493647 is reserved for special use!
Kotlin has a lot useless syntax sugar, for example @decorators are less powerful than the @decorators in python.

And How do you say the opposite of sugar syntax in English? Because my lovely rustlang has a lot of sigil chars as old Perl, it makes me sad.

> And How do you say the opposite of sugar syntax in English?

Syntax spice ? It burns when you eat/read and it burns when you write/poo

> Because my lovely rustlang has a lot of sigil chars as old Perl, it makes me sad.

I unironically prefer Perl mess, at least you don't have macros and annotations generating who-knows-what at random parts of code.

I mean, not that you couldn't in Perl, there is plenty of horrid things you can do using some more obscure Perl's features (my favourite so far was modifying other package's package-scoped variables), but most code in Perl doesn't.

I’ve thought I had usually seen syntax salt be used as the opposite of sugar. I know there is some university’s PL Theory lecture guides that use it, just can’t think of where it is.

edit —- I just checked, and salt is ‘name dropped’ in the Wikipedia article for syntax sugar, and there were a few more links making me think it is relatively common.

syntactic broccoli.

The idea behind 'sugar' is 'tasty but inessential', so the strict opposite would be something essential but not so tasty.

But maybe 'syntactic thorns' correspond better to the sense you intend to express.

> Kotlin has a lot useless syntax sugar, for example @decorators are less powerful than the @decorators in python.

I think you're comparing two different things. Kotlin has "annotations", not decorators. It's because of it's java inheritance, where that is the way it's done. It's two different concepts. With a decorator, your code is replaced with a function call with the function itself as a parameter. With an annotation, you're just adding metadata and an annotation processor or static analysis tool etc can do whatever it wants with it. Like aspect oriented programming. You mark your code as having certain properties, and then other code can find it and react to those.

I don't know of much other useless syntactic sugar in Kotlin? I think most of it is quite nice, for instance how you can put the lambda outside parenthesis or create infix operator for more readable code. I also like the implicit `it` parameter in lambdas, better than what this article shows. Doesn't support multiple arguments, though.

Doesn't support multiple arguments by itself, because "it" will always refer to the "group". But with destructing you can name yourself as many arguments as you want:

    listOf(1,2,3).let { (a, b, c) -> a * b + c }
Which I think it's a good compromise
Sweet is usually contrasted with savory. I think "savory syntax" rolls off the tongue well.
Did the author of the feature missed Perl or something ?

Perl does that with few thing, like regexp captures. It's kinda sorta neat for writing shell oneliners but that's about it. But then concise and intelligent design wasn't ever Ruby's forte.

Thats not a very "intelligent" response to the article because it goes into detail about benefits the author sees in concision and communicating intent in normal code.
Well, Perl died because it's syntactically too sweet.
This is ironic given just how much of this syntax Ruby has lifted straight from Perl.

Including $1 etc. for regexp captures, and a litany of other "$" variables.

If anything, those parts of Ruby's design tend to be among the aspects most often maligned, and we have to excuse it with "Perl did it, blame Perl".

Ruby is in many way the unholy offspring of Perl and Smalltalk. And I mean that mostly in a good way, but it also gave us warts.

I would love for C++ to have more concise lambda syntax.

Having to write [](int x, int y) { return x*y } even for the simplest lambda is a pain.

Rust's syntax looks ideal: |x, y| x*y. Haskell's is also sweet.

For my own purposes I've defined macros so that I can write L1(x+5) or L2(x*y) (the x and y are hardcoded in). Not elegant but it keeps me sane.

Personally, I mostly want a more ergonomic way to write lambdas that I find more pleasant to type and read, even if it is as 'verbose'
Personally I don't think closures need any additional syntax, just reuse function definition syntax and implicitly capture its context based on variable usage.

I also hate the name "lambda" because it feels intentionally confusing and special. It's just a function that captures some context.

Boost.Lambda (https://www.boost.org/doc/libs/1_62_0/doc/html/lambda.html) is probably what you want, and even predates the introduction of proper lambdas into C++!

Here’s a few simple examples cribbed from their documentation:

    // Set all elements to 1
    for_each(a.begin(), a.end(), _1 = 1);
    // Print elements separated by spaces
    for_each(b.begin(), b.end(), std::cout << _1 << ' ');
    // Sort in reverse order
    sort(c.begin(), c.end(), *_1 > *_2);
The title is unfortunate: it seems like TFA will argue this syntax is indeed useless, while the article actually explains its rationale and comes to the conclusion it's quite useful.

It's unfortunate because readers on HN are already responding to the title instead of to the content...

TFA?
Some context on the acronym (which typically stands for "the fucking article"): https://news.ycombinator.com/item?id=19781756
Interesting… the first time I’ve come across a useless acronym.
I guess the more useful and original one is RTFA, and TFA is a derivative that gets used sometimes.
And RTFM predates that one. Read the f manual is a common reply to basic questions about tools and commands in Linux.
RTFA and TFA are not equivalent! One is a verb, "you should RTFA, it answers your question!" while the other is a noun, "TFA mentions this syntax is useful because [...]".

In any case, I made the mistake of assuming everyone is currently familiar with the idiom just because it was widespread when I joined HN.

I suppose at one point it'll be a mistake to assume everyone knows what ROTFL or LOL or IMO mean, just because they used to be widespread internet culture at one point.

I said "derivative", not equivalent.

Also, TFA used singularly is different from just saying "the article", e.g. when you want to imply RTFA. Within your comment you could see the "F" part was quite out of nowhere and can be considered rude.

> Within your comment you could see the "F" part was quite out of nowhere and can be considered rude.

Not if you see actual usage: https://hn.algolia.com/?dateRange=all&page=0&prefix=false&qu...

Context matters, and people here usually interpret it as "The Fine Article", and it was indeed written in that sense. It's supposed to be a shortcut (not rude like RTFM, which does have a meaning of impatience because it's directed at people perceived as too lazy to search the docs).

Anyway, a shortcut that requires this much explanation is a failed shortcut, and I apologize for that. It's sometimes hard for me to realize that what used to be a really common idiom that needed no explanations can quickly become something unfamiliar to new readers. Just in case, I won't use it again.

I miss being able to say "re" (re-hi or re-hello) instead of WB (welcome back).
(comment deleted)
It is quite useful; and if you read many HN comment sections, you will see it often.
It's a Slashdot-ism even older than HN
I like to read it as "the featured article".
I always understood it to stand for for "The Full Article", as in not just the title and not just any quoted excerpts (since Slashdot and some other sites it's used on have excerpts).
(comment deleted)
> TFA?

Sorry, didn't mean to confuse.

I've seen the idiom so many times here on HN I assumed it was widespread. It means "The Fine Article" (replace Fine with Fucking if feeling impatient).

It comes from the extremely well known idiom RTFM, for Read The Fucking Manual, used in unixy/hacker circles when someone asks for something trivially answerable by simply reading the manual.

I forgot that with new generations of techies, the old idioms become obscure or are replaced by newer idioms!

Well, I expected that, but slighlty hoped that ironical quotes will help (which is also part of the name of the series, started with explanatory intro[1]).

But I am afraid it is hard to do something to prevent people communicating with article title on its own. Happens all the time with me, whatever I write about! (I tried boring over-explanatory titles, people still argue with _that_.)

1: https://zverok.space/blog/2023-10-02-syntax-sugar.html

Numbers feel hacky. Rust's tuple.0 feels hacky.
I personally love Rust's tuple syntax. I wish more languages allowed [0] as .0.
Speaking as one of the eight people to ever write Elixir professionally, this was a small ergonomics feature that I really got to like (along with the `|>` pipe operator).
I understand the author's position, and despite its usefulness, I think the numbered arguments are an inelegant solution. In Haskell, by way of comparison, any one-parameter function can be used as the argument to map:

  ghci> map reverse ["hello", "goodbye"]
  ["olleh","eybdoog"]
is (mostly) equivalent to the more verbose form with an explicit lambda:

  ghci> map (\s -> reverse s) ["hello", "goodbye"]
  ["olleh","eybdoog"]
Operators are just normal functions, but tuples are not automatically uncurried. Here I'm using the multiply operator (*) to multiple a list of pairs:

  ghci> map (*) [(1,2), (3,4)]
  ERROR
  ghci> map (uncurry (*)) [(1,2), (3,4)]
  [2,12]
Points-free syntax can be used to construct chains of operations without any explicit lambda variable. Here filter strings of more than two characters:

  ghci> filter ((>2) . length) ["a","bb","ccc","dddd"]
  ["ccc","dddd"]
In lambda syntax:

  ghci> filter (\x -> (length x) > 2) ["a","bb","ccc","dddd"]
  ["ccc","dddd"]
I find the Haskell syntax concise and clear, and I always miss it when using other languages (despite Haskell's other shortcomings).

Edit: formatting

Currying is fun and I miss it sometimes as well, but as near as I can tell, it is essentially immiscible with traditional languages. I've seen so, so many attempts, and they are so, so bad.

Surprisingly few attempts even seem to get over the bar of understanding what currying is, and why it isn't just partial application. Now, I am not too concerned with which is "better" or anything, nor do I much care for pedantry for the sake of pedantry, but in this case it's a matter of type signatures; curry(some_function) and partial_apply(some_function, param1, param2) are fundamentally different type signatures and for that reason, not because of abstract verbal purity or something, aren't the same thing.

I mean, right out of the gate you have the problem that in most conventional languages you're looking at your first example being map(reverse)(["hello", "goodbye"]).

Whether Ruby syntax could be bent into making it work without parens (or Perl perhaps) I don't know but with all the other things going on in those syntaxes I'm sure it would be quirky as can be, possibly quirky beyond practical usefulness. And in dynamic scripting languages you'd be looking at a lot of actual function calls, so performance is going to be trashed if you use this pervasively.

I agree that the Haskell approach is not easily portable to other languages. At least according to the article, the Ruby community is struggling to find a solution. I don't think parentheses (or their absence) is a decided factor: for example, I don't find the hypothetical Ruby syntax ["hello","goodbye"].map(&reverse) to be offensive or wildly inconsistent. Note that the map function itself doesn't need partial application, only its function parameter.

For the same reason, I disagree that performance in dynamic scripting languages would suffer unduly.

> I don't find the hypothetical Ruby syntax ["hello","goodbye"].map(&reverse) to be offensive or wildly inconsistent.

This syntax wouldn't work in Ruby, because bare `reverse` is already a method call, not a reference to a method by name. Allowing method calls without parentheses is crucial to Ruby's design, where all objects are fully opaque, and every `obj.attr` looking like a getter, is just a call to an instance method `attr`. (This is, as far as I understand, the opposite to similar languages like Python/JS, where the object is a dictionary of attributes, and `obj.method` reference the attribute of type "method", while `obj.method()` is an invokation.)

This design became a huge drawback in the age of functional(ish) programming, because the simplest way for refer to a method in Ruby is `method(:its_name)` (which is also inefficient, because it creates wrapping object of class Method on the fly, there is no pre-existing first-class object), and any attempt of passing/combining methods would be cumbersome due to it.

FWIW, you can do that in Ruby:

  (JSON.method(:parse) >> method(:puts)).call("[1, 2, 3]")
...which is semantically cool, but looks ridiculous.

Another important trait of Ruby's design is that most of the important methods belong to their first argument, so it is not `reverse(string)` but `string.reverse`, so even to obtain a reference to a method, you need to have an object it belongs to! So you can't do that:

   method(:>).call(a, b)
...because all operators are called on their first operands, so you need this this:

   a.method(:>).call(b)
...which will require to refer to at least a first argument of the operator, so you can't produce "argument-less comparison operation to call later".
This is the sort of thing I meant by "essentially immiscible". At a 30,000 foot view, yeah, it looks like lots of languages could "just" add currying.

But when you get down into the weeds of what it means, it turns out to be something you can't "just" drop in. You really need to build the syntax for it from day one.

> you can't produce "argument-less comparison operation to call later".

You absolutely can, if you know the specific one (that is, type) you need.

E.g.,

  gt = Integer.instance_method(:>).method(:bind_call)
  # gt is a two arg callable functionally equivalent to ->(a,b) { a < b } for Integer a, Any b
Of course, doing this instead of just doing:

  gt = -> { _1 > _2 }
loses Ruby duck-typing and dynamism with regard to the first parameter; which may be acceptable in some cases, but not others. Can you do the general form without an explicit lambda? Probably there is a way, but its not easy.

  Surprisingly few attempts even seem to get over the bar of understanding what currying is, and why it isn't just partial application. 
Huh, what is the difference between currying and partial application? I always thought the terms were interchangeable.
> Huh, what is the difference between currying and partial application?

Partial application (of one arg) takes a function that is of type:

   (a, b, c) -> d
And makes it:

  a -> (b, c) -> d
Currying makes it:

  a -> b -> c -> d
Ruby’s Proc#curry does magic that mostly lets you pretend its either or something in between.
Just my opinion here...

The first example with map reverse is very readable and an improvement over lambda syntax. The chaining example (filter on length) is horrible for readability and I greatly prefer the lambda syntax.

"Just passing a function" is incompatible with Ruby's current syntax where a bare method name calls the method unless you then introduce a way for method definitions to defer evaluation of its arguments, but then you open a can of worms given that Ruby allows aliasing and replacing arbitrary methods of already defined classes. I suspect it'd be a nightmare of inconsistencies.
It’s a bit pedantic to mention, but there is #method(:method_symbol), and obviously &:method_symbol for some cases. Both of which give you a proc you could call later with arguments provided to it. It’s not “the function” since, it’s really an anonymous function, bound to something, that will send :method_symbol to that thing. Gets around the bare-method-runs-method and metaprogramming issues with current Ruby syntax.
The entire point was that those constructs that do not just pass the bare method or something like a Proc that allows you to call the method, are needed exactly because the syntax will not allow for just mentioning a bare method name without calling it.

If you're fine with the extra syntax, yes, there are many alternatives, but they don't address the point.

I made a small nod towards Haskell's way of doing things in the last paragraph of "How others do it" section:

> We might also start look into the concept of the “tacit programming”, which, from some point of view, is also about “not repeating the arguments,” but this would be a much longer post—while it is obscenely long already.

Note though that my analysis was mostly about "how you would do that in an established language," not "how would I design a language from scratch." And Ruby is very different from Haskell in its syntax, semantics, and typical intuitions of the language writer and reader.

So designing a solution for clearer representation of "default parameters" is quite different in a language which is built around an `object.method(argument) { block }` as its atomic phrase, and one built around `function(arguments)`. (This can turn into a discussion of which phrase structure is more elegant in general... But I believe it was already done quite a few times in a history of programming languages evolution!)

Ruby does allow you to pass a one-parameter function as the argument to `map`:

  irb(main):001:0> ["foo", "bar"].map(&:reverse)
  => ["oof", "rab"]
Not precisely the same as what you've outlined, of course, but perhaps useful nonetheless.
Fun fact, the arity of functions generated by Symbol#to_proc is not 1 but -2 (that is, additional arguments are passed through as well), so we can write stuff like

    module MissionControl
      #...
    
      [Proc, Method, Binding, UnboundMethod].each_with_object(self, &:include)
    end
if one were so inclined.
Huh - truly did not know about _negative_ arity in Ruby, and what that actually means. I learned something today, thank you!
> In Haskell, by way of comparison, any one-parameter function can be used as the argument to map

Ruby doesn’t have functions, but any one parameter block (to which any one parameter callable, and some other things, can be converted with an &) can be passed as the block parameter to map. And, yes, you can use function composition and similar operations to build up more complex callables from existing ones without an explicit new block or lambda.

Your examples mostly have pretty close parallels in ruby:

  irb(main):044:0> ["hello", "goodbye"].map &:reverse
  => ["olleh", "eybdoog"]
is the more compact form of the form with the explicit lambda:

  irb(main):045:0> ["hello", "goodbye"].map {|s| s.reverse}
  => ["olleh", "eybdoog"]
composition works similarly, though there is no compact notation to convert an operator and its right operand to a callable, only the left:

  irb(main):048:0> ["a","bb","ccc","dddd"].filter &(2.method(:<) << :length.to_proc)
  => ["ccc", "dddd"]
But, you do have to cheat a little to do:

  irb(main):049:0> [[1,2],[3,4]].map &:*.to_proc.uncurry
  => [2, 12]
in place of:

  irb(main):049:0> [[1,2],[3,4]].map {|x, y| x * y}
  => [2, 12]

Because while ruby has Proc#curry, it lacks Proc#uncurry. But its simple to define:

  class Proc
    def uncurry
      ->(args) { self.call *args }
    end
  end
> ghci> filter (\x -> (length x) > 2) ["a","bb","ccc","dddd"] ["ccc","dddd"]... I find the Haskell syntax concise and clear...

> ["a","bb","ccc","dddd"].filter &(2.method(:<) << :length.to_proc)

I can't imagine a stronger case for GP's thesis. The point-free ruby rewrite is so baroque and needlessly circuitous... the extra variable is a far lesser evil, and more concise:

    ["a","bb","ccc","dddd"].filter { |x| x.length > 2 }
my instinct is

    ["a","bb","ccc","dddd"].grep /.../
but then, I'm a recovering perl hacker
Yeah, mirroring the Haskell code, while possible in Ruby, is definitely not the best Ruby-aware approach, either for concision or clarity. The composability is available, but the problems where it is the best solution are different.
(comment deleted)
Even without patching the standard library:

    irb> [[1,2],[3,4]].each_with_object(:*).map(&:reduce)
    => [2, 12]
Given the mechanics of this I wouldn't claim it meets a strict definition of point-free, but that's an iterated application of operator-like methods omitting any explicit named/positional argument references.
I don't know how people feel about point-free these days. It was cool at first, then hated quite a lot due to issues I forgot (maybe a case or write-only code practice), but I find there's a high technical value and poetry to craft terms that plug together well with near no linking/binding declaration.
I try to optimize for the reader of the code who didn't write it who has to maintain it, which often feels like myself a year after I crack back open the file. From that perspective, I don't love _1. Basically I don't want to have to dereference things when possible, I want a direct reference. My working memory is not large, etc.

I'm not sure I see the value of ".then"... the one-off variable names on the side make it easy for me to scan quickly and understand what is happening w/o having to procedurally view the right side of the assignment. If you want to ensure that these vars are truly one-off, wrap it in a block or method.

I don't think Ruby's syntax needs to evolve. I just wish the core team cared more about opening up the lang to the improvements which were being offered by e.g Chris Seaton (RIP) and his colleagues. I wish typing proposals were being hotly debated etc. I'm sad that after 20 years I don't really want to use Ruby or Rails going forward despite how incredible the ecosystem really is, mostly because the data science, typing, and FE integration story is weak now.

As someone who loved rails, got a job writing typed python, then tried to go back to rails, the lack of typing really kills me.

I never realized how _nice_ it can be to have types in a dynamic language, even if they’re just annotations.

To be fair though Ruby is a much much harder language to wrangle under types than python.

Yeah I tried it a few times and there was usually pushback during code review. I agree though, saving a few characters is not worth the readability you lose vs even something like `users.map { |u| (...) }`
We outlawed it immediately via Rubocop but I use it all the time in IRB.

We also don't allow single letter variable names. I'm an old school rubyist and find this to be an annoying constraint that can't be auto-corrected so it adds a little friction to writing code but arguing for more terse code is always a losing proposition so I just deal with it.

Yes, in repl it's convenient. I don't mind single letter block var names if the block spans one line :)
`ii` it is then
sigh, I once encountered a PFY junior who named all variables some variation on `nx`, so `n1`, `n2`, `n99` because they 'were all just numbers'.
Using `i`, `j`, and `k` as index variables is not bad practice.
Well, my attempt at a humorous aside didn't land.

To be strictly serious we are talking about block parameters in ruby which contain the values yielded to it which, in an enumeration, is likely to be a value object from a collection and only occasionally an index (where you've explicitly chained `#with_index` for e.g.). If you are nesting deep enough in ruby to call for `k` then, yes, it would be bad practice.

I would quit if I had to write multi-character index variables.
Aesthetically, yes, I agree (subjectively) that "_1" is uglier than "|u|".

However, saying that the latter is "more readable" lacks foundation imo... both are conventions, both are simple to understand, and you can adapt to either quickly. I'd argue this is a case of "familiarity"/"readability" confusion.

A more practical argument is that "|u|" is more widely known by inexperienced ruby devs, and hence has an advantage on larger teams, especially those with turnover. With that said, I agree with author's position.

In practice, you wouldn't use |u| either. You'd use |user|. I would only allow single character variables if they are scoped entirely within one line of code. But even then, best practice is to use good variable names, so I'd still prefer `users.map{ |user| do_something(user) }` or whatever. I use the to_proc pattern for existing methods on the object like `users.map(&:email)`.

The only case where I've used a shorthand pattern like _1 is with regex matching where the matched groups are not named, so $1, $2, $3 refer to the first, second, third matched groups, etc. Having worked a lot with csvs (mentioned in an example), referencing the header (or creating one) has always been really valuable, using numbers is asking for trouble when the spec inevitably changes or when a dev wants to know if a script will work with some new file.

Imo using "good variable names" in cases where that variable is effectively functioning as nothing more than a syntactically necessary placeholder for a point-free phrase is a clear (if well-intentioned) mistake. It makes the code noisy and verbose for no benefit.

To take the exact example you gave, `users.map{ |u| do_something(u) }` is unambiguously better than `users.map{ |user| do_something(user) }`.

What we really want is `users.map{ do_something }`. It is only happenstance that ruby allows this form for methods only via `users.map(&:some_method)`, and we are stuck with forced variable names when calling other functions. Still, we can reduce the boilerplate as much as possible, and this is the basic argument of the article.

Also fine: `users.map{ |x| do_something(x) }` (the "x" as a conventional placeholder for the meaningless but syntactically necessary placeholder) and `users.map{ do_something(_1) }` (the _1 accomplishing the same thing). In either case, the "meaningful variable" is already there in the name "users" -- repeating it is mere stutter and detracts from, rather than enhancing, clarity.

The exception (as you noted) would be a longer multiline block where repeating `user` can aid short term memory, especially if the block contains other local variables.

(comment deleted)
> It is only happenstance that ruby allows this form for methods only via `users.map(&:some_method)`, and we are stuck with forced variable names when calling other functions.

Not sure what you are trying to say here, because there are so much odd about this that it is hard to penetrate.

First, & works on proc-able objects (those that are Procs or can be converted to them via #to_proc), not "methods", and what you show it being used with is a symbol, not a method. Symbol#to_proc on a symbol s produces a proc that is functionally something like:

  ->(*args, **kwargs) { args[0].send(s, args[1..-1, **kwargs] }
While that's complicated, the upshot is that it creates a proc that calls the method whose name is the symbol & was applied to on its first argument, passing its remaining arguments as the arguments to the method.

> What we really want is `users.map{ do_something }`.

But if you want to pass a method on self, the only thing in Ruby that you can call like do_something(x), to map, you can do that without an expllcit block already, by using the Object#method method, which returns a Proc that is equivalent to calling the method:

  users.map &method(:do_something)
And if you want to call a Proc object in a local variable called do_something to map (the closest thing to a "non-method function" that exists in Ruby), you can do that without an explicit block, even more concisely, because you don't have to convert it to a Proc first:

  users.map &do_something
Sigh. Yes, I am aware of the technicalities you describe. I thought my point was clear without my needing to be pedantically accurate.

In many languages (even in JS!) you can just map a function point-free, rather than having to introduce a variable just because syntax demands it, or worry about which syntax to use depending on if the "function" you're calling is a method on the object being mapped or something else. It's all noise.

> you can do that without an expllcit block already, by using the Object#method method, which returns a Proc that is equivalent to calling the method:

> users.map &method(:do_something)

Again, I am aware, and the ugliness of that solution proves the point I was trying to make -- it is even worse than the block syntax with a variable.

> In many languages (even in JS!) you can just map a function point-free,

Yes, in many languages, "functions" are a thing that exists. In Ruby, there is no such thing as a function. The concept does not exist.

Which is why it is bizarre to talk about what you can and can't do with "functions" in Ruby, and to call kinds of different not-function things (including symbols, of all things) "functions" in that discussion. Its just applying language that makes no sense.

>using "good variable names" in cases where that variable is effectively functioning as nothing more than a syntactically necessary placeholder for a point-free phrase is a clear (if well-intentioned) mistake.

If variable names are well picked they provide guiding context hints that ease understanding. If you have only a terse pack of symbols, deciphering the code will be longer because you have to go trough more contextualization to understand where the code take place and what it actually operates over for which purpose.

I find this too cluttered: `billing_subscriptions.each { |billing_subscription| billing_subscription.status = ACTIVE }`

This reminds me a lot of using(&:blabla). I'd prefer it: `billing_subscriptions.each { |bs| bs.status = ACTIVE}`

But don't mind me I'm just bikeshedding

(comment deleted)
_1 takes two characters per use, single-character explicitly named variables take one character per use plus 3 per variable (4 for the first), counting spaces, so you save two characters (three for the first) if the variable is used once, minus one per additional use of a variable.

With only one variable, and sometimes with very simple expressions with more than one, you probably get a readability benefit by avoiding stuttering:

  { _1 + _2 }
reads a little better than:

  { |x, y| x + y }
But with any even slightly complex expression in the block, that goes from a benefit to a cost.
Raku [1] deserves a honorable mention where it has named anonymous parameters, written as `$^x` and ordered lexicographically.

In my taste as a PL enthusiast, the biggest problem with this kind of anonymous parameters is a conflict with nested blocks. Suppose that our fictitious language is expression-oriented, `{ |x| ... }` denotes an explicit closure, `{ ... }` denotes an implicit closure and `$1`..`$n` are anonymous parameters in them. Consider the following examples:

    { $1 + $2 }             // Yeah, that would be `{ |x, y| x + y }`
    {}                      // Is this a zero argument function?
    { $100 }                // Does this have 100 arguments???
    { |x| $1 + $2 }         // Maybe this should be invalid...
    { { |x| $1 + $2 } }     // But maybe this should be okay??
    { { |x| $1 + x }($2) }  // And same to this???
    { { |x| $1 }($2) }      // Uh...
    { { $1 }($2) }          // Should be same to above, but...
    { { $2 }($1) }          // Not this (and will fail)!
The problem is much worse when you don't even have an explicit boundary like `{ ... }`. A limited form of automatic function, say `_ + _` without no parentheses, might work, but anything beyond that quickly devolves into a mess.

[1] https://docs.raku.org/language/variables#Variables

Elixir puts some guardrails on its numbered-argument anonymous function syntax, which I think do a good job of restricting its use to defining extremely simple functions.

1. The function has to use all of its arguments at least once. You can’t do `&(&2)`, because it skips the first argument.

2. Single-expression. You can’t do something like `&(x = &1; x + 1)`.

3. No nesting. You can’t define an additional anonymous function inside of a & expression, or even use a `&SomeModule.some_fn/arity` capture. For anonymous functions with nesting, you have to use the `fn arg1, arg2, ... -> body end` syntax.

> One is tempted to ask here: what would be conditions/language changes so we could’ve just written this?

> serialize :salary, format: -> csv_currency(_1)

> I didn’t analyze this deeply, but “intuitively” this seems enough syntax for the interpreter to understand “the next code is one-statement lambda’s body.”

I believe that Swift has an @autoclosure attribute which allows this?

And Clojure has their way of handling unnamed variables in anonymous functions.

;; Single argument

(fn [x] (* 2 x))

#(* 2 %)

;; Multiple arguments

(fn [x y] (+ x y))

#(+ %1 %2)

;; Remaining arguments

(fn [x & args] (reduce + x args))

#(reduce + % %&)

Example copied from this nice page - https://silviapan.com/blog/anonymous-functions-clojure/

In K, you can write a lambda with an explicit argument list:

    {[a;b]a+b}
But if you leave it off, the names "x", "y", and "z" implicitly refer to the first three arguments, and their presence controls the valence (arity) of the lambda.

    {x+y}    / a lambda with two arguments
    {y}      / also a lambda with two arguments, discarding the first
Using the implicit names for short definitions is highly idiomatic. By removing an unnecessary degree of freedom, it increases the likelihood that two K programmers will arrive at identical formulations of a short piece of code.
It's not just useless, but actively harmful.

Especially when reading code, there's yet another special syntax form to learn, and one which has no real utility to structure the code better or to better communicate it's intent and/or operation.

Kind of dubious even of the “useful” example toward the end, which can be rewritten without _1 nearly as succinctly as:

  ENV.fetch('CONFIG_PATH')
    .then &File.method(:read)
    .then &YAML.method(:parse)
    .dig('metadata', 'created_at')
    .then &Time.method(:parse)
Really, if we want more compct syntax sugar for this, letting .:<identifier> be shorthand for .method(:<identifier>) would probably be better than _1 for this circumstance and more broadly useful for making functional-style programming more concise in Ruby.
Well, first of all, I was one of those who fought for method references as first-class objects for a few years (the story is told in my blog before[1]), and "let Ruby eventually become more functional" was one of my main arguments!

That being said, there are a few problems on this road, that make it less probable to be followed. First, as Ruby doesn't have first-class Method objects (e.g. object corresponding to `File.method(:read)` doesn't exist until you call `method(:name)` explicitly), this style being popularized will either bring a lot of subtle performance problems, or will require redesign of Ruby's internals.

Second, if we'll make an example _a bit_ more complicated, say:

  ENV.fetch('CONFIG_PATH')
    .then { File.read(_1, encoding: 'KOI8-U')
    .then { JSON.parse(_1, symbolize_names: true) }
    .dig('metadata', 'created_at')
    .then { Time.new(_1, in: CURRENT_TIMEZONE) }
...it would be impossible to rewrite with `method`, until currying would be signficantly improved (currently it can only be applied to first arguments, not tail ones, and even then looks horrible). So it is not impossible, and it is a turn of the language I'd like to see in the future, but it is not as straightforward as I once imagined.

1: https://zverok.space/blog/2022-01-13-it-evolves.html#taking-...

If the author is around here, could they explain their use of <blockquote>s? They appear to be used to introduce comments inside the text, but that’s really a confusing markup.
Ugh, never thought about that this way!

I believe it is a habit back from my journalistic days, when there were "insets" besides the main text with "things that deserve mentioning here, but kinda out of the main flow... but more related to it than footnotes." I miss the richness of markup of paper magazines!

Does it harm the reading experience significantly?

<aside> would be appropriate for that use case in HTML.
Seems like allowing arbitrary names for the variables would have been the sweet spot between concision and explicitness. So:

    things.each { puts _thing }
    pairs.map { _x * _y }
It's kinda insane, but in the beautiful way that Ruby is.
Tangential but:

> My homegrown theory is that the idea of array.each { ... } as the main syntax for cycles was the first “aha!” of the new language development for its author

> The style that flourishes with this new shortcut is representing computation as a chain of small blocks, declaratively describing transformations from inputs to outputs. The style that is native to Ruby, the first mainstream language to have enumerable.transformation { ... } as the main cycle construct, and, I believe, played a role in normalizing “map”, “reduce”, and “filter” as a way of thinking about how things are handled.

Matz got that from Smalltalk. That's why most of the enumeration methods in Ruby match the names of similar methods in Smalltalk ("collect", "reject", "select", etc.) and not the corresponding names in functional languages ("map", "reduce", "filter"). The "each" method is the odd one out because that's called "do" in Smalltalk and "do" is a reserved word in Ruby.

Smalltalk definitely isn't mainstream today, but it did have its day in the sun where it was one of the better-known languages.

Yeah, should've mentioned that probably (though a post is already a mess of footnotes, aside notes, and parenthicals).

I do believe though that a lot of modern languages got that from Ruby (or, via Ruby, from "JS that Rubyists wrote" and such), when it was a "hot new thing" for a brief moment in 2000s. Or maybe it is my skewed perspective.

>That's why most of the enumeration methods in Ruby match the names of similar methods in Smalltalk ("collect", "reject", "select", etc.) and not the corresponding names in functional languages ("map", "reduce", "filter").

Ruby has both, actually; "map" is a synonym for "collect", "reduce" is a synonym for "reject", and "filter" is a synonym for "select" (well, actually, they are both synonyms for "find_all"). And the non-Smalltalk names seem to have had more influence on newer methods, e.g., Enumerable has filter_map, but not select_collect, even as a synonym.

Ruby didn't always had both. `reduce` as an alias for `inject` was introduced in Ruby 1.8.7; `filter` as late as in Ruby 2.6 (just a few years ago, and most of the codebases I saw are still preferring `select`).

Yes, the dictionary is normalized in the last years towards what became industry/mainstream standard names for the concepts, but the statement that initial names were derived from Smalltalk is correct.

I can't imagine writing something like this while also serving in the Ukrainian Army as an active soldier. The 'Irks and Quirks' of Ruby would seem much less important to me in that position!
I’m surprised Swift’s `$1`, `$2`, etc. weren’t mentioned in this article.
I don’t know Scala, and found interesting the way each occurrence of the anonymous determiner refers to the next item in the sequence. I’m not fan of using underscore for this however. I think that a new reserved word worth the clarity it brings. For example "thither"[1], "thilk"[2] or even "said" (as in aforesaid) or "such" are both rare enough to lower possible collision in existing code bases, and explicit (you only have to discover it once at worst) to make a good trade of, to my mind.

[1] https://en.wiktionary.org/wiki/thither [2] https://en.wiktionary.org/wiki/thilk [3] https://en.wiktionary.org/wiki/said#Determiner