Victor Shepelev, a Ruby developer from Kharkiv, writes about Ruby language design, refactoring, and life during the war in Ukraine.
HN links it for detailed Ruby posts, such as method references and the Data class, and for his wartime updates from Kharkiv.
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-offense-for-method-references
I agree on the first part. This is valid for all programming languages though.
I disagree that you get the same safety as C# anywhere though. But even more importantly - I don't think people should write C#-like code in ruby. It does not really work that well. It is better to write ruby like ruby; and even in ruby there are many different styles. See zverok using functional programming a lot. I stick to oldschool boring OOP; my code is very boring. But usually well-documented. I want to have to think as little as possible because my brain is very inefficient and lazy; zverok has a good brain so he can write more complex code. It is very alien code to me though, but he also documents his code a lot. You can find his code or some of his code here, it is a quite interesting ruby style, but also alien to me: https://zverok.space/projects/
(Ruby also adopted some habits from perl. I also don't think writing perl-like ruby makes a lot of sense. See also the old global variables inspired by perl; I can not recall most of them off-hand, so I avoid using them. My brain really needs structure - it is such a poor thinking machine really. And slow. My fingers are much faster than my brain really.)
I don't have an Elixir background (though I envy Elixir in many ways), but I absolutely utilize pattern matching in my Ruby code bases:
- https://github.com/keygen-sh/typed_params/blob/4e4982b7d2b263862c9a77296780c45c91c7e90f/lib/typed_params/formatters/jsonapi.rb#L72-L90
- https://github.com/keygen-sh/typed_params/blob/4e4982b7d2b263862c9a77296780c45c91c7e90f/lib/typed_params/controller.rb#L116-L123
- https://github.com/keygen-sh/keygen-api/blob/36cd61db143cc1c770cf0e4b8e9fb1be63993a3b/app/migrations/adjust_validity_for_validation_codes_migration.rb
- https://github.com/keygen-sh/keygen-api/blob/36cd61db143cc1c770cf0e4b8e9fb1be63993a3b/app/policies/release_policy.rb#L10-L26
- https://github.com/keygen-sh/typed_params/blob/4e4982b7d2b263862c9a77296780c45c91c7e90f/spec/typed_params/parameterizer_spec.rb#L6-L32
I love it. I want even more pattern matching too, like defp: https://bugs.ruby-lang.org/issues/19764 (with original credit to https://zverok.space/blog/2023-05-05-ruby-types.html).
The use case for this would be that it doesn't reify the block IOW doesn't allocate a proc to wrap the block.
... but that optimisation has already been made† (and IIRC it is too for *args and **kwargs that just get forwarded to calls), so it mostly exists for consistency with anonymous * and ** and valueless keys, as well as documentation (making it explicit that the method accepts a block right from the signature). Here's the discussion: https://bugs.ruby-lang.org/issues/11256
I don't particularly like it given the style I like to use, but I can see how people using a certain different style would as their style would be more consistent and carry better intent.
Anyway the post linked from the OP has a more detailed rundown: https://zverok.space/blog/2023-11-24-syntax-sugar4-argument-forwarding.html
† https://bugs.ruby-lang.org/issues/14045
They’re both shortcuts to creating class-like things.
Take @dataclass in python for example, that’s similar to Struct for Ruby.
‘Data’ documentation from the GitHub PR (https://zverok.space/ruby-rdoc/Data.html):
Data provides no member writers, or enumerators: it is meant to be a storage for immutable atomic values. But note that if some of data members is of a mutable class, Data does no additional immutability enforcement
See also Struct, which is a similar concept, but has more container-alike API, allowing to change contents of the object and enumerate it.
> This is an inspiring series of writing.
Check out his other writing, too, like the "Rebuilding the Spellchecker" series: https://zverok.github.io/spellchecker.html. And "Wikipedia as the Datasource": https://zverok.github.io/blog/2021-11-30-irregular1.html