What would I change about Ruby?

Last edited May 2021
Backlinks: What would I change about programming language X?

Ruby is close to my favorite programming language. That does not mean I’m entirely happy with it. As an exercise, I’m writing up what I’d change about the language.

This is purely hypothetical — the thought of forking Ruby and making change is preposterous. But it is an interesting exercise!

Ruby was created in the mid-90’s. It’s now 25 years later, and there have been plenty of advances in programming languages, and the best practices around Ruby have emerged and mostly solidified. This creates a good opportunity to look back and reflect.

Remove symbols

Symbols and (frozen) strings are too similar to warrant a distinction between the two. (Clojure has symbols and (immutable) strings, and keywords on top of that. Keywords are similar to immutable strings, but can be namedspaced (e.g. ::foo is similar to :my.namespace/foo). )

To do: Ooh, potentially plenty of implications of this.

Use one in-memory string encoding

Always encode strings as UTF-8. Provide a “data array” class to contain arbitrary data, and allow converting between strings and data, e.g.

"stuff".to_data(encoding: "UTF-8")
some_data.decode_string(encoding: "UTF-8")

Use distinct character literal

For strings, Ruby can use single quotes (e.g. 'example'), double quotes (e.g. "example"), and percent-literal syntax (e.g. %[example]). The ?-syntax can be used to construct single-character strings (e.g. ?x which is identical to "x").

I believe character literals are useful, and other languages gravitate towards using single quotes for those.

first_name = "Denis"

# This is a character literal, not a string
first_name[0] # => 'D'

# This is a string, built from two character literals
'D' + 'e' # => "De"

Change keyword for defining functions and methods

A small one, but def always strikes me as odd: you can’t use it for defining anything but functions and methods. Perhaps fn or fun instead?

class Person
  fun initialize(name)
    @name = name

Rename initializer method

Ruby’s constructor is called initialize, e.g.

class Person
  def initialize(name)
    @name = name

I find it difficult to type initialize, and often make typos when typing it, which leads to hard-to-debug problems because the initializer does not exist.

Perhaps renaming it to init could work. Or construct (like in JavaScript).

Or, taking a page from Python’s book, using a clearly reserved name such as __init__ (__init__ is an identifier, not a keyword. What would that mean for other identifiers that are not keywords? Would we have __self__ instead of self). This would allow the interpreter to detect typos: an unknown reserved name could be an error.

class Person
  def __init__(name)
    @name = name

Ruby already uses the double-underscore approach in some cases: __FILE__, __dir__, __method__ and __callee__. Interestingly, these are implemented as methods on Kernel — except __FILE__, which is a constant, and that explains the rather unfortunate inconsistency in capitalisation.

Simplify assignment in initializer

Ruby’s initializer is verbose and repetitive when passing in many values:

class Person
  def initialize(first_name)
    @first_name = first_name

I rather like Crystal’s approach, providing a shorthand:

class Person
  def initialize(@first_name)

To do: It looks weird that the initialize method is now empty.

Treat modules are files

Ruby is not prescriptive in how to organise code. Over time, though, conventions have appeared: Zeitwerk, for example, defines a file structure that is mostly sensible.

I’d like to have that more tightly integrated into the language. To do: OK, but… how would that work exactly?

To do: Then we can remove most of the indentation caused by nesting, which makes the code less rightward.

Make modules not globally available

To do: Ugh this is a big one

Simplify procs, blocks and lambdas

To do

To do: Blocks are useful (distinct from functions) because they support non-local return.