Question re: code found in "Programming Crystal" Book

On page 135:

module Hardness
   def data
      mohs = {...}
   end

   def hardness
      data[self.name]
   end
end

What is this syntax?

data[self.name]

I’ve read and re-read this section and can’t figure it out for the life of me…I mean, I know data[...] is an array index…and self.name would likely be making reference to a name variable. But the only place I see name is the method defined above. Unless I’m missing something.

In this context it’s a hash lookup.

Variable names do not include a self. prefix.

self.name will call the method named name. It is equivalent to just using name, much like how data in this context is returning the the hash defined in the method above. However, using self is a way to explicitly denote you want to call a method and not reference a local var. If there was also a data local var, data in the context of data[self.name] would reference that variable and NOT the response of the method. It is still possible to reference the method tho:

data       # => References local var named `data`
data()     # => Calls the `data` method
self.data # => Also calls the `data` method

Ultimately what this Hardness module is showing, is that the module can be included into any type that exposes a def name : String method that would power the hardness method. IMO a more proper way to handle this would be:

module Hardness
  abstract def name : String

  def data
    {"talc" => 1, "gold" => 2.5, "calcite" => 3, "apatite" => 5, "corundum" => 9}
  end

  def hardness
    data[self.name]
  end
end

I.e. by declaring an abstract def name : String you are more clearly defining the requirement that the including type has that method. It also allows readers of the documentation to see that it needs to be defined.

I personally prefer always using self. when I want to call a method, just to make things more clear. But do whatever makes the most sense to you.

3 Likes