Reputation: 597
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
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