in reply to Re^2: rough approximation to pattern matching using local (Multi Subs)
in thread rough approximation to pattern matching using local

In the official Perl 6 glossary this feature is called subsignatures. It's not actually limited to multi subs or even to subs but can be used in regular assignments too.

There's some more at "Destructuring parameters" in the official end user doc, Moritz's nice article Pattern Matching and Unpacking, and the language design docs starting at Unpacking array parameters.

  • Comment on Re^3: rough approximation to pattern matching using local (Multi Subs)

Replies are listed 'Best First'.
Re^4: rough approximation to pattern matching using local (Multi Subs)
by BrowserUk (Patriarch) on Jan 27, 2015 at 00:55 UTC

    Does it work (now)? If so, in which of the disparate implementations? (A (direct) download link appreciated.)

    What penalty does it impose over two (or more), separate (differently named) subroutines?

    Ie. Is the syntactic sugar worth the implementation costs?


    With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority". I'm with torvalds on this
    In the absence of evidence, opinion is indistinguishable from prejudice. Agile (and TDD) debunked

      Does it work?

      I'll start with a narrow view of your "Does it work?" question:

      • The linked code works in the Perl 6 compiler I'm using. (By "linked code" I mean the code in the links I provided, and the red-black tree on Rosetta Code that Moritz linked to.)

      • The compiler I'm using is Rakudo on MoarVM. (Rakudo is nowadays the de facto reference Perl 6 compiler and MoarVM the de facto reference Rakudo backend.)

      • I set up my Rakudo on MoarVM using the "Rakudobrew" tool. Running `perl6 -v` displays "This is perl6 version 2014.12-117-g2542c3d built on MoarVM version 2014.12-4-g7e95c05". (To duplicate my setup or download any version of Rakudo/NQP/MoarVM use Rakudobrew.)

      A look at 'roast', the suite of tests for a Perl 6 compiler, provides part of a broader view of the "does it work?" question:

      • To start subjectively assessing test coverage for a feature, first search the design documents to find pages that look relevant, then individually search each such page to find verbiage that seems relevant. Then look for nearby test file links embedded inline within the design doc.

        (Collectively the design docs contain links to most of the 35K+ tests in 'roast'. The rest are for covering things like the 140+ articles in the Perl 6 Advent Calendars; these now have close to 100% coverage thanks to David Warring's determination to sort them out in the latter half of 2014.)

        For example, the design doc section I linked as Unpacking array parameters embeds links to a couple test files from roast: S06-signature/unpack-array.t and S06-multi/unpackability.t.

      • To assess test results, look at a recent 'roast', decide which tests are important (eg search for the term(s) relevant to your feature of interest and/or the names of test files of interest) and then focus mainly on any of these tests that failed or are tagged "todo" or "skip".

        For example, searching for "signature" ("pattern matching" doesn't match anything) in the current latest 'roast' run for Rakudo on MoarVM reveals 80+ matches. At the time of writing this I consider just 36 to be of real interest.

        (I ignored matches such as todo tests for the "signature" method on the not-yet-implemented compiler object representing the distro used to compile the compiler.)

        An informal analysis of the matches of interest to me indicated around 500 relevant passing tests and 65 todo/skipped. About half of the latter look like they really need to be done by 6.0, i.e. supposedly by the end of this year, especially the ones covering binding of return values and named arguments. YMMV.

      I plan to properly address your "is it worth it?" speed question in a separate comment this weekend.

      Is it slower?

      What penalty does it impose over two (or more), separate (differently named) subroutines?

      Ie. Is the syntactic sugar worth the implementation costs?

      A simple test I thought might be useful:

      my $t; sub a {}; sub b {}; multi sub c (1) {}; multi sub c (2) {}; for ^7 { my $iterations = ^10 ** $_; say $iterations.max ~ " calls"; $t = now; for ^$iterations { $_ %% 2 ?? a() !! b() }; say "regular: {now - $t}"; $t = now; for ^$iterations { c($_ %% 2 ?? 1 !! 2) }; say "multi: {now - $t}"; }

      With a recent Rakudo on MoarVM:

      1 calls regular: 0.00630924 multi: 0.0046663 10 calls regular: 0.00344620 multi: 0.00392575 100 calls regular: 0.0056468 multi: 0.0079017 1000 calls regular: 0.0238749 multi: 0.02800533 10000 calls regular: 0.0541161 multi: 0.287505 100000 calls regular: 0.43776129 multi: 2.39229575 1000000 calls regular: 4.45541020 multi: 24.2623499

      Unfortunately the numbers are clearly pretty useless.

      I hope to l follow up this upcoming week when I've got a better handle on how to usefully answer your question. I'm thinking I'll install perl6-bench which has robust logic for squeezing noise (startup, gc, etc.). out of results. Other than the noise in the numbers, perhaps you could confirm the basic benchmark I created is a reasonable test in principle to answer your question?

      Update

      I believe the "benchmark" I used above caused confusion in this thread due to its inclusion of implicit `where` constraints (where the argument had the value 1 or 2). These literal fixed value `where` constraints are supposed to one day be resolvable at compile-time but as I write this (April 2015) they are resolved at run-time.

      So here's an alternative "benchmark":

      my $t; class A {}; class B {}; sub a (A $a) {}; sub b (B $a) {}; multi sub c (A $a) {}; multi sub c (B $a) {}; for ^7 { my $iterations = ^10 ** $_; say $iterations.max ~ " calls"; $t = now; for ^$iterations { $_ %% 2 ?? a(A) !! b(B) }; say "regular: {now - $t}"; $t = now; for ^$iterations { c($_ %% 2 ?? A !! B) }; say "multi: {now - $t}"; }

      And the timing results:

      1 calls regular: 0.00321471 multi: 0.00306454 10 calls regular: 0.0029107 multi: 0.0029139 100 calls regular: 0.0032379 multi: 0.00331093 1000 calls regular: 0.0091466 multi: 0.00742758 10000 calls regular: 0.04885046 multi: 0.053550 100000 calls regular: 0.5330036 multi: 0.5973669 1000000 calls regular: 5.6755910 multi: 5.946978

      So, as I tried to explain in other comments in this thread, type checking and candidate ordering of multisub calls are generally completed at compile-time, just like regular subs.

        Okay. I'm assuming that '^n' means '0 to n-1'. Despite that it doesn't explain the presence of the '^' in the line: my $iterations = ^10 ** $_;; which seems to me (from the displayed effect of the statement), to be exactly the same as: my $iterations = 10 ** $_;? (Ie. no caret.)

        Anyway, making my assumption, and ignoring the anomaly, what your benchmark seems to show is that alternately calling two non-multi subs, 1/2 a million times each, takes 1/6 the time required to call one of two multisubs determined by pattern matching 1/2 a million times each.

        My conclusion:

        Is the cost of the convenience of runtime functional (argument) pattern matching worth the benefit of not having to decide which function to call explicitly?

        In my world: when 10 minutes becomes an hour; or an hour becomes six hours; or 6 hours becomes 36? Absolutely not!

        I suspect that in Haskell; ML; OCaml; Erlang; Miranda; Clean; -- Ie. compiled functional languages -- the runtime cost is minimal because the compilers can, if not completely infer the matching function at compile-time; at least substantially reduce the possibilities through type-inference.

        But (again; just my suspicion), P6 has to in(tro)spect the argument list at runtime and then attempt a best match (or fail?) against the signatured possibilities for the given function name, at runtime. Hence the performance cost.

        Update:For reference: This Perl 5.10.1 chosing between two functions and executing each of them 1/2 million times:

        #! perl -slw use strict; use Time::HiRes qw[ time ]; sub a{} sub b{} my $start = time; $_ % 2 ? a() : b() for 1 .. 1e6; printf "%.9f\n", time() - $start; __END__ C:\test>junk99 0.275810003

        That's 16 times faster than your a() | b() code; and 88 times faster than the P6 multi-sub version.


        With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority". I'm with torvalds on this
        In the absence of evidence, opinion is indistinguishable from prejudice. Agile (and TDD) debunked
Re^4: rough approximation to pattern matching using local (Multi Subs)
by LanX (Saint) on Jan 27, 2015 at 03:06 UTC
    Unpacking is nice and it's good to see this feature not only in Python...

    But I'm afraid you are missing the point here.

    "pattern matching" a la Haskell allows to filter for literal arguments not only types like demonstrated for multi subs.

    For instance a naive Fibonacci (in pseudo-code) can look like this

    Multi fib(0|1) = { 1 }; Multi fib($i) = { fib($i-1) + fib($i-2) };

    Can I do this with multi subs in Perl 6?

    Cheers Rolf

    PS: Je suis Charlie!

      The following works in current Rakudo:

      multi fib ($ where 0|1) {1} multi fib ($i) { fib($i-1) + fib($i-2) }

      According to the relevant design docs I ought to be able to omit the `$ where ` bit but that bit of sugaring has not yet been implemented in Rakudo.