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.
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).
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.
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.
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.
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:
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With