I get this error while trying update Hash using old value with merge!
My code:
# Error in line 2: instantiating '(Int32 | String)#+(Int32)'
# in line 2: ambiguous call matches both Int8 and Int16
data = {"name" => "user", "age" => 12}
puts data.merge!({"age" => data["age"] + 2})
But this works great:
data = {"name" => "user", "age" => 12}
puts data.merge!({"age" => 13})
puts data.merge!({"age" => 12 + 2})
Playground example:
https://play.crystal-lang.org/#/r/4t4o
data["age"] can either by a String or an Int32, so that + won't work.
Of course you as the programmer who wrote the code knows that data["age"] is an Int32, but the compiler has no idea, it just sees that you are accessing some element in the Hash.
You can do data["age"].as(Int32) to solve this.
Or probably not use a Hash at all and use classes if you can.
Thanks for explanation
But that error message ambiguous call matches both Int8 and Int16 looks strange.
Hm, right. You can reproduce that with:
a = 1 || "foo"
a + 2
but I'd open that as a separate issue
Works fine with NamedTuple
data = {name: "user", age: 12}
puts data.merge({age: data[:age] + 4})
@stepanvanzuriak because the types of each key in NamedTuple are known at compile time.
The hash is a mutable object , for example:
data = { :name => "user", :age => 12} #=> Hash(Symbol, Int32 | String)
data[:name] = 12
data[:age] = "old"
data # => {:name => 12, :age => "old"}
but the NamedTuple is a inmutable struct, but the type of each key is known by the compiler so it knows
data[:age] is a Int32, so it can respond to :+
data = {name: "user", age: 12} #=> NamedTuple(name: String, age: Int32)
From the docs
The compiler knows what types are in each key, so when indexing a named tuple with a symbol literal the compiler will return the value for that key and with the expected type
Most helpful comment
But that error message
ambiguous call matches both Int8 and Int16looks strange.