Specify a type constraint - not a function

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

No, this is not possible.


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:

val apply : (int -> int) t -> int t -> int

so the API I was thinking about is:

you can create an atom with any type of state:

const priceAtom = atom(10)
const messageAtom = atom('hello')
const productAtom = atom({ id: 12, name: 'good stuff' })

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:

const readOnlyAtom = atom((get) => get(priceAtom) * 2)

The problem this creates is that in OCaml land (keeping types simpler than they need to be):

type 'a t_writable
external atom : 'a -> 'a t_writable = "atom"
type 'a t_readonly
type get
external atomFn : (get -> 'a) -> 'a t_readonly = "atom"

if you call:

let a = atom (fun _ -> 5) 

you get a readonly atom (because you passed a function), but the type states, it’s a mutable type, because atom returns t_writable.

Is it possible to put a type constraint for the type to be int | float | string | record | variant?

module Atom = struct
external int : int -> int t_writable = "atom"

an example in TypeScript

No, OCaml doesn’t support type overloading. In particular, types don’t exist at runtime.

Rather than trying to have one function with two different behaviours, you can write two functions:

val writable: 'a -> 'a writable
val readable: (get -> 'b) -> 'b readonly

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.