-
Notifications
You must be signed in to change notification settings - Fork 561
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
Add an isa
operator
#17212
Add an isa
operator
#17212
Conversation
a6ac5e5
to
37d6c4a
Compare
As a first draft it now stands ready for review and consideration, though there is one outstanding issue of semantics left. We may want to consider whether barename package names should be looked up into HV*s at compiletime and if not fail to compile, at least under if($x isa "Late::Loaded::Package") { ... } Though on the reverse hand, named package dispatch is only resolved late at runtime and typoes aren't detected at compiletime there either: Nonexistent::Package->new() though at least in that latter case the code would die at runtime, rather than just silently evaluating to false. I'm open to ideas in either direction. Discussion? |
PS. also open to discussion on whether I should squash more of these commits together before merging. |
Maybe add a link to #17200 in the PR description so we know what it is about? |
I don't see the approach of checking the package hash for existence at compile time breaking much existing code, but I can see how it will make things harder if you're trying to make a duck-typing class fit some imaginary class hierarchy. As an imaginary example, consider using a One thing against compile-time checking is if an object is optional (consider a cookie jar). Then the module implementing this ( |
pp.c
Outdated
|
||
/* TODO: Support right being an HV ref to the stash directly */ | ||
|
||
SETs(boolSV(sv_derived_from_sv(left, right, 0))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this correct? Shouldn't it call isa() on the class/object so isa() overrides work?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ahyes, in the general case almost certainly - and definitely for the DOES
version that will be necessary. I was hoping to avoid that for this initial implementation as it's quite a rare case.
I wonder if it would be possible to optimise it a bit. Attempt to resolve the "isa"
method and i the likely event that it doesn't exist continue with this optimised path, but if it does exist then invoke that instead via call_sv()
. I was hoping to avoid a nested code invocation in the common case that nobody overrides sub isa
though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've had an attempt at this, but I can't seem to get the nested call_sv()
call quite right. It mostly runs but then crashes:
http://paste.scsys.co.uk/586753
if I put various warn() calls in there I can see it mostly gets to the right places, or even the presence of warn() makes it work entirely but then if I remove the warn it stops again :( Can anyone suggest what I'm doing wrong here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The answer, for posterity, is that the SvTRUE()
macro might evaluate its argument more than once, so doing SvTRUE(POPs)
is always a bug. It should be written SvTRUEx(POPs)
or maybe SvTRUE(TOPs); POPs;
. Doing that it works fine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are there any valid reasons to override the isa
method?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@xenu I think it is surely easily possible to find examples on CPAN of people overriding ->isa
to do all sorts of odd things. Whether any of those are "legitimate" is open to question. I'm simply trying to provide a more efficient (notation/implementation) of the same behaviour as is usually achieved by just calling that now.
Besides, there's precedent in the overload
module to allow all sorts of unary, and binary infix operators between types to be overridden. To not allow this isa
in similar fashion would seem strange.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are there any valid reasons to override the
isa
method?
Apparently Test::MockObject uses an overridden isa.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I override isa in Object::ForkAware also.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Both Moo and Type::Tiny override isa in some places to allow interoperability with Moose.
Somewhat related, using:
Will trigger a warning:
And return the value |
pp.c
Outdated
* TODO: Consider if we want a NOUNIVERSAL flag for requesting this in a | ||
* more obvious way | ||
*/ | ||
isagv = gv_fetchmeth_pv(SvSTASH(SvRV(left)), "isa", 1, 0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you use gv_fetchmeth_pvn instead of gv_fetchmeth_pv for a lesser run-time cost?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems so. 04987d4
I won't merge this yet as it will collide with #17229 and I'd prefer to see that one in first so I can take advantage of it yet, but aside from that I think this is basically ready now. Any further objections? |
I am generally happy with this proposal. I am ok with it being a word infix op, if that helps elsewhere. I think that the class name should behave exactly as it does in Foo::Bar->new() for consistency for all permutations of quoted or bare or with trailing '::'. I think it should respect 'isa' method overriding and any dereferencing overloading. I think that the new test file should pass 't/TEST -deparse t/op/isa.t' (this is where the test file is first fed through Deparse then run). Finally, i would ideally like the main body of pp_isa() to be separated out into a separate function that is part of the API, and which I can then call from signature-handling code to guarantee that it provides exactly the same semantics. |
I believe it already does those things; happy to adjust and fix them if not as those sound sensible.
Ah I can try testing that too.
Yes that sounds a good idea. Do you have a suggestion on what I should name it? |
Looking happy:
|
@iabyn I've added a new function documented thus:
The |
Annoyingly there was already an API function called I've also opened #17334 to suggest we look further into that and try to clean up the names somewhat. |
I think we're ready for review. |
Just a minor comment, but since my job is now 60% code reviews ... inconsistent spacing on the |
Huh.. Yeah; |
* Allocate opcode, keyword and feature bit * Implement pp_isa * Add basic unit test
…ll_sv() in the common case of classes that don't
…much older sv_isa()
Commit 813e85a, so this p.r. can be closed. |
An implementation of the
isa
operator as per #17200Outstanding issues: