Skip to content

Refactor hash map internals

Yorick Peterse requested to merge refactor-hash-map-internals into master

In order to simplify the syntax, hash map literals have been removed. Instead, one now creates a hash map using Map.new. To make it easier to create a map with a bunch of pairs in one expression, we introduce the Map.set method. This method behaves similar to Map.[]=, but returns the Map itself instead of the value written:

Map
  .new
  .set('foo', 'bar')
  .set('baz', 'quix')

So why were map literals removed? Well, first of all their syntax was not intuitive: %[key: value]. This syntax was taken from Elixir, but is not used in other languages that we are aware of. Many languages use curly braces (e.g. {key => value}), but these are already used for closures. Some languages reuse square brackets (e.g. [key: value]), but this makes the meaning of [] unclear. We considered using a syntax similar to Scala:

Map.new(key -> value)

Here -> would be a method that returns some sort of tuple, and Map.new would take this list of tuples and use them to fill the map. The method -> would have to be available for every object, since it's perfectly valid to use outside of constructing maps. This means -> would have to be defined on Object, or in a trait that is implemented for it. Manually implementing the method/trait would be too cumbersome. The hypothetical code for this might look as follows:

impl Object {
  def ->!(V)(other: V) -> Tuple!(Self, V) {
    Tuple.new(self, other)
  }
}

Unfortunately, the Ruby compiler does not support the use of self types in generic types well enough to make this work. This is a long standing issue 1, but it would require an extensive rewrite of the type system to support. Since we want to rewrite the Ruby compiler in Inko, adding support for this in the Ruby compiler would be a waste of time.

There are a variety of other approaches, such as passing a closure to Map.new that can be used to fill up the map. All of these suffer from similar problems: the Ruby compiler's type system is a bit buggy.

To work around all of this, we added the Map.set method. While the resulting code is a bit more verbose, it does not require any compiler changes. The API should also feel familiar to those used to immutable programming languages, which typically use a similar approach for constructing hash maps.

The removal of map literals also allows us to remove various compiler optimisations of these literals, simplifying the compiler and making the language more predictable.

Edited by Yorick Peterse

Merge request reports