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

fixing smartmatch just hard enough (and when, too)

Thread Next
Ricardo Signes
August 16, 2012 14:58
fixing smartmatch just hard enough (and when, too)
Message ID:

So, last year I said "we really ought to cut most of smart match away."

I've slept on it about 380 times, and I'm still for it.  Here's the new plan:

## The New ~~ Operator

    $a      $b                  Meaning
    ======= =======             ======================
    Any     undef               ! defined $a
    Any     ~~-overloaded       ~~ overloading is used
    ~~-ol   Any                 ~~ overloading is used (reversed)
    Any     CodeRef, &{}-ol     $b->($a)
    Any     Regexp, qr-ol       $a =~ $b
    Any     Simple              $a eq $b
    Any     Any                 fatal

This is a nearly a subset of the current behavior.  Here are some major points:

  (1) This is ordered for the sake of knowing which overload to use first.

  (2) A simple scalar (non-reference, non-glob, non-vstring) on the rhs is
  used for stringwise equality.  I have not yet seen any convincing argument
  that we can correctly intuit an eq/== behavior, so we pick one.

  Forcing numeric comparison can be done with things like the helpers found in
  Smart::Match:  things to produce a CodeRef that has the correct behavior.

  (3) Container types (array, hash) are no longer treated specially.  They were
  the most unintuitive case, and they get kicked out.  More on that later.

This gets is back to what I've always said is the real power of ~~: a
simple way to provide "a test" to run against a value, where the creator of the
value know just how it will be used.  With one exception, we're dispatching
entirely based on the rhs, and they're all obvious.

"Oh no!" you may cry, "What about $x~~@opts?  All I wanted was an `in`

## Core Junctions

Well, that's why we have junctions.  Remember those?  Of course you do, they're

  if ($x ~~ any( qw( John Paul Ringo ) )) {
  } else {

We already have very nice junctions in Perl6::Junction.  We bring that into the
core (with a new name) and we get nice useful ways to put tests together.  We
also get them anywhere else we want them.  For me, that's "all over the place."
I bet it will be for lots of other people, too.

## Backcompat and ~~

My anxiety over backward compatibility for ~~ is not gut-wrenching.  Still, it
would be nice to do what we can.

After a lot of talk about making ~~ pluggable across different scopes, I think
it's a non-starter.  It's just not the kind of thing we can plug this way,
because of the way in which we expect it to be used.  If we expect users to
pass a test from their code to some other code, they need to know how that code
will use it.  It needs to be the same everywhere.

It would be nice to provide a best-effort backcompat with a deprecation warning
when possible.  I'd like to know how good that effort can be.  Since the new
table is in many ways a subset of the old one, I think it can be pretty good.

    As previously mentioned, the "switch" feature is considered highly
    experimental; it is subject to change with little notice.  In
    particular, both "given" and "when" have tricky behaviours that are
    expected to change to become less tricky in the future.  Do not rely
    upon their current (mis)implementations.
    -- perlsyn

## Fixing given and when

I think we already fixed given pretty hard.  I need to look at bit more at it,
but let's take it as "fixed enough" for now.


Oh, when, how you trouble me.

I propose that `when(X)` *always* means `$_ ~~ X`.  All of the special cases
for parsing and interpreting the inside of when can go.  They can all be
reduced to `when(sub{...})`.  I would also smile upon `when({...})` acting as a
test block.  (Since you can't put a hashref on the rhs of ~~ anymore, there is
no ambiguity.)

The exceptions (from perlsyn's "Experimental Details on given and when") are:

  EXCEPTION             WAS                      IS NOW
  1. subroutine call -- when (foo()))       --   when ({ foo() })
  2. regex match     -- when ( /foo/ )      --   when (qr/foo/)
                     -- when ( $_ !~ /foo/) --   when ( none( qr/foo/ ) )
  3. smart match     -- when ($a ~~ $b)     --   when ({ $a ~~ $b })
  4. comparison      -- when ($a lt $b)     --   when ({ $a lt $b })
  5. some builtins   -- when (eof)          --   when ({ eof })
  6. negation        -- when (! $x)         --   when ({ ! $x })
  7. bool filetest   -- when (-r)           --   when ({ -r })
  8. flip flops      -- when ($a .. $b)     --   when ({ $a .. $b })
  9. conjunction     -- when ($a && $b)     --   when ( all( $a, $b ) )
                                              or when ({ $a && $b })
  0. disjunction     -- when ($a || $b)     --   when ( any( $a, $b ) )
                                              or when ({ $a || $b })

Also, `when` should be valid inside an auto-topicalizing `while`, providing an
implicit next, making its behavior more generic so that `when` can be used
safely everywhere that it makes sense.

## In Conclusion

I think this makes ~~ and given/when both totally predictable.  I've gone over
this design quite a bit, and I think it turns these features from a liability
to an asset.  I also think that we'll be able to eliminate quite a lot of
documentation, which I take as a very good sign.  (A lot of that documentation
includes things like "this part is really hard to understand.")

When ~~ works like this, I will use it all the time.


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