Skip to content

Multi-Arity + vararg Improvements #114

@prabhjots

Description

@prabhjots

When skipping an arity

The following function does not have an arity 2

(defn foo 
  ([a] "1") 
  ([a b c] "3") 
  ([a b c & args] "3 and more"))

In Clojure

Clojure 1.11.1
user=> (defn foo ([a] "1") ([a b c] "3") ([a b c & args] "3 and more"))
#'user/foo
user=> (foo 1 2)
Execution error (ArityException) at user/eval137 (REPL:1).
Wrong number of args (2) passed to: user/foo

ClojureScript also behaves the same

In Clava

./node_cli.js -e '(do (defn foo ([a] "1") ([a b c] "3") ([a b c & args] "3 and more")) (prn (foo 1 2)))'
"3 and more"

An Error was expected but instead vararg variant is invoked.

We do get an error if vararg is not there.

Vararg performance

Consider the following example

(defn foo 
  ([a] "1") 
  ([a & args] "vararg"))

Following is part of the code which is generated

let f44 = function (var_args) {
    let G__4849 = arguments["length"];
    switch (G__4849) {
      case 1:
        return f44.cljs$core$IFn$_invoke$arity$1(arguments[0]);
        break;
      default:
        let args_arr4651 = [];
        let len__22086__auto__52 = arguments["length"];
        let i4753 = 0;
        while (true) {
          if (i4753 < len__22086__auto__52) {
            args_arr4651.push(arguments[i4753]);
            let G__54 = i4753 + 1;
            i4753 = G__54;
            continue;
          }
          break;
        }
        let argseq__22178__auto__55 =
          1 < args_arr4651["length"] ? args_arr4651.slice(1) : null;
        return f44.cljs$core$IFn$_invoke$arity$variadic(
          arguments[0],
          argseq__22178__auto__55
        );
    }
  };
  f44["cljs$core$IFn$_invoke$arity$1"] = function (a) {
    return "1";
  };
  f44["cljs$core$IFn$_invoke$arity$variadic"] = function (a, args) {
    return "vararg";
  };
  f44["cljs$lang$applyTo"] = function (seq56) {
    let G__5758 = first(seq56);
    let seq5659 = next(seq56);
    let self__22117__auto__60 = this;
    return self__22117__auto__60.cljs$core$IFn$_invoke$arity$variadic(
      G__5758,
      seq5659
    );
  };
  f44["cljs$lang$maxFixedArity"] = 1;

the problometic part is the default case in the switch statement.

Using the new es6 spread feature the variadic function could be rewritten as

  f44["cljs$core$IFn$_invoke$arity$variadic"] = function (a, ...args) {
    return "vararg";
  };

the diffrence is the ...before the args

then the default case in the switch could be rewriten as

let f44 = function (...var_args) {
    let G__4849 = var_args["length"];
    switch (G__4849) {
      case 1:
        return f44.cljs$core$IFn$_invoke$arity$1(var_args[0]);
        break;
      default:
        if(G__4849 > f44["cljs$lang$maxFixedArity"]) {
             return f44.cljs$core$IFn$_invoke$arity$variadic(...var_args)
        }
        throw `Wrong number of args (${G__4849}) passed to: user/foo`
    }
  };

also f44["cljs$lang$applyTo"] could be simplified in a similarly.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions