Crystal: Broken meta-model above Reference (Why "Object == Class" answers true) (Original: [Metamodel] Superclass of Reference is mismatched)

Created on 12 Jan 2017  路  9Comments  路  Source: crystal-lang/crystal

When I'm working on #3871 for creating the macro of getting the superclasses/ancestors of a Type, I found that if I run the code below, that will show the superclass of Reference is Class:

class Reference
  def self.superclass
    {{@type.superclass}}
  end
end

puts Reference.superclass #=> Class

But this is mismatched with the API document which tells that Reference is inherited from Object

And more, if I change the spec of superclass macro into this, it gives me Reference's supeerclass is Object!?

it "executes superclass" do
  assert_macro("x", "{{x.superclass}}", %(Reference)) do |program|
    # original: [TypeNode.new(program.string)] of ASTNode
    [TypeNode.new(program.reference)] of ASTNode
  end
end
Failures:

  1) macro methods type methods executes superclass
     Failure/Error: result.should eq(expected)

       expected: "Reference"
            got: "Object" # <<<<<<<<< got Object now, why?

     # spec/spec_helper.cr:269

Version: 0.20.3, 0.20.4, master

bug implemented topictype-system

Most helpful comment

Fixed! Now in master:

abstract class Object
  def self.superclass
    {{@type.superclass}}
  end
end

Reference == Object            # => false
Reference == Class             # => false
Reference.superclass == Object # => true
Reference.superclass == Class  # => false

Class == Object        # => false
Object == Class        # => false
Object.crystal_type_id # => 418
Class.crystal_type_id  # => 1027

All 9 comments

It's a bug in the metamodel, right now if you do:

p Object # => Class

There's a bug with Object and its superclass. We never fixed that.

So, Reference's superclass is Object, but it shows as Class.

@asterite Thanks!

btw, why Class == Object is true (use the code below), is that means that Object is completely the same as Class in Crystal world?

abstract class Object
    def self.superclass
    {{@type.superclass}}
    end
end

Reference == Object #=> false
Reference == Class  #=> false
Reference.superclass == Object #=> true
Reference.superclass == Class  #=> true

Class == Object #=> true
Object == Class #=> true
Object.crystal_type_id #=> 588
Class.crystal_type_id  #=> 588

It's because the meta-model above Reference (so Object, and Class) is broken... once something is broken you can't rely on things working well :-)

I think the problem is in these lines and this, where we the Object's metaclass to be Class. Normally the metalcass of a type Foo is a new type that's called Foo:Class which you can see when you something like:

class Foo
end

Foo.x # => undefined method 'x' for Foo:Class

Which is a different method than if you do:

class Foo
end

Foo.new.x # => undefined method 'x' for Foo

That's because to solve Foo.x we solve the type of Foo, which is the metaclass, so Foo:Class, and look up methods there (class methods will be there). To solve Foo.new.x we first solve Foo.new, so we solve Foo which is Foo:Class, and the new method will return an instance of Foo, which now has type Foo, and we search the methodx in that type.

Now, in program.cr we set Object's metaclass to Class. So if you have Object in your program, as an expression, we first solve its type, which is the metaclass, so Class... and that's kind of wrong. But @waj wanted to model it like that after Objective-C (picture here, you can see Foo's class is called "Foo's metaclass", which in our case would be Foo:Class, but for NSObject it's NSObject's metaclass whose class is itself, so it's like it's also Class, but that doesn't seem right to me, we need Object:Class and Class to be different things), I never really understood why and that probably should be changed. I guess we should have something like Object:Class and the class of that would be Class. In fact, I'll change it right away so we can move on with this :-)

Great explanation. Thanks @asterite 馃憤

I changed the title that may be easily searched on GitHub :heart:

To be a bit more clear about the object, the type of a thing like Foo is to go to the metaclass. In the case of Object it was Class, and in the case of Class it's also Class, hence their type was the same. I'll change it so that the type of Object is now Object:Class.

Fixed! Now in master:

abstract class Object
  def self.superclass
    {{@type.superclass}}
  end
end

Reference == Object            # => false
Reference == Class             # => false
Reference.superclass == Object # => true
Reference.superclass == Class  # => false

Class == Object        # => false
Object == Class        # => false
Object.crystal_type_id # => 418
Class.crystal_type_id  # => 1027

Is that moving towards having Object be a useable type?

No, that's a completely different issue :-)

Was this page helpful?
0 / 5 - 0 ratings

Related issues

MakeNowJust picture MakeNowJust  路  64Comments

malte-v picture malte-v  路  77Comments

akzhan picture akzhan  路  67Comments

rdp picture rdp  路  112Comments

RX14 picture RX14  路  62Comments