Prev: [ANN] Filtered functions
Next: Filtered functions.
From: Pascal Costanza on 5 Dec 2009 10:26 vanekl wrote: > On Dec 4, 9:19 pm, Raffael Cavallaro > <raffaelcavall...(a)pas.espam.s.il.vous.plait.mac.com> wrote: >> On 2009-12-04 17:08:39 -0500, Pascal Costanza <p...(a)p-cos.net> said: >> >>> I am very excited that I can finally annouce a public release of >>> 'filtered functions' >> This is excellent news! predicate dispatch portable across 6 >> implementations - amazing! >> >> thanks for sharing this >> -- >> Raffael Cavallaro > > Where did you read six? I thought his paper said it's only fully > implemented on one, SBCL. Back when we published the paper, it ran indeed only on 1.5 implementations. ;) (SBCL, and to a certain extent LispWorks.) I have put quite some effort into Closer to MOP to make this work well in other implementations as well. Filtered functions now work on Allegro Common Lisp, CLisp, Clozure Common Lisp, Embeddable Common Lisp, LispWorks and SBCL. It's still likely that the performance is not as good as it could be, but I have some ideas for that... Pascal -- My website: http://p-cos.net Common Lisp Document Repository: http://cdr.eurolisp.org Closer to MOP & ContextL: http://common-lisp.net/project/closer/
From: Pascal Costanza on 5 Dec 2009 10:28 Slobodan Blazeski wrote: > Any ideas where those would be useful? ContextL was interesting but I > never figured a natural problem where to use it. Did you check the paper? An extensible code walker could be a nice application. (That's like the interpreter, except that it doesn't interpret. ;) Jim Newton also described a code walker as a motivation for a similar extension of a CLOS-style object system he described earlier... Pascal -- My website: http://p-cos.net Common Lisp Document Repository: http://cdr.eurolisp.org Closer to MOP & ContextL: http://common-lisp.net/project/closer/
From: Pascal Costanza on 5 Dec 2009 10:45 Kenneth Tilton wrote: > What was wrong with COND? It's not extensible. Pascal -- My website: http://p-cos.net Common Lisp Document Repository: http://cdr.eurolisp.org Closer to MOP & ContextL: http://common-lisp.net/project/closer/
From: Kenneth Tilton on 5 Dec 2009 11:44 Slobodan Blazeski wrote: > On Dec 4, 11:08 pm, Pascal Costanza <p...(a)p-cos.net> wrote: >> Hi, >> >> I am very excited that I can finally annouce a public release of >> 'filtered functions', an extension of generic functions that Charlotte >> Herzeel, Jorge Vallejos, and myself have developed some time ago and >> that we are very excited about because it seems to be quite powerful in >> a number of very different scenarios. It took a while to release >> filtered functions, because it is a quite non-trivial extension of >> generic functions and requires a CLOS MOP implementation that is >> compliant with the AMOP specification to quite a deep level. Therefore, >> this required some serious preparation in the form of a much improved >> Closer to MOP library, that I released today as well. >> >> You can find filtered functions at the Closer project website athttp://common-lisp.net/project/closer/- below you will find a general >> overview of the concept. >> >> Filtered functions are an extension of generic functions, extended with >> a filtering step where the arguments received by a generic function are >> mapped to other values based on user-defined mapping functions. Those >> filtered values are then used to perform the actual selection and >> execution of applicable methods. Nevertheless, the methods that are >> eventually executed see the original objects as received by the generic >> function, and not the filtered ones. >> >> Here are some examples to illustrate the expressive power of filtered >> functions. >> >> Factorial >> ========= >> >> In order to be able to use filtered functions, we need to have filter >> functions that map received arguments to values that we actually want to >> base our dispatch on. For the factorial function, we want to distinguish >> between negative and positive numbers, and the number zero. For that we >> can just use the Common Lisp function SIGNUM that returns +1 for >> positive numbers, -1 for negative numbers, and just 0 for the number 0. >> The filtered function FAC can thus be defined as follows. >> >> (define-filtered-function fac (n) >> (:filters (:sign #'signum))) >> >> DEFINE-FILTERED-FUNCTION is exactly like DEFGENERIC, except that it can >> also define one or more filters. Here, it defines a filter with the name >> :SIGN wich specifices that the function SIGNUM is to be used for filtering. >> >> We can now define methods for FAC: >> >> (defmethod fac :filter :sign ((n (eql +1))) >> (* n (fac (- n 1)))) >> >> (defmethod fac :filter :sign ((n (eql 0))) >> 1) >> >> (defmethod fac :filter :sign ((n (eql -1))) >> (error "Fac not defined for negative numbers.")) >> >> Here, we use the qualifiers :FILTER :SIGN in the method definitions to >> indicate that we indeed want to use the :SIGN filter for method >> selection. We then use EQL specializers to ensure that the method >> definitions are applicable for the three different cases that SIGNUM >> yields. Remember that the method bodies always see the original >> arguments, not the filtered ones, and this is why the FAC methods can do >> the correct computations. >> >> State pattern >> ============= >> >> Filtered functions can be used to dispatch methods based on the state of >> an argument passed to a filtered function, which enables expressing >> State-like idioms. Assume the following simple CLOS class is defined for >> implementing a stack. >> >> (defconstant +stack-size+ 10) >> >> (defclass stack () >> ((contents :initform (make-array +stack-size+) >> :reader stack-contents)) >> (index :initform 0 >> :accessor stack-index))) >> >> Instances of this class have three different states: Such a stack can >> either be empty, or full, or anywhere in between (in 'normal' state). We >> can express this as a function that recognizes the state of a stack. >> >> (defun stack-state (stack) >> (cond ((<= (stack-index stack) 0) 'empty) >> ((>= (stack-index stack) +stack-size+) 'full) >> (t 'normal))) >> >> It is now straightforward to use stack-state in a filter named :state >> for the typical stack operations. >> >> (define-filtered-function stack-push (stack value) >> (:filters (:state #'stack-state))) >> >> (define-filtered-function stack-pop (stack) >> (:filters (:state #'stack-state))) >> >> (define-filtered-function stack-emptyp (stack) >> (:filters (:state #'stack-state))) >> >> We can now group the behavior of a stack according to its different >> states. Note that for 'normal' state, we do not need to mention the use >> of any filter here, because the methods are not specialized on anything >> specific anyway. (Filtered functions always allow for 'regular' methods >> alongside the filtered methods.) >> >> ;;; Normal state >> >> (defmethod stack-push (stack value) >> (setf (aref (stack-contents stack) >> (stack-index stack)) >> value) >> (incf (stack-index stack))) >> >> (defmethod stack-pop (stack) >> (decf (stack-index stack)) >> (aref (stack-contents stack) >> (stack-index stack))) >> >> (defmethod stack-emptyp (stack) >> nil) >> >> ;;; Empty state >> >> (defmethod stack-pop :filter :state ((stack (eql 'empty))) >> (error "Stack is empty.")) >> >> (defmethod stack-emptyp :filter :state ((stack (eql 'empty))) >> t) >> >> ;;; Full state >> >> (defmethod stack-push :filter :state ((stack (eql 'full)) value) >> (error "Stack is full.")) >> >> Note that we used a derived state function here, that determines the >> state of the stack based on some of its other properties. Since filter >> functions can be any functions, we could also use the reader of a slot >> as a filter function, and thus have the behavior of a filtered function >> depend on the explicit state of an object. >> >> Filtered functions can do a lot more: As already mentioned, they can use >> more than one filter; filter functions can see all the arguments a >> generic function receives; and filters can be guarded, which means that >> methods that use a particular filter may be completely ignored if the >> arguments don't fulfil a certain predicate. You can read the paper >> "Filtered Dispatch" athttp://p-cos.net/documents/filtered-dispatch.pdf >> that I co-authored with Charlotte Herzeel, Jorge Vallejos and Theo >> D'Hondt for a more thorough introduction, background information and >> semantics, and which also includes an extensible metacircular Lisp >> interpreter as an example, based on implementing EVAL as a filtered >> function. >> >> Pascal > Any ideas where those would be useful? ContextL was interesting but I > never figured a natural problem where to use it. Good Q. A more telling question would be, what natural problem were you working on when you saw the need for this (and what alternative solutions were considered, and why did this seem best). I am a little suspicious of post hoc use cases to justify SPT (stupid pet tricks). Cells arose as a solution to automatic layout of GUI elements. The "layers" slot of Cello widgets arose as a way of customizing widget backgrounds/decoration without forever adding more and more slots to the base widget class (before taking over rendering almost entirely, and a complete takeover is a logical next step should I ever resume Cello development). I am guessing they were not working on factorials when they spotted the need for filtered dispatch. kt -- http://thelaughingstockatpngs.com/ http://www.facebook.com/pages/The-Laughingstock/115923141782?ref=nf
From: Pascal Costanza on 5 Dec 2009 11:48
Kenneth Tilton wrote: > > I am guessing they were not working on factorials when they spotted the > need for filtered dispatch. > Indeed. Pascal -- My website: http://p-cos.net Common Lisp Document Repository: http://cdr.eurolisp.org Closer to MOP & ContextL: http://common-lisp.net/project/closer/ |