OCaml equivalent javascript 'apply'

Some time has passed since I encoded OCaml, and I ran into this problem, which sounds simple, but I have a mental block with a solution:

Write a function that takes 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 negation of f (i.e. (f x1 x2 .. xn) == not (g x1 x2 .. xn) for all valid parameter sets).

He was inspired by the following block of code that 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 see no way to implement this in OCaml (the keyword or arguments "arguments" are not available) due to the fact that the function f does not have a predefined number of arguments. I found links that talk about working with functions with a variable number of arguments (for example, https://blogs.janestreet.com/variable-argument-functions/ ), but I would like to know if there is a simpler / natural "solution this particular problem.

+5
source share
3 answers

I am a JavaScript programmer, and I have always argued that variational arguments are harmful . If we don’t have variational functions in JavaScript (just stay away from the arguments object), then every function in JavaScript typed in a system like Hindley Milner (minus API-specific functions like DOM functions) can easily be converted to an equivalent function at OCaml.

So what is the OCaml equivalent of the apply function? I believe this is a regular application:

 let apply fx = fx (* equivalent of apply in JavaScript *) 

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

 let sfgx = fx (gx) (* the S combinator from the SKI combinator calculus *) 

This function will be written in JavaScript as follows:

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

Note that each function definition and function call is explicitly written in curry.

This is the difference between JavaScript and OCaml:

  • In OCaml, all functions have a default value, and you must not explicitly perform them.
  • In JavaScript, all functions are not executed by default, and you must explicitly view them.

So, let's take a look at the irregular variations of the S-combinator. Firstly, OCaml:

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

Equivalent in JavaScript:

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

Note that the regular function application is the same for OCaml and JavaScript. For curry functions:

 let result = sfgx (* equivalent to `((sf) g) x` *) 

Equivalent in JavaScript:

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

For irregular functions:

 let result = s (f, g, x) 

Equivalent in JavaScript:

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

What about the apply function? How is this equivalent to the normal use of a function?

In OCaml you can do this:

 let args = (f, g, x) (* args is a tuple *) let result = s args (* normal function application *) 

Equivalent in JavaScript:

 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 universal. They can be used as lists or tuples, depending on the context.

The args parameter assigned by apply can be any object, like an array, and is treated as a single tuple argument. Each function in JavaScript can be considered as one function of arguments. Multiparameter functions in JavaScript can be considered as a one-parameter function of a tuple argument. The apply JavaScript function is just a special form of application with a normal function.

And what does it mean? Consider:

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

If we consider arguments an implicit parameter of an internal 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:

 let negate fx = not (fx) 

Now you can say that this will only work for single argument functions. This is not true. Typical negate signature:

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

Consequently, it can work for any type of 'a , including tuples. This is equivalent to JavaScript, in which multi-parameter functions are only one-parameter functions of tuple arguments.

Finally, the only real problem is converting curry functions to broken functions so you can negate them. Unfortunately, in OCaml there is no general way to decompose a function. So, you need a family of functions for uncurry curry functions of several arities:

 let uncurry2 f (x, y) = fxy let uncurry3 f (x, y, z) = fxyz . . . . 

After giving up functions, you can curry return them back. However, as with uncurry , there is no way for a universal curry function. Therefore, you will again need the curry family of functions:

 let curry2 fxy = f (x, y) let curry3 fxyz = f (x, y, z) . . . . 

The only way to create common curry or uncurry functions is to use dynamically typed languages ​​(like Lisp or JavaScript) or intrusive languages ​​(like Idris or Agda). The OCaml type system (Hindley Milner type system) is too restrictive to provide such functions.

+4
source

A function that takes multiple arguments in ocaml is actually a function that takes one argument and returns another function. This is curry.

What you want to do is possibly using irregular functions, i.e. functions that take only one argument (which can be a tuple):

For example, you want f : (a * b * c) -> bool instead of f : a -> b -> c -> bool . But you must manually transform your functions.

You can edit functions like let uncurry f (x,y) = fxy , but this only carries the problem because you have to do this for any number of arguments.

Maybe you can override functions that take a list of arguments as an argument. I mean, I don’t know the specifics of what you are trying to do.

+2
source

It is not difficult, but you need to write (and on the call sites, choose) an explicit definition for each of them manually, since OCaml lacks the necessary mechanism for abstracting from such definitions of different phenomena.

Note that this is a template that already exists in the OCaml code: see List.map(2) , List.iter(2) , etc.

Definitions may look like this:

 let negate f = (fun a -> not (fa)) let negate2 f = (fun ab -> not (fab)) let negate3 f = (fun abc -> not (fabc)) (* etc *) 

Note that type systems that allow this type of polymorphism are conceivable: in fact, Typed Racket can express this very definition.

0
source

All Articles