Matias
Matias

Reputation: 597

Type alias and Hash as method parameter

I'm trying to create an initializer for a Class that receives a Hash as parameter. The Hash is a {String => Type} hash, and can be nested. I'm getting an error when running this code:

#file: types.cr
class Types
  alias Type = Nil |
               Bool |
               Int32 |
               Int64 |
               Float64 |
               String |
               Array(Type) |
               Hash(String, Type)

  def initialize(@input : Type)
  end
end

input = {"a" => {"b" => {"c" => {"c1" => 1, "c2" => 2, "c3" => true}}}}
s = Types.new(input)

Here is the error I get when running the code above:

$ crystal types.cr

Error in types.cr:16: instantiating 'Types:Class#new(Hash(String, Hash(String, Hash(String, Hash(String, Bool | Int32)))))'

s = Types.new(input)
          ^~~

in types.cr:11: instance variable '@input' of Types must be Types::Type, not Hash(String, Hash(String, Hash(String, Hash(String, Bool | Int32))))

  def initialize(@input : Type)
                 ^~~~~~

Is this possible with Crystal? How should I approach this?

Thanks!

Upvotes: 3

Views: 468

Answers (1)

Vitalii Elenhaupt
Vitalii Elenhaupt

Reputation: 7326

You can do this specifying type of each hash:

c = {"c1" => 1, "c2" => 2, "c3" => true} of String => Types::Type
b = {"c" => c} of String => Types::Type
a = {"b" => b} of String => Types::Type

t = Types.new({"a" => a} of String => Types::Type)
pp t # => #<Types:0x103085ec0
     #    @input=
     #      {"a" => {"b" => {"c" => {"c1" => 1, "c2" => 2, "c3" => true}}}}>

Another approach is to define and use Hash-like type:

alias Type = Nil         |
             Bool        |
             Int32       |
             Int64       |
             Float64     |
             String      |
             Array(Type) |
             Hash(String, Type)

alias TypesHash = Hash(String, Type)

t = TypesHash{
  "a" => TypesHash{
    "b" => TypesHash{
      "c" => TypesHash{
        "c1" => 1, "c2" => 2, "c3" => true,
      },
    },
  },
}

t                                            # {"a" => {"b" => {"c" => {"c1" => 1, "c2" => 2, "c3" => true}}}}
t["a"]                                       # {"b" => {"c" => {"c1" => 1, "c2" => 2, "c3" => true}}}
t["a"].as(TypesHash)["b"]                    # {"c" => {"c1" => 1, "c2" => 2, "c3" => true}}
t["a"].as(TypesHash)["b"].as(TypesHash)["c"] # {"c1" => 1, "c2" => 2, "c3" => true}

So you can pass it to the constructor just like TypesHash object:

class Types
  def initialize(@input : TypesHash); end
end

Types.new t

Upvotes: 4

Related Questions