Fork me on GitHub
Prabhjot Singh05:08:01

# Suggestion to handle multi-arity functions When you create a function in javascript it gets a length property which is the number of arguments. For Example

function x(a,b,c){


console.log(x.length) // 3

function y(a, b, ...c) {

console.log(y.length) // 2
length does not include vararg. But that can be handled by adding a custom tag. We can use the above information to generate multi arity functions for calva
(defn foo
     ([a] "Arity 1")
     ([a b] "Arity 2")
     ([a b & args] "Arity 2 + Vararg"))

(println (foo 1))
(println (foo 1 2))
(println (foo 1 2 3))
Can be compiled to
export const foo = multi(
  function foo(a) {
    return "Arity 1";
  function foo(a, b) {
    return "Arity 2";
  add_vararg_tag(function foo(a, b, ...args) {
    return "Arity 2 + Vararg";

console.log(foo(1))                  // Arity 1
console.log(foo(1, 2))               // Arity 2
console.log(foo(1, 2, 3))            // Arity 2 + Vararg
Following is a how the multi and add_vararg_tag can look like
function add_vararg_tag(f) {
  f["vararg"] = true; // there are many ways to do this, JS Symbols could be used
  return f;

function multi(name, ...funcs) {
  const varargFunc = funcs.filter((f) => f["vararg"])[0];

  const m = {};
  for (let f of funcs) {
    if (f !== varargFunc) {
      m[f.length] = f;

  return function (...args) {
    var f = m[args.length];

    if (f) {
      return f(...args);

    if (varargFunc && args.length >= varargFunc.length) {
      return varargFunc(...args);

    throw `Arity ${args.length} is not supported by ${name}`;


@UGFGYK4GM Do you mean, instead of the current multi-arity implementation? What would be the benefit of the above vs the current implementation?


(The current implemenation comes from CLJS)


$ ./node_cli.js -e '(do (defn foo ([] :zero) ([_] :one)) (prn (foo))) (prn (foo 1))'

Prabhjot Singh06:08:14

the main advantage is that there is less code generated.

Prabhjot Singh06:08:37

so the output is smaller


Can you put the above in a Github Discussion? Bundle size is important, but so is performance, so that has to be measured as well

Prabhjot Singh06:08:51

I beleive there could be a runtime performance gains as well with the new approach but that may not be true.

Prabhjot Singh06:08:36

yes I agree, performance is important and I have not done any performance testing yet.


The output for multi-arity with fixed args is already not so big, the biggest win could be made with using the spread operator for varargs


which should probably be handled as a separate issue


there is a cherry issue here: but we have to have one for clava too

Prabhjot Singh06:08:08

ok I will look at that. Also, the other motivation was to make it easier to write muti-arity functions directly in javascript. For example for the core functions.


I would prefer not to use multi-arity for core functions, so we don't have to do extra work performance-wise

Prabhjot Singh07:08:51

Now I realise that the multi-arity problem is not as bad as I thought. As you explained it is only an issue with vararg. But I will create an issue in Github anyway for reference and further discussion.

Prabhjot Singh08:08:03

Ok now I am convinced that this is actually not a better idea. But I have submitted an issue to suggest some improvements to the current code gen.

Chris McCormick09:08:29

i'm excited to build some software using this at some point, thank you all for your work on it. 🙏

🎉 1
Prabhjot Singh10:08:07

@borkdude are there plans to support calling a data structure like a function. Like following ({:a :b} :a)


Only if this won't impact bundle size and performance negatively. Keyword as function is supported right now but only in direct call position

Prabhjot Singh10:08:26

clojureScript accomplish this by calling .call() on the datastructure. This works because all the js functions already have a method call on them. If we want to do the same in clava then we will need to add the call method on {} and [] etc. In the past people use to achieve this by changing the prototype but now a days it is not regarded as a good idea. It is not good for tree shaking and for many other reasons. Do you have any other ideas.


Yes, might not be worth it. Just use get


using symbols as protocols means that we can create a thin wrapper around JS' iterable protocol and start extending it to our own types using extend-type


// core.js
export const IIterable = Symbol('clava.core/IIterable');

export const IIterable_iterator = Symbol.iterator;

export const iterator = es6_iterator;
(deftype Foo []
  (iterator [_]
    (iterator [1 2 3])))

(map inc (->Foo)) ;; => [2 3 4]

👍 2

wouldn't this be a conflict/recursive call by accident?

(iterator [_]
    (iterator [1 2 3])))


rn, the extend-type of this expands to

(Foo["prototype"][IIterable] = true);
(Foo["prototype"][IIterable_iterator] = function () {
var self__ = this;return iterator([1, 2, 3]);


actually I'm not sure I understand your question


never mind, the misunderstanding was mine :)