Arc Forumnew | comments | leaders | submitlogin
Overloading by Arity
4 points by fallintothis 4462 days ago | 2 comments
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.


3 points by Pauan 4462 days ago | link

"But dispatching on the number of parameters can't be that hard, right?"

Racket has this built-in (scroll down to "case-lambda"):

http://docs.racket-lang.org/reference/lambda.html?q=case-lam...)

It's not hard to write a macro in Arc/Nu that uses Racket's case-lambda, and in fact I had a semi-working version, but I gave that up when I started work on Nulan.

Unlike Arc, Nulan doesn't have optional args, so you have to use arity overloading:

  $def foo
    X Y -> X + Y       # 2 argument version
    X   -> (foo X 10)  # 1 argument version: default for Y is 10
Though I suppose it shouldn't be too hard to write a pattern matcher that supports optional args... something like this:

  $def foo
    X (opt Y 10) -> X + Y
In this case, "opt" is an object that has a %pattern-match type, which means users can write their own pattern matchers, unlike in Arc where it's all hardcoded.

-----

1 point by akkartik 4462 days ago | link

Yeah I've played with this idea too. Wart's def and Anarki's defgeneric support overloading by type, but the original common lisp version of wart also supported overloading by number of args:

http://github.com/akkartik/wart/blob/sbcl/021def.lisp

This is different from your idea and more like java. Or more like ruby, rather; functions are open definitions and you can add clauses to them whenever you want.

-----