Recent submissions

Comment · 2 years ago
(Author here)

I don't think this corresponds to my experience.

I am working as a staff engineer in big production codebases (with all the traits of those: some code is good, some is bad, some is unrecognizable legacy, sometimes we are in a hurry and write awful code, sometimes we have time for refactoring etc.)

And my observations about the logic and perception of the features are drawn from the practice of code reviews, mentoring new people, and discussing ways of solving tasks in this environment. Obviously, I am frequently a driver of new code practices, but I am also trying to be a good person and a good colleague and notice how comfortable people are with various parts of the codebase, various idioms, etc.

One of the main topics of this article series is uncovering the language's logic and intuitions (to stop perceiving it as a "bag of random syntactic features you need to learn or should guess") and using those intuitions for code that is, yes, better for the reader, but the code that can be created in a quickly-changing production environment and rewritten fearlessly.

Comment · 2 years ago
Oh, thanks! Added a footnote to the article.
Comment · 3 years ago
Thanks, that's the highest praise I could possibly hope for :)

It is one of the things I always enjoyed and tried to achieve myself (when the discussion of design decisions in one language would provide food for thought in the context of other languages/domains).

Comment · 3 years ago
Comment · 3 years ago
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.

Comment · 3 years ago
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.

Comment · 3 years ago
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?

Comment · 3 years ago
Comment · 3 years ago
> 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".
Comment · 3 years ago
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!)

Comment · 3 years ago
Comment · 3 years ago
Thanks, that's insightful!
Comment · 3 years ago
Yes, that's what I referred to. Before 2.7, object&.:method was almost merged (or rather merged and reverted) because Matz had second thoughts about its uglyness... Which is not completely untrue, but not having a concise way for referring to a method is irritating.

That still wouldn't have solved passing additional args, so maybe { object.method_name(_1, args)} is the next best thing. Though it perceives non-atomic due to wrapping block.

Comment · 3 years ago
Ruby-idiomatic pipe operator is Object#then. After a lot of design proposals and discussions it is more or less evident no solution other than method would integrate naturally with the rest of the code. So it is just `arg.then{ third(_1)}.then{ second(_1)}.then{ first(_1)}`

Would've been a bit more concise with method references, almost introduced in 2.7, but alas.

(But, well, people tend to want "something that looks like operator, preferably something that looks exactly like |>" and reject every other possibility)

Live from the official Hacker News API

A modern reader for Hacker News · SvelteKit + Skeleton