in reply to Re: Haskell vs Perl 6 (was Re: Capturing parenthesis and grouping square brackets)
in thread Capturing parenthesis and grouping square brackets

I'd add a note saying something like "While the code looks like the two trees are flattened to lists first and then compared, Haskell is lazy, which means that behind the scenes it will only do as much work as it has to and stop flattening once it finds the first mismatch. Simply don't worry about it. It works."

I've read both codes and both explanations (the fact that the one for Haskell is quite a bit shorter is telling. On a Perl forum.) and still think the Haskell code is much clearer and cleaner.

A better explanation of the Perl6 code might help somewhat. "take yields a value that is added to a gather'd list." Humpf?!? "Cool is a flavor of undef." Whatafuck?

Heck the all fringe($a) Z=== fringe($b) alone is crazy enough to require two pages of explanation.

Jenda
Enoch was right!
Enjoy the last years of Rome.


Comment on Re^2: Haskell vs Perl 6 (was Re: Capturing parenthesis and grouping square brackets)
Download Code
Re^3: Haskell vs Perl 6 (was Re: Capturing parenthesis and grouping square brackets)
by BrowserUk (Pope) on Jul 03, 2013 at 14:58 UTC
    A better explanation of the Perl6 code might help somewhat.

    If you look at Re: Challenge: Perl 5: lazy sameFringe()? (threads::Gather Configurably truely lazy or semi-lazy), you see a Perl5 implementation that more closely mirrors the Haskell variant.

    It uses a home brewed implementation of take & gather that uses a thread and a queue under the covers to provide a truly lazy process.

    That homebrew take/gather looks like this:

    #! perl -slw package threads::Gather; use strict; use Exporter; use threads; use threads::Q; our @ISA = qw[ Exporter threads::Q ]; our @EXPORT = qw[ take gather ]; sub take; sub gather (&$@) { no strict 'refs';# no warnings 'redefine'; my( $code, $Qsize ) = ( shift, shift ); my $Q = threads::Q->new( $Qsize ); my $caller = caller; local *{ "$caller\:\:take" } = sub (_) { $Q->nq( @_ ); }; local $^R = $code; async( sub{ &$code; $Q->nq( undef ); }, @_ )->detach; return sub { $Q->dq; }; } return 1 if caller;

    The take() sub is only available within the gather code block, and simple pushes its argument(s) onto a queue.

    The code block itself is run in a separate thread; and the return of the gather sub is an iterator that simply dequeues values from the queue.

    If teh size of the queue is set to 1, then each it take() will block the queue until the value it pushed is popped off using the iterator.

    Not efficient -- a thread swap for every leaf -- but truly lazy.

    If you supply a bigger queue size than one -- on the call to gather(), then gather block will be able to take() that many values before blocking and a thread swap is forces; thus you get a 'batching' behaviour which is more efficient.

    This is (I suspect) the intent of the perl6 take/gather "at some future point in time", but currently it take is simply a push to a hidden array and once all the values have been collected, the gather subroutine iterates over that array popping them off as it goes. (See Perl6::Gather for a crude Perl5 implementation.)

    But, as of yet -- and if I am any judge, for the foreseeable future -- that "at some future point in time" is unlikely to ever arrive.

    Not that threading is the only way a lazy take/gather could be implemented. It could be done today in Perl5 using Coro if that runs where you live.

    It could also be done using green (user space) threading; or continuations; or go-style goroutines. And if you believe the P6 specs, it intends to support all of those and more.

    But, when (to my knowledge) there has been no (ZERO) attempt to provide and flavour of multitasking in any of the p6-ish interpreters, it doesn't look likely. YOU cannot add this stuff successfully as an after thought; you have to architect it in from the ground up or you end up with iThreads or similar.

    And it is actually quite easy to do, if you do it from the start. As soon as the run-loop/dispatcher is capable of executing any single opcode, it should be possible to start two copies each in its own thread and dispatch them concurrently.

    Heck the all fringe($a) Z=== fringe($b) alone is crazy enough to require two pages of explanation.

    I'm far from expert in P6, but I don't have a problem with that line. The Z=== looks a bit weird, (but so did <=> and =~ etc. when I first came to perl5), but its obviously some form of comparison operator.

    all is like the function of the same name in List::MoreUtils. Only true if all the arguments are true, and short-circuiting as soon as the first one is false.

    So, if all the fringes of $a are equal to all the fringes of $b, the trees are equivalent. Seems clear enough, even if I'd have to look up the details. (I had to do that for the Haskell code; and I often have to do the same for C-runtime routines and the more obscure Perl built-ins.)


    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".
    In the absence of evidence, opinion is indistinguishable from prejudice.
      This is (I suspect) the intent of the perl6 take/gather "at some future point in time", but currently it take is simply a push to a hidden array and once all the values have been collected, the gather subroutine iterates over that array popping them off as it goes.

      This is incorrect. The gather/take implementations in Niecza and Rakudo are lazy and have always been.

      It could also be done using green (user space) threading; or continuations; or go-style goroutines. And if you believe the P6 specs, it intends to support all of those and more.

      But, when (to my knowledge) there has been no (ZERO) attempt to provide and flavour of multitasking in any of the p6-ish interpreters, it doesn't look likely. YOU cannot add this stuff successfully as an after thought; you have to architect it in from the ground up or you end up with iThreads or similar.

      The general framework for P6's concurrency and parallel processing capabilities was designed many years ago. The design wisely abstracts across backends with varying concurrency primitives (CLR, JVM, Parrot, and other VMs). Implementations have then followed this design.

      Pugs was written in Haskell so had Haskell's capabilities underneath from day 1.

      Rakudo was born with usable continuations and coroutines underneath it on day 1 of its existence (because "Parrot is based on continuations"). The threads story is more complex; interested parties would best research for themselves.

      "gather/take has controlled the design of Niecza from day 1". Niecza has continuations, coroutines, and threads.

Re^3: Haskell vs Perl 6 (was Re: Capturing parenthesis and grouping square brackets)
by raiph (Friar) on Jul 04, 2013 at 15:27 UTC
    I'd add a note saying something like "While the code looks like the two trees are flattened to lists first and then compared, Haskell is lazy, which means that behind the scenes it will only do as much work as it has to and stop flattening once it finds the first mismatch. Simply don't worry about it. It works."

    This is misleading, as it suggests that P6's gather/take is not lazy. Would you be willing to update your comment to fix this mistake?

    Update: I was wrong. Apologies to Jenda.

      BEG YOUR PARDON?!?

      How does a note suggested to be added to the explanation of the Haskell code "suggest P6's gather/take is not lazy"? There was no mention of Perl6 in that comment, neither direct, nor intended, nor implied!

      Jenda
      Enoch was right!
      Enjoy the last years of Rome.