Search code examples
crystal-lang

Reopening class and adding instance variables


I am teaching myself Crystal-lang and I came across a section in the documentation that I don't quite understand.

Here is the documetation page.

On that page it gives the following code:

class Person
  @age = 0

  def initialize(@name : String)
  end
end

This is followed by the following statement:

This will initialize @age to zero in every constructor. This is useful to avoid duplication, but also to avoid the Nil type when reopening a class and adding instance variables to it.

Can someone please explain, or show me an example of the bolded behaviour? I'm not certain I understand what it means by "reopening a class and adding an instance variable to it".


Solution

  • Here is an example of reopening a class, and adding an instance variable to it:

    class Person
      @age = 0
    
      def initialize(@name : String)
      end
    end
    
    # Usually in another file
    
    class Person
      def gender=(gender : String)
        @gender = gender
      end
    
      def gender
        @gender
      end
    end
    
    person = Person.new("RX14")
    
    typeof(person.gender) # => String | Nil
    person.gender # => nil
    
    person.gender = "???"
    
    person.gender # => "???"
    

    We add the @gender instance variable, which is not initialized in the def initialize. The compiler infers the type of @gender to be String | Nil, since it is assigned to a string in gender=, but it is not initialized in the constructor, meaning it can also be nil.

    However, we can add a default value to the @gender instance variable, which applies to all constructors, define before or after the default:

    class Person
      @age = 0
    
      def initialize(@name : String)
      end
    end
    
    # Usually in another file
    
    class Person
      @gender = "unknown"
    
      def gender=(gender : String)
        @gender = gender
      end
    
      def gender
        @gender
      end
    end
    
    person = Person.new("RX14")
    
    typeof(person.gender) # => String
    person.gender # => "unknown"
    
    person.gender = "???"
    
    person.gender # => "???"
    

    This avoids the @gender variable getting the String | Nil type, since it is initialized to "unknown" when Person is constructed. Since Nil types are often avoided, this is an important tool to have.