package bap-future

  1. Overview
  2. Docs

Variadic arguments.

Variadic, abstracts a common idiom of a function applied to a variable number of arguments. A common examples of such function would be OCaml's standard printf and scanf functions. A more general examples, are monadic parsers, such as MParser, command line parsers such as Cmdliner and Core's Command. They all are using the same trick to collect arguments of different type and pass it to a function, s.t. the function type actually defines the type of collected arguments. Both Cmdliner and Command relies on Applicative functor defined in 1. However, it requires a return function, that is not possible to provide in general for co-inductive types, that can only be observed or mapped. It is still possible to implement a variadic interface using the restricted Applicable interface given only one restriction: it is not possible to create an empty variadic list of arguments (that can be considered as a benefit).

Here are some examples, that highlights common use cases of the variadic structure. Suppose, we have several future values of different types, that we would like to merge with some function f, to be concrete let's assume, that we're waiting for:

arch : arch future - program architecture to be defined; lang : lang future - a programming language; abi : abi future - an ABI; api : api future - an api specification

And we have a function typecheck of type

arch -> lang -> abi -> api -> pass future

,

that will typecheck a binary program, according to the typing rules of the specified programming language, binary interface, and type environment api. Given the Variadic interface we can write it as

Future.Variadic.(apply (args arch $lang $abi $api) ~f:typecheck

Note, since future implements a more powerful Monad interface, it is still possible to apply a typecheck function without using the variadic interface, e.g.,

arch >>= fun arch ->
lang >>= fun lang ->
abi >>= fun abi ->
api >>= fun api ->
typecheck arch lang abi api

However, this is less general, as it specifies a concrete order of argument bindings, it is also requires a much less general monad interface with bind and return operations, that are in general not available for coinductive types, for example for stream type. If we substitute future type constructor in the above example with a stream constructor we will no be able to implement the latter solution, as we lack the monad interface.

Using a stream for this particular example, makes sense, since, the specified properties, can be defined on a module level, so they can be defined multiple times for each project. In that case function typecheck will be applied for each quartet of the arguments.

When used with collections, such as list, sequences, sets, etc, the pattern can be used to generalize cartesian product from a function taking a pair of arguments, to a function taking arbitrary amount of arguments.

For collection, the Variadic can be used to generalize cartesian product to N arguments:

module AList = struct
  include List
  let apply fs xs =
    cartesian_product fs xs >>| fun (f,x) -> f x
end
module Varags = Variadic.Make(AList)

let cartesian_product = Varags.apply

For option and error monad, with the following definition of apply,

let apply f x = match f,x with
  | Some f, Some x -> Some (f x)
  | None -> None

The produced Varargs.apply will be a generalization of Option.merge, i.e., it will apply function f to N arguments of different types, if all of them are not zero (i.e., None, Error.

module type S = sig ... end

Variadic argument list.

module Make (T : Applicable.S) : S with type 'a arg = 'a T.t
include S with type 'a arg = 'a
type ('f, 'r) t

('f,'r) t is a list of arguments, where 'f defines the arrow type of the arguments, and 'r is the return type. C.f., 'f and 'r with the first and last parameter of the format type constructor.

type 'a arg = 'a

'a arg is an Applicable value

val args : 'a arg -> ('a -> 'b, 'b) t

args x creates a singleton list of arguments that can be applied to a function that takes x argument, and returns a value of type 'b.

val ($) : ('a, 'b -> 'c) t -> 'b arg -> ('a, 'c) t

args $x appends argument x to a list of arguments args.

val apply : f:'f -> ('f, 'r) t -> 'r arg

apply args ~f applies function f to arguments args.