Time for more musings and short experiments! Even if the ground's been covered before, I still learn some things from these. So I hope you do, too, dear reader. I was sitting in a waiting room mulling over my previous experiment at http://arclanguage.org/item?id=16725 and got to thinking about different strategies for dealing with complex argument lists. If there's one thing I like about Java (and it's a short list), it's that you can overload function definitions by arity, or number of arguments. Arc already has a limited way of doing this with optional parameters. E.g., (def foo (x (o y 1))
(+ x y))
takes either one or two arguments. This is limited because 1) optional arguments must come at the end of the parameter list (lest it be ambiguous) and 2) without going to great lengths, the code has to do the same general thing irrespective of a possibly-missing argument.But dispatching on the number of parameters can't be that hard, right? Right: (def check-complex (clauses)
(each (parms body) clauses
(unless (errsafe (all atom parms))
(err "Arity-overloaded definitions cannot use complex parameters:"
parms))))
(def check-arities (clauses)
(with (seen (table) prev-parms (table))
(each (parms body) clauses
(let arity (len parms)
(if (seen arity)
(err "Duplicate arities:" (prev-parms arity) parms)
(= (seen arity) t (prev-parms arity) parms))))))
(mac arity-def (name . clauses)
(let clauses (pair clauses)
(check-complex clauses)
(check-arities clauses)
(w/uniq fn-parms
`(def ,name ,fn-parms
(case (len ,fn-parms)
,@(mappend (fn ((parms body))
(list (len parms)
`(let ,parms ,fn-parms ,body)))
clauses)
(err "Wrong number of arguments to" ',name))))))
This gives more flexibility than optional parameters, because the way we bind arguments will be positional (so we can have "optional" parameters anywhere in the list): ; (def has ((o x) property value) ...) can't work
(arity-def has
(property value) [(testify value) (property _)]
(x property value) ((has property value) x))
arc> (has "abc" len even)
nil
arc> ((has len even) "abc")
nil
arc> ((has len even) "abcd")
t
and we can have completely different implementations for each parameter list: ; Wikipedia example (https://en.wikipedia.org/wiki/Function_overloading)
(arity-def volume
(s) (* s s s) ; Cube
(r h) (* 3.14 r r h) ; Cylinder
(l b h) (* l b h)) ; Cuboid
arc> (volume)
Error: "Wrong number of arguments to volume"
arc> (volume 1)
1
arc> (volume 1 2)
6.28
arc> (volume 1 2 3)
6
arc> (volume 1 2 3 4)
Error: "Wrong number of arguments to volume"
It can also help when currying is awkward: ; Example from http://arclanguage.org/item?id=16727
(arity-def less
(measure) (compare < measure)
(measure than) [< (meaure _) (measure than)]
(measure x y) (< (measure x) (measure y)))
And just generally gives us another tool in the box. |