def flatten_type(object)
if object.is_a?(Array)
flatten_type(object[0])
else
object
end
end
x = [1, [2, ['a', 'b']]]
puts x[0].is_a? Array # => false
puts typeof(x[0]) # => (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[0] is x[0], it is 1, right? the type of 1 is (Array(Array(Char) | Int32) | Int32), how it change to 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[0] 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[0])
flatten_type(object[0])
else
show_typeof_object(object)
object
end
end
def show_typeof_object0(x : T) forall T
{% puts "typeof(object[0]) 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[0]) is (Array(Array(Char) | Int32) | Int32)
typeof(object) is (Array(Array(Char) | Int32) | Int32)
typeof(object[0]) is (Array(Char) | Int32)
typeof(object) is (Array(Char) | Int32)
typeof(object[0]) 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[0]) # => Char | Int32
That is, typeof(a[0]) is not Int32, it’s the union. The compiler can’t know (in general) what’s in each position of the array.
I have been trying to understand this code intermittently these days, but I still don’t understand how above code is running.
Following is a improved version for above code.
def flatten_type(object : T) forall T
{% puts "typeof(object111) is #{T}" %}
p! object.is_a?(Array)
if object.is_a?(Array)
show_typeof_object0(object[0])
flatten_type(object[0])
else
show_typeof_object(object)
object
end
end
def show_typeof_object0(x : T) forall T
{% puts "typeof(object[0]) is #{T}" %}
end
def show_typeof_object(x : T) forall T
{% puts "typeof(object222) is #{T}" %}
end
# ic(1.7.2):001> typeof([1, [2, ['a', 'b']]])
# => Array(Array(Array(Char) | Int32) | Int32)
flatten_type([1, [2, ['a', 'b']]])
The output is:
╰─ $ cr run 1.cr
typeof(object111) is Array(Array(Array(Char) | Int32) | Int32)
typeof(object[0]) is (Array(Array(Char) | Int32) | Int32)
typeof(object111) is (Array(Array(Char) | Int32) | Int32)
typeof(object[0]) is (Array(Char) | Int32)
typeof(object111) is (Array(Char) | Int32)
typeof(object[0]) is Char
typeof(object111) is Char
typeof(object222) is Char
typeof(object222) is Int32
object.is_a?(Array) # => true
object.is_a?(Array) # => false
I still confusing on why the code if object.is_a?(Array) run only twice? if the latter is return false, why above output in macro is not output as expected? i consider some magic happen on compile-time before the if condition is run?
But I have to say that some knowledge that is never mentioned in the official documents seems to be used here? It’s hard to figure out for a programmer who’s used to Ruby.