In JavaScript land, libraries often times overload functions with runtime checks depending on the passed value type.
For example create does one thing if 5 is passed or if a function is passed:
const create = (arg) => ...
const a = create(5)
const b = create(prev => prev + 1)
in OCaml the bindings would be 2 functions, calling the same JS function create, something like:
type t
external create : 'a -> t = "create"
external createFn : ('a -> 'a) -> t = "create"
Which allows you to write the equivalent
let a = create 5
let b = createFn (fun prev -> prev + 1)
The problem is, that 'a is not constrained in any way, so there’s nothing preventing you from calling create instead of createFn with a function, causing a runtime error:
let c = createFn (fun a -> "invalid " ^ string_of_int a) (* compile error - expected string, got int *)
let d = create (fun a -> "invalid " ^ string_of_int a) (* no error -> runtime exception *)
Is there any way to say: the accepted argument is anything except a function?
Something like (not real syntax): type 'a t = 'a constraint 'a <> 'b -> 'c
You might to expand by providing an example Javascript API that you would like to translate… because your question is generally not an issue in OCaml! However the function create should not be typed 'a -> t as it implies that the 'a argument could be anything, with no means for the function create to know what it is and hence do anything with this parameter (= it’s useless!)
In your examples, you show usages where the parameter is an integer (so perhaps this is the type that your create function should expect?)
val create : int -> t
val createFn : (int -> int) -> t
And boum, now it’s impossible to use the wrong create function!
Alternatively, when the parameter could indeed be “anything” at the time of creation, it’s much more common to have it show up in the type of the result:
type 'a t = ...
val create : 'a -> 'a t
This time around we don’t need two variations on create, since one can work for both. The type constraint that it should be a function or a value would show up later in other functions, when we actually need to know the concrete type of the 'a value to do stuff with it. For example:
and then the weirdness is that you can create computed values (which depending on the # of arguments become read-only, write-only or read-write), so passing a function, results in a read-only atom:
Note that the Javascript magic disallow the creation of a “function” atom, which functional programmers would find natural, so it’s technically a win in expressivity to differentiate the two behaviours:
let four = atom 4
let fn = atom (fun x -> x + 1)
let five = atomFn (fun get -> get fn (get four))
In any case, using the wrong atom/atomFn function would get caught as a type error by later usage of the constructed value.