Seunghyun Yoo

Posts | Development | About

[EN] Variadic Function in OCaml

After understanding the concept of function currying, some ideas arose: isn’t it possible to define a variadic function, which is a function of indefinite arity, in OCaml? In the C programming language, the most famous example might be the printf function. The following code is a concrete example of defining and using the variadic function.

#include <stdarg.h>
#include <stdio.h>

int
add_em_up (int count,...)
{
  va_list ap;
  int i, sum;

  va_start (ap, count);         /* Initialize the argument list. */

  sum = 0;
  for (i = 0; i < count; i++)
    sum += va_arg (ap, int);    /* Get the next argument value. */

  va_end (ap);                  /* Clean up. */
  return sum;
}

int
main (void)
{
  /* This call prints 16. */
  printf ("%d\n", add_em_up (3, 5, 5, 6));

  /* This call prints 55. */
  printf ("%d\n", add_em_up (10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10));

  return 0;
}

Reference: http://www.gnu.org/software/libc/manual/html_node/Variadic-Example.html

However, OCaml is a static/strongly typed programming language so every function should be defined clearly at the compile time. It means it doesn’t allow to have an indefinite number of arguments. For example, if we define a function add that takes two integers x, y, the type of the function add is determined as int -> int -> int = <fun>. Adding an unexpected argument z will cause an error because the interpreter can’t find the return type in the type chain int -> int -> int. Then can we make a variadic function in OCaml? The answer is, interestingly, YES when function currying is exploited.

The trick is to take a function object instead of integers as input. The function object should take the next function object as an input. To be specific, let’s define a function f1 as let f1 m f2 = f2 m. The additional input m is to say “I am calling the function f2 with the input m”. The evaluation of f1 m f2 f3 ... fn is reduced to f2 m f3 ... fn and finally we get fn m. Note that each m is different for each evaluation. Therefore, we may exploit this property to have n-1 arguments by defining f1 ... fn-1 appropriately and the last function fn returning the value m itself, that is let fn x = x. If we want to make an accumulator for an arbitrary number of integers, the function f1 can be defined as let f1 i m f2 = f2 (m+i) to take an integer. The function add must provide the initial value m by defining as let add f = f 0.

Here is the source code:

let add f = f 0;;
let arg i m f = f (m+i);;
let end_of_args x = x;;
let three = add (arg 1) (arg 2) end_of_args;;
let six = add (arg 1) (arg 2) (arg 3) end_of_args;;

Reference: https://blogs.janestreet.com/variable-argument-functions/