Search code examples
switch-statementtype-inferencecrystal-lang

How to restrict type of a function argument


Let's say I have a following function:

def encode(obj)
  case obj
  when Int32
    "i#{obj}e"
  when String
    "#{obj.size}:#{obj}"
  when Symbol
    encode(obj.to_s)
  when Array
    obj.reduce "a" {|acc, i| acc + encode(i)} + "e"
  else
    raise ArgumentError.new "Cannot encode argument of class '#{obj.class}'"
  end
end

And I want to get rid of that else branch to make a compile-time check for the type of an argument. I can write something like this:

def encode(obj : Int32 | String | Symbol | Array)

In this case it's ok. But what if have a bigger list of types? Is there a more elegant way to do this? I want compiler to check that this function accepts only those types that are matched in the case expression.


Solution

  • Overloading to the rescue:

    def encode(obj : Int32)
      "i#{obj}e"
    end
    
    def encode(obj : String)
      "#{obj.size}:#{obj}"
    end
    
    def encode(obj : Symbol)
      encode(obj.to_s)
    end
    
    def encode(obj : Array(String))
      obj.reduce "a" { |acc, i| acc + encode(i) } + "e"
    end