Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make everything an Object #144

Closed
masak opened this issue Jun 11, 2016 · 17 comments
Closed

Make everything an Object #144

masak opened this issue Jun 11, 2016 · 17 comments

Comments

@masak
Copy link
Owner

masak commented Jun 11, 2016

Currently Ints are not Objects:

$ bin/007 -e='say(42 ~~ Object)'
0

This issue makes the case that they — and in fact everything — should be.

The usual case against making "primitive types" not inherit from Object is performance: they need to be closer to the metal so they can be fast. 007 has no such concerns. Languages like Java have done weird, unnerving dances with Integer wrapper types, just to have somewhere OO-y to store properties and methods. In a move nobody quite understands, JavaScript copies this design, and somehow makes it worse.

Ruby takes the stance that fixnums are objects, because hey, everything is:

$ irb
irb(main):001:0> 42.is_a?(Fixnum)
=> true
irb(main):002:0> 42.is_a?(Object)
=> true

I'd like to take this stance in 007 too, but I believe I have an even better argument than just "everything's an object": an object is something that you do property access on. Do our ordinary Int and Str and Array values have properties? Yes, they do! Hence, we'd expect them to be a kind of object.

Of course, this goes for Q nodes, too. They're just a type of object, and should typecheck as such. They don't, currently:

$ bin/007 -e='say(Q::Identifier { name: "foo" } ~~ Object)'
0
@vendethiel
Copy link
Collaborator

So Object.ACCEPTS should... Just return true? :P

@masak
Copy link
Owner Author

masak commented Jun 12, 2016

So Object.ACCEPTS should... Just return true? :P

That's the gist of it, yes. (007 doesn't have an .ACCEPTS, nor currently True (Edit: does now), but if it had both, it would.)

Perhaps more importantly, it means that properties that Object has (get, has, update, extend, id) should also be available on all other types.

@masak
Copy link
Owner Author

masak commented Jul 27, 2016

All of a sudden I'm uneasy about a choice I made early on with objects: that the Object type has the special behavior of allowing any property key, both at creation, and during modification later.

Though I can't quite put my finger on it, I think this will interact badly with #33. This issue makes it a bit clearer why that might be.

Object currently implies one more thing, besides being the Top type: it allows arbitrary property names. All subtypes have fixed sets of property names.

This violates Liskov substitutability, and leads to the usual problems when doing upcasts.

my n: Int = 42;
my o: Object = n;
n.gnarxl = 7000;    # compiles fine, blows up at runtime: .gnarxl doesn't exist on Int

Perhaps my biggest shame in all of this is that I've for a long time disliked what Perl 6 does with the type hierarchy and Mu/Any/junctions. It feels somehow wrong to "special-case" the type system itself like that, encoding dispatch rules into it that don't fall out of the ordinary message passing mechanism itself, but needs to be supplied by the dispatcher/language/substrate. The special case introduced with Object is not far removed from this.

Currently I'm thinking of whether to fix this. An obvious fix would be this:

  • Make Object instances non-extendable. They'd essentially only contain the standard built-in methods. Object can still be a concrete class; experience shows that people like to create these for e.g. id uniqueness reasons.
  • Create a new type Hash that takes over the old job of Object.
  • Consider making Hash accept not just Str keys, but any hashable key. (But maybe make this a separate task.)
  • Make the syntaxes { ... } and new { ... } create a Hash instead of an Object. If you want to create an Object, you'd have to do it as new Object { ... }.

@vendethiel
Copy link
Collaborator

why does this compile fine though?

@masak
Copy link
Owner Author

masak commented Jul 27, 2016

Oh, sorry, I just noticed I had a typo in the example I wrote:

my n: Int = 42;
my o: Object = n;
n.gnarxl = 7000;    # compiles fine, blows up at runtime: .gnarxl doesn't exist on Int

The last line should of course be

o.gnarxl = 7000;    # compiles fine, blows up at runtime: .gnarxl doesn't exist on Int

Does that make it clearer why it compiles fine?

@vendethiel
Copy link
Collaborator

Yes, much more sense :)

@vendethiel
Copy link
Collaborator

Then: I'm very much against a top type that's Hash.

@masak
Copy link
Owner Author

masak commented Jul 28, 2016

  • Make the syntaxes { ... } and new { ... } create a Hash instead of an Object. If you want to create an Object, you'd have to do it as new Object { ... }.

I agree with my past self about { ... }, but disagree about new { ... }. It's better to associate new with objects instead of objects and hashes. Also, by removing that use of new, we also remove the last place where the type after new is optional. (Making the syntax more consistent.)

After all, if new { ... } created hashes, why shouldn't new [ ... ] create arrays? Which it could, of course... and maybe one could make the argument that these are allocations of non-primitive types, so new kind of makes sense. But (ironically), this issue is all about how there are no primitive types in 007 — so let's just simplify things and go with { ... } without the new.

@masak
Copy link
Owner Author

masak commented Aug 15, 2016

Putting Object at the top will end up affecting the way we think about boolification. Right now we say "objects are truthy iff they have more than zero properties". That's clearly not going to cut it for all the types.

masak pushed a commit that referenced this issue Sep 8, 2016
It seems we're simply not using the 'new { ... }' form in
practice.

Addresses #179, but also relevant for #144.
@masak
Copy link
Owner Author

masak commented Nov 1, 2016

I'm increasingly convinced that we're going to need to abandon types declared in Perl 6 using Perl 6's class and role. That was a stopgap solution anyway.

What we need to do is to get ourselves a minimal MOP-like thing, with a .declare-type method. Details pending.

Edit: There's now #242.

@masak
Copy link
Owner Author

masak commented Nov 2, 2016

Here are the things that we'd expect to work once Object is on top:

  • Should be able to update Q object properties
  • get and put should work on any of the Val:: objects and Q:: objects
  • has should work on everything
  • update should work on everything (and respect key restrictions)
  • Everything should have an id

@masak
Copy link
Owner Author

masak commented Nov 11, 2016

Putting Object at the top will end up affecting the way we think about boolification. Right now we say "objects are truthy iff they have more than zero properties". That's clearly not going to cut it for all the types.

Essentially, this is going to push us to having a userland boolification protocol.

And a stringification protocol too, from what I can see.

My initial idea is to just call those methods .Bool and .Str, respectively.

@masak
Copy link
Owner Author

masak commented Nov 11, 2016

In starting this one, I realized pretty soon that #184 is blocking on it.

@masak
Copy link
Owner Author

masak commented Nov 11, 2016

Oh, mostly because the Val::Sub type contains a properties static-lexpad which can currently get away with being an Object, but really wants to be a Dict post-#184, because it needs to hold arbitrary keys and values.

Though, hm, I guess in a pinch it could do a poor-man's dictionary by being an Array, so it's not a total blocker...

@masak
Copy link
Owner Author

masak commented Jan 9, 2017

I'm going to slap the "needs-more-design" label on this one, because likely there are more horrors lurking in these waters, similar to the boolification protocol thing.

For example, it does feel like 007 will need to be aware of the primitives/reference types distinction in some way, but I'm not sure how. Can you create several instances of the Int 5? If so, then why do they all compare equal? If not, then what mechanism makes sure you only ever have one 5?

@masak
Copy link
Owner Author

masak commented Aug 8, 2017

Been thinking about this one. I think one significant first step could be had by declaring _007::Type in the Perl 6 code base, and then instantiate it for each 007 type we have. Then we have something like _007::Object which are everything that isn't a _007::Type. We can smooth over the differences between _007::Type instances and _007::Object instances so that it isn't noticeable from 007 userland.

@masak
Copy link
Owner Author

masak commented Sep 16, 2017

For example, it does feel like 007 will need to be aware of the primitives/reference types distinction in some way, but I'm not sure how. Can you create several instances of the Int 5? If so, then why do they all compare equal? If not, then what mechanism makes sure you only ever have one 5?

This has been neatly resolved in #242. The answer is that no, 007 doesn't have primitive types. However, some types are "wrapped" and their exact specification disappears into the Perl 6 layer.

masak pushed a commit that referenced this issue Aug 8, 2018
This re-establishes Val::Object, but this time as the kind of
empty type that goes at the top of a type hierarchy.

As a consequence, "everything is an Object", which right now is
hard-coded into the ~~ and !~~ operators.

In the fullness of time, we'll instead code it into the type
hierarchy itself, so that a special case is not needed.

Closes #144.
@masak masak closed this as completed Sep 28, 2018
masak pushed a commit that referenced this issue Jan 16, 2019
This re-establishes Val::Object, but this time as the kind of
empty type that goes at the top of a type hierarchy.

As a consequence, "everything is an Object", which right now is
hard-coded into the ~~ and !~~ operators.

In the fullness of time, we'll instead code it into the type
hierarchy itself, so that a special case is not needed.

Closes #144.
masak pushed a commit that referenced this issue Feb 11, 2019
This re-establishes Val::Object, but this time as the kind of
empty type that goes at the top of a type hierarchy.

As a consequence, "everything is an Object", which right now is
hard-coded into the ~~ and !~~ operators.

In the fullness of time, we'll instead code it into the type
hierarchy itself, so that a special case is not needed.

Closes #144.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants