The Crystal Programming Language Forum

Recursive typed literal hash won't compile

How can I declare a literal hash like this and have it compile? This won’t work:

alias Allowed = Hash(Symbol, Allowed|Bool)
h = {
  :symbol => { :symbol => true } of Symbol => Allowed|Bool
} of Symbol => Allowed|Bool

The compiler says:

crystal build foo.cr
Showing last frame. Use --error-trace for full trace.

In /usr/share/crystal/src/hash.cr:1920:46

 1920 | def initialize(@hash : UInt32, @key : K, @value : V)
                                                 ^-----
Error: instance variable '@value' of Hash::Entry(Symbol, Bool | Hash(Symbol, Allowed | Bool)) must be (Bool | Hash(Symbol, Allowed | Bool)), not Hash(Symbol, Bool | Hash(Symbol, Allowed | Bool))

I can use a NamedTuple literal instead, I’m wondering if the above is just a compiler issue or if I’m doing something wrong.

Why the of Symbol => Allowed|Bool on the main hash? It works with the following below:

alias Allowed = Hash(Symbol, Allowed|Bool)
h = {
  :symbol => { :symbol => true } of Symbol => Allowed|Bool
  } 
 
pp h #> {:symbol => {:symbol => true}}
pp typeof(h) #> Hash(Symbol, Hash(Symbol, Bool | Hash(Symbol, Allowed | Bool)))

The reason your example isnt working is because youre returning a Hash(Symbol, Hash(Symbol, Bool)) not what youre defining in the Alias.

You could do this too:

alias Allowed = Hash(Symbol, Bool)
h = {
  :symbol => { :symbol => true }  of Symbol => Bool
  } of Symbol => Allowed
 
pp typeof(h) #> Hash(Symbol, Hash(Symbol, Bool))