lfex / los

LFE Object System
7 stars 1 forks source link

Add implementation for Clojure-like multi-methods #7

Open oubiwann opened 9 years ago

oubiwann commented 9 years ago

Usage should be something like this:

> (include-lib "los/include/multi-methods.lfe")
> (defmulti area 'type)
> (defmethod area 'rectangle
       ((`(#(length ,l) #(width ,w)))
         (* l w)))
> (area '(#(type triangle) #(base 2) #(height 4)))
  4.0

A non-macro version of this could be done in the following manner:

(defmodule polymorph
  (export all))

(defun area
  ((`(,type . ,rest))
   (dispatch 'area type rest)))

(defun dispatch
  ((fname `#(type ,type) args)
   (call (MODULE) (list_to_atom (++ (atom_to_list fname)
                                    "-"
                                    (atom_to_list type))) args)))

(defun area-triangle
  ((`(#(base ,b) #(height ,h)))
   (* b h (/ 1 2))))

(defun area-rectangle
  ((`(#(length ,l) #(width ,w)))
   (* l w)))

These could then be used in the following manner:

(c "examples/no-macros/polymorph.lfe")
#(module polymorph)
(slurp "examples/no-macros/polymorph.lfe")
#(module polymorph)
> (area '(#(type triangle) #(base 2) #(height 4)))
4.0
> (area '(#(type rectangle) #(length 2) #(width 4)))
8

Additional implementations could be added with ease:

(defun area-square
  ((`(#(side ,s)))
   (* s s)))

(defun area-circle
  ((`(#(radius ,r)))
   (* (math:pi) r r)))

Then use them:

> (area '(#(type square) #(side 2)))
  4
> (area '(#(type circle) #(radius 2)))
  12.566370614359172

See the following:

oubiwann commented 9 years ago

Made some more progress on this tonight (see changes 7ea02cc73c15c6525b9591cda94e9b1c56f5dfcd and d2182255b833d33aad18c2201b9a0ad18adeee1a). The following now works:

(defmulti area)
(defmulti perim)

(defmethod area 'triangle
  ((`#m(base ,b height ,h))
   (* b h (/ 1 2))))

(defmethod perim 'triangle
  ((`#m(base ,b height ,h))
   (+ b h (math:sqrt (+ (* b b)
                        (* h h))))))

(defmethod area 'circle
  ((`#m(diameter ,d))
    (area `#m(type circle radius ,(/ d 2))))
  ((`#m(radius ,r))
   (* (math:pi) r r)))

(defmethod perim 'circle
  ((`#m(diameter ,d))
    (perim `#m(type circle radius ,(/ d 2))))
  ((`#m(radius ,r))
    (* 2 (math:pi) r)))

With usage being simply this:

> (area #m(type triangle base 6 height 10))
30.0
> (perim #m(type triangle base 6 height 10))
27.6619037896906
> (area #m(type circle diameter 8))
50.26548245743669
> (perim #m(type circle diameter 8))
25.132741228718345

So far, the macros only support the rather trivial usage shown above, with the type hard-coded. The next steps are:

  1. Allow users to pass the attribute name in defmulti
  2. Support multiple attributes (for multiple dispatch)