Skip to content

RFC: Allow variadic functions #1072

Open
@hellerve

Description

@hellerve

Lasciate ogne speranza, voi ch'intrate. Or: this RFC is pretty big, sorry about that.

What?

in which I try to explain what I’d like.

Many programming languages have some version of variadic functions, or functions with multiple arities. Often these do not play well with typing, but that is not necessarily the case. I would like a simple version of variadic functions: Multiple arities, each with their own body. It could looke like this, if we’d adopt it for defn proper:

(defn add
  [a] (inc a)
  [a b] (+ a b)
  [a b c] (+ (+ a b) c))

If we’d like to add a special syntactic construct, I would propose defnmulti, because it walks and quaks kind of like a multimethod:

(defnmulti add
  [a] (inc a)
  [a b] (+ a b)
  [a b c] (+ (+ a b) c))

I would advise against a special symbol, however, since it would be entirely backwards compatible with defn—having a single argument count and body would just be a special case: a multimethod with one version. This would avoid duplicate implementations etc.

For simplicity reasons, I would not enforce any constraints for overlapping types of multifunctions (i.e. the a argument having to have the same type in every version above), but I think it would be best practice to have all of them have the same types if possible.

This PR explicitly excludes things like rest arguments, keyword arguments, or optional arguments. These are related, but an entirely different beast, especially implementation-wise.

Henceforth I shall call this construct multifunctions, since they’re functional multimethods, kindasorta.

Why?

in which I explain why I’d like it.

Multifunctions are useful for functions that make sense with multiple numbers of arguments—see add above. They are also useful in contexts where there are default values for some arguments:

(defn get-or
  [m] (get-or m (zero))
  [m default] (get m default))

We see patterns like these crop up in the Map module, for instance, which I am alluding to above—though the internals of Map are slightly different.

How?

in which I explain how to accomplish what I’d like.

Implementing multifunctions is a matter of refactoring the existing Defn object type to allow multiple argument counts and bodies. Since the argument count is always unique, both unifying and emitting against the correct binder should just be a matter of looking up how many arguments are passed at the call site.

While the implementation seems more or less straightforward, it will have to touch a lot of parts of the compiler, and it will be a fairly big programming effort. As of now, I do not foresee any roadblocks ahead, though.


Any questions, comments, and any sort of feedback is welcome! Thanks for staying with me this entire time!

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions