# Why following flatten_type method work?

I saw some code in this post, but i don’t know why this code work as expected.(flatten the type)

``````def flatten_type(object)
if object.is_a?(Array)
flatten_type(object)
else
object
end
end

puts typeof(flatten_type(1))                          #=> Int32
puts typeof(flatten_type([1, ]))                   #=> Int32
puts typeof(flatten_type([1, [2, ['a', 'b']]]))       #=> Int32 | Char
``````

Any idea? Thanks.

What part of it is suprising do you? What do you not understand?

Let us use following code as a example:

``````def flatten_type(object)
if object.is_a?(Array)
flatten_type(object)
else
object
end
end

x = [1, [2, ['a', 'b']]]
puts x.is_a? Array                           # => false
puts typeof(x)                               # => (Array(Array(Char) | Int32) | Int32)
puts typeof(flatten_type([1, [2, ['a', 'b']]])) # => Int32 | Char

``````

I can’t understand how flatten_type can flatten the nested type.

x = [1, [2, [‘a’, ‘b’]]]; the object is x, it is 1, right? the type of 1 is `(Array(Array(Char) | Int32) | Int32)`, how it change to Int32 | Char ?

Thanks

Let us know if it’s still not clear.

Yes, still not clear, I think I lack imagination in some way.

let me try guess this code step by step.(crystal i not work for me for now because missing LLVM 15 support)

• x is [1, [2, [‘a’, ‘b’]]]
• Try run flatten_type(x)
• x.is_a? Array => Yes
• try run flatten_type(x), because x is `1`, so, should be flatten_type(1).
• 1.is_a? Array => No
• So, flatten_type finally return `object`, it is `1`,
• the compile-time type of 1 should be `(Array(Array(Char) | Int32) | Int32)`

Why the result is Int32 | Char ?

This is the part that’s actually slightly different.

The compiler doesn’t know what’s in the first position of the array. You know `x` is an int, but for the compiler that could be an int or the other array.

It’s easier if you also print the types as the compiler figures things out:

``````def flatten_type(object : T) forall T
{% puts "typeof(object) is #{T}" %}
if object.is_a?(Array)
show_typeof_object0(object)
flatten_type(object)
else
show_typeof_object(object)
object
end
end

def show_typeof_object0(x : T) forall T
{% puts "typeof(object) is #{T}" %}
end

def show_typeof_object(x : T) forall T
{% puts "typeof(object) is #{T}" %}
end

flatten_type([1, [2, ['a', 'b']]])
``````

The output is:

``````typeof(object) is Array(Array(Array(Char) | Int32) | Int32)
typeof(object) is (Array(Array(Char) | Int32) | Int32)
typeof(object) is (Array(Array(Char) | Int32) | Int32)
typeof(object) is (Array(Char) | Int32)
typeof(object) is (Array(Char) | Int32)
typeof(object) is Char
typeof(object) is Char
typeof(object) is Char
typeof(object) is Int32
``````

Another way to understand this:

``````a = [1, 'a']
puts typeof(a) # => Char | Int32
``````

That is, `typeof(a)` is not Int32, it’s the union. The compiler can’t know (in general) what’s in each position of the array.

1 Like