develooper Front page | perl.perl5.porters | Postings from August 2012

Re: fixing smartmatch just hard enough (and when, too)

Thread Previous | Thread Next
Damian Conway
August 22, 2012 16:36
Re: fixing smartmatch just hard enough (and when, too)
Message ID:
Robert Sedlacek wrote:

> Do you have any specific use-case for bidirectional overload acceptance?

No. Just a lot of (bad) experience with both OO code and code that
extends existing behaviours.

The point here is simply that, when we allow new types (represented by objects)
to be included in the smartmatching system, then it should be up to those new
types to *fully* determine how they interact with that system.

In other words, if you build a class, and if that class overloads ~~,
then it's important that the overloading predictably take control of
*every* smartmatch involving objects of the class. Because only the
class itself can know what smartmatching semantics make sense for its
objects (regardless of which side of the smartmatch they appear), and
hence determine how smartmatching should be implemented when one of its
objects is involved.

> I can see how this would be useful for some object types. But it also
> might make certain other cases impossible.

I don't believe it would make any cases impossible.

If you're overloading ~~ for the class, you handle every case in the
If you're not overloading ~~ for the class, the issue doesn't arise is the
first place, since smartmatching falls back on the rest of the table.

> Say I have an object representing a type itself:
>   23 ~~ $IntType
>   $IntType ~~ 23
> I'd find it odd if that would work, but won't if I represent the type
> check as a code reference.

But it *would*: you'd just write the ~~-overloading method for your TypeObj
class in such a way that it handles coderefs the way you want. If you want
the standard smartmatching behaviour (i.e. pass the TypeObj to the sub)
you'd implement it like so:

    package TypeObj;

    use overload
        '~~' => sub {
                    my ($type_object, $comparand, $reversed) = @_;

                    # What are we smartmatching against...
                    my $comparand_type = ref $comparand;

                    # If it's a subref, pass the typeobj to the sub...
                    if ($comparand_type eq 'CODE') {
                        return $comparand->($type_object);
                    else {
                        return $comparand_type eq $type_object->type;

But if you want it to match against a code ref the same way it matches
against a number (i.e. is this other operand of the correct type?),
you'd implement it like this instead:

    package TypeObj;

    use overload
        '~~' => sub {
                    my ($type_object, $comparand, $reversed) = @_;

                    return ref($comparand_type) eq $type_object->type;

That's the entire point: if you overload ~~, then you get to (and have to)
decide the behaviour for all cases involving that class.

> Also, I wouldn't be able to match the object
> itself, right? For example:
>   $IntType ~~ sub { ... }
> would ask the object on the left to match, but not the code reference

No. It would call the overloaded ~~ on the Type object, which would then
decide how to handle that smartmatch (as in the examples above).

> This also means that an externally passed object could, unintentionally,
> change the meaning of my given/when constructs.

Yes. But that is *always* the case, for any operation involving objects
with overloaded operators.

> Is it maybe possible to have separate overloads for 'smartmatcher' and
> 'smartmatchee' (couldn't find better terms right now)?

You *can* have that: you just implement the overloading so as to check
whether it was passed the extra 'reversed' argument, and then do
whatever you want in either branch:

    package TypeObj;

    use overload
        '~~' => sub {
                    my ($object, $other_operand, $reversed) = @_;

                    if (!reversed) {
                        # Do this if object is smartmatcher...
                    else {
                        # Do this if object is smartmatchee...

>> The when construct
>> ==================
>>     Form                  Meaning
>>     ==================    ==================================
>>     when (EXPR)  {...}    if ($_ ~~ (EXPR))     {...; break}
>>     when {BLOCK} {...}    if ($_ ~~ sub{BLOCK}) {...; break}
> I do wonder if `when {...}` could (internally) be an `if (do {...})`
> instead of `if ($_ ~~ sub { ... })` for performance reasons,

I'd certainly like the better performance too, but I'd *hate* the fact
that it would eliminate the implicit ~~, and hence would by-pass
overloading if $_ contains an object. :-(


Thread Previous | Thread Next Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at | Group listing | About