Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Age discrimination in Perl 6 using subsets and multiple dispatch (perl.org)
30 points by ekiru on Aug 22, 2010 | hide | past | favorite | 11 comments


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;


You do understand that “due to a bug in Rakudo” means that this is a limitation of the implementation rather than the language, and that it will be fixed in due time – right?


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).


That did make me laugh:

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

First, this was about Perl 6, not Perl.

Second, it was not official documentation, but from a (quite interesting) blog post -- which, as the author noted, explained the matter directly afterwards.

I'm frankly expecting better from the clockwork fish.


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.


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.


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.


1. The values of :after methods are ignored. 2. You're calling person-age from within the method so you enter infinite recursion.

What you were looking for is:

  (defmethod person-age :around ((someone person))
    (let* ((age (call-next-method))
	   (category (if (< age 16) 
		         :child
		         (if (>= age 66)
			     :senior
			     :adult))))
       (values age category)))


yeah, I am on a pristine box without slime; and those written after a few beers late at night.


Predicate dispatch in CLOS:

ftp://publications.ai.mit.edu/ai-publications/2001/AITR-2001-006.pdf


Well, there goes Sunday. I was supposed to port a bunch of stuff to ABCL tomorrow, but I can't wait to fall asleep tonight and read that paper first thing in the morning. Wish I could fork multiple selves of me to devour all these goodies, Rainer :-)

  (mapcar (lambda (task)
            (bt:make-thread
                 (lambda ()
                   (funcall (car task) (cdr task))))
          (loop for me from 1 to *infinity*
                and problem in *all-problems*
                collecting (list me problem)))




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: