

Age discrimination in Perl 6 using subsets and multiple dispatch - ekiru
http://blogs.perl.org/users/tyler_curtis/2010/08/age-discrimination-in-perl-6-using-subsets-and-multiple-dispatch.html

======
mechanical_fish
This is a pretty good advertisement for Lisp.

The Clojure article is about a piece of code. The Perl 6 article is about all
of the syntax that you need to understand before you can read the equivalent
piece of code.

Including this gem:

 _That asterisk is a special value called Whatever. It generally does what you
mean in a given situation._

Perl documentation is always good for a laugh. But, I wonder, what does
"generally" mean?

 _...Due to a bug in Rakudo's handling of Whatever when combined with chained
comparison operators, we have to write an explicit block for Adult._

Oh. It means that sometimes -- even in the context of a trivial nine-line
example -- the damned asterisk will mysteriously fail and will force me to
bang my head against the keyboard for half an hour until I finally figure out
that I just can't use it. Because, apparently, this works:

    
    
        subset Foo of Bar where *.baz < 16;
    

and this works:

    
    
        16 < $bar.baz < 66
    

but this does not work:

    
    
        subset Foo of Bar where 16 < *.baz < 66;

~~~
ekiru
Hi. I'm the author of the post.

> The Perl 6 article is about all of the syntax that you need to understand
> before you can read the equivalent piece of code.

I think it's more that the Clojure article just shows the code with very
little explanation. Whereas my post was intended to demonstrate some of the
basic capabilities of Perl 6's multiple dispatch and type system for people
who aren't very, if at all, familiar with Perl 6. People unfamiliar with
Clojure can probably guess what the code in the Clojure post does. I think I
was able to (although I've dabbled in Clojure and Common Lisp, so I can't
speak from the viewpoint of a total newbie to those languages). Some of the
Perl 6 syntax I devoted paragraphs to explain were probably as clear as
(defrecord Person [name age]), but I chose to explain them exactly to be sure
that any readers unfamiliar with Perl 6 were able to know the exact
semantics(e.g., that readonly attributes are the default). Some of the syntax
is not something someone who has never heard of Perl 6 will be able to look at
and know the meaning of immediately. Is that really such a problem, if the
meaning of such features(sans bugs in the implementations) can be explained
and fully understood in a couple of paragraphs and make things much more
convenient for the person who does know the language? You learn them in a day
or two and benefit from them for the entire time you use the language.

In addition: the two pieces of code are not equivalent. The Perl 6 functions
validate the types of their arguments. The age-group function can tell you the
age-group of both a Person and an age. As well, if you want to write another
multi dispatching on the age group of a Person, it's easier with the Perl 6,
because you just have to do "multi can-have-kids-meal (Child $kid) { True }"
and "multi can-have-kids-meal ($person) { False }".

> But, I wonder, what does "generally" mean? It means that you should read the
> rest of the paragraph to see some examples of its uses. Don't worry, though,
> the actual spec (<http://perlcabal.org/syn/S02.html>, grep for "The *
> character") is much more detailed. My explanation was a bit lackluster, I
> suppose, but I was trying to not devote much time to it, since the post is
> about multiple dispatch and the type system, not about the philosophy of
> DWIM as expressed in the simplicity and usefulness of Whatever.

> Oh. It means that sometimes -- even in the context of a trivial nine-line
> example -- the damned asterisk will mysteriously fail and will force me to
> bang my head against the keyboard for half an hour until I finally figure
> out that I just can't use it. Well, more like five minutes (from time that I
> noticed the problem until the time that I had figured out the cause of the
> bug in Rakudo). But, yes, there are still bugs in Rakudo. I doubt this
> particular one will be around much longer(although it'll probably still be
> in this month's Rakudo *, since the compiler release was Thursday).

------
mahmud
Circa 1984:

    
    
      (defclass person ()
        ((name  :initarg :name :accessor person-name)
         (age   :initarg :age  :accessor person-age)))
    
      (defmethod person-age :after ((someone person))
        (let* ((age (person-age someone))
              (category (if (< age 16) 
                           :child
                           (if (>= age 66)
                             :senior
                             :adult))))
           (values age category)))
    

Instead of having some outside "ticket" function to categorize the age of a
person, you should just instrument the generated PERSON-AGE accessor to add
the extra info you need. That way you don't need to modify the rest of your
system to use this data in non-standard ways. The accounting module, for
example, can take care of senior citizen and kid discounts of tickets without
having to be told about it. The system would extensible, without rewrite, once
you want to add seasonal discounts and other business-requirement unapparent
during development. (Looking into ContextL and Context Oriented Programming.)

Similary, print-name shouldn't exist as a standalone function for something so
mundane. You should just augment the builtin PRINT-OBJECT method that
specializes on the PERSON class to print suitable titles. You don't even have
to instrument the main PRINT-OBJECT, just the one that prints the actual
receipt to the paper-printing HARDCOPY stream.

    
    
      (defmethod print-object :around ((someone person)
                                       (stream hardcopy))
        (when (next-method-p)
          (let ((obj (call-next-method)))
               ...
               ...
            (with-open-printer (lpr #p"/dev/printer")
               (format lpr "..")))) ...
    
    

I mean, seriously; I hope we're not blogging back and forth about CLOS-101
applications of methods.

~~~
ekiru
I know that this is a pretty tame example of subset types. But do you really
think that modifying the return values of person-age for everything in the
system is more easily extensible than a subset type which is external to the
objects? Suppose that somewhere else in the system, you want to dispatch based
on whether a Person is old enough to legally drink alcohol. With subset types,
you don't have to be aware of the Child/Adult/Senior partition at all. With
the :after approach, you're dependent on the ordering of the after methods and
you have to know about the existence of the :child/:senior/:adult test.

Here's the code for buy-beer in Perl 6:

    
    
      subset CanDrink of Person where *.age >= 21;
      subset CannotDrink of Person where *.age < 21;
    
      multi buy-beer (CanDrink $person) { give $person, Beer.new }
      multi buy-beer (CannotDrink $kid) { call-cops-on $kid }
    

No other code in the system need be aware of it. No other code in the system
is affected by it.

~~~
mahmud
Subset types, at least in that form you have shown, do _not_ exist in Common
Lisp. That's a very MLish feature, and Perl keeps on surprising me :-)

Do another, more elaborate one when you have time. Would love to learn new
things.

