But typeof returns a regular type Animal, not Animal+.
Another example:
module Foo
end
module Bar
end
class Class1
include Foo
end
class Class2
include Foo
include Bar
end
array = [Class1.new, Class2.new] of Foo
typeof(xs) # => Array(Foo)
typeof returns a regular type Foo, not Foo+.
So I think virtual type is a type inference technique for compilers and should not be exposed at the user-level, just tell the user that “objects of a derived class may be treated as objects of a base class”.
I think everything will be simpler if we stop making the distinction between virtual and non-virtual types, even though it might end up in slightly less performant code. I’m also sure LLVM will notice what’s the effective type and optimize it. The main issue is that the semantic changes: if Bar overrides a method and returns a different type, it affects the type of the parent call seen as a virtual type. But I think that’s a reasonable thing to expect (it works like that in every other statically typed language)