Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

OCaml equivalent of javascript 'apply'

It's been a while since I've coded OCaml, and I came across this problem which sounds simple but I'm having a mental block with solving:

Write a function that takes in a function f with a variable number of arguments that returns a boolean (i.e. f is of type 'a -> 'b -> 'c -> ... -> bool) and returns a function g that represents the negation of f (i.e. (f x1 x2 .. xn) == not (g x1 x2 .. xn) for all valid parameter sets).

It was inspired by the following code block which solves the problem in Javascript:

function negate(func) {
  return function() {
    return !func.apply(null, arguments);
  };
}

(from http://eloquentjavascript.net/1st_edition/chapter6.html)

However, I don't see a way to implement this in OCaml (the "arguments" keyword or equivalent is not available) because of the fact that the function f has no preset number of arguments. I have found links that talk about dealing with functions with variable numbers of arguments (such as https://blogs.janestreet.com/variable-argument-functions/) but I would like to know if there is a simpler / more 'natural' way to deal with this specific problem.

like image 915
user2258552 Avatar asked Jun 22 '15 21:06

user2258552


People also ask

What is the syntax of OCaml function application?

Since OCaml is a functional language, and applying functions is the most important thing you do as an OCaml programmer, the syntax of function application is designed to be as simple as it can possibly be: it is simple prefix juxtaposition. No parens are needed (other than for the usual purpose of disambiguation and changing precedence).

How to translate a ReasonML file to OCaml?

If in the future you find some ReasonML code that you wish to translate to OCaml, you can use the bsrefmt program that is included with the installation of bs-platform. To translate a ReasonML file, do bsrefmt test.re --print=ml, and the OCaml equivalent will be printed to stdout.

What is the difference between JavaScript call and apply function?

JavaScript call function is derived from borrowing and invoking functions. JavaScript Apply function takes the arguments as an Array. JavaScript Call function takes the arguments separately. In JavaScript Apply, elements can be further added to another array. In the Call function, we have to add an element to the list only.

How to use infix operators as prefixes in OCaml?

First note that (most) any infix operator in OCaml can also be used as a prefix operator by surrounding the operator with parens and putting it in a prefix position, like so: 2 + 3 = (+) 2 3.


1 Answers

I am a JavaScript programmer and I have always maintained that variadic arguments are harmful. If we don't have variadic functions in JavaScript (just stay away from the arguments object), then every function in JavaScript typable in the Hindley Milner type system (minus API specific functions, like DOM functions) can be easily converted into an equivalent function in OCaml.

So what's the OCaml equivalent of the apply function? I believe that it's normal function application:

let apply f x = f x (* equivalent of apply in JavaScript *)

How is normal function application equivalent to the apply function in JavaScript? Consider:

let s f g x = f x (g x) (* the S combinator from the SKI combinator calculus *)

This function would be written in JavaScript as follows:

var s = function (f) {
    return function (g) {
        return function (x) {
            return f(x)(g(x));
        };
    };
};

Note that every function definition and function call is explicitly written in curried form.

This is the difference between JavaScript and OCaml:

  1. In OCaml all the functions are curried by default and you have to be explicitly uncurry them.
  2. In JavaScript all the functions are uncurried by default and you have to be explicitly curry them.

So, let's take a look at the uncurried variants of the S combinator. First, OCaml:

let s (f, g, x) = f (x, g (x)) (* sml convention is to use uncurried functions *)

The equivalent in JavaScript:

var s = function (f, g, x) {
    return f(x, g(x));
};

Note that normal function application is the same in both OCaml and JavaScript. For curried functions:

let result = s f g x (* equivalent to `((s f) g) x` *)

The equivalent in JavaScript:

var result = s(f)(g)(x);

For uncurried functions:

let result = s (f, g, x)

The equivalent in JavaScript:

var result = s(f, g, x);

So what about the apply function? How is that equivalent to normal function application?

In OCaml, you can do this:

let args   = (f, g, x) (* args is a tuple *)

let result = s args    (* normal function application *)

The equivalent in JavaScript is:

var args   = [f, g, x];           // args is an array

var result = s.apply(null, args); // normal function application

As you can see tuples in OCaml are equivalent to arrays in JavaScript. Arrays in JavaScript are versatile. They can be used as either lists or tuples, depending upon the context.

The args parameter given to apply can be any array-like object and it's treated as a single tuple argument. Every function in JavaScript can be thought of as a single argument function. Multi-parameter functions in JavaScript can be thought of as a single-parameter tuple argument function. The apply function of JavaScript is just a special form of normal function application.

So what does this imply? Consider:

var negate = function (f) {
    return function () {
        return !f.apply(null, arguments);
    };
};

If we consider arguments to be an implicit parameter of the inner function then the equivalent of the above function in OCaml is:

let negate f = fun arguments -> not (f arguments) (* arguments is explicit *)

This can be simplified to:

let negate f x = not (f x)

Now, you might say that this will only work for single argument functions. That's not the case. The type signature of negate is:

val negate : ('a -> bool) -> 'a -> bool

Hence, it can work for any type 'a including tuples. This is equivalent to JavaScript in which multi-parameter functions are just single-parameter tuple argument functions.

Finally, the only real problem is converting curried functions into uncurried functions so that you can negate them. Unfortunately, there's no generic way of uncurrying a function in OCaml. So you need a family of functions to uncurry curried functions of several arities:

let uncurry2 f (x, y) = f x y

let uncurry3 f (x, y, z) = f x y z

           .
           .
           .
           .

After negating the functions you can curry them back. However, just as with uncurry there's no way to generically curry a function. Hence, you again need a family of curry functions:

let curry2 f x y   = f (x, y)

let curry3 f x y z = f (x, y, z)

         .
         .
         .
         .

The only way to create generic curry or uncurry functions is to use dynamically typed languages (like Lisp or JavaScript) or dependently typed languages (like Idris or Agda). The type system of OCaml (the Hindley Milner type system) is too restrictive to allow such functions.

like image 109
Aadit M Shah Avatar answered Sep 16 '22 13:09

Aadit M Shah