Any way to define a polymorphic record field via a function?

In the following example I would like to keep the type t abstract.

module Len : sig
  type t
  val v : ('a list -> int) -> t
end = struct
  type t = { len : 'a. 'a list -> int }
  let v len = { len }
end

But the compiler complains with

Error: This field value has type 'b list -> int which is less general than
         'a. 'a list -> int
2 Likes

I don’t know. The best I’ve ever come up with is this:

type length = { length : 'a. 'a list -> int }

module Len : sig
  type t
  val v : length -> t
end = struct
  type t = {
    len : 'a. 'a list -> int;
    etc : string;
  }
  let v {length} = {
    len = length;
    etc = "abc";
  }
end

So the user has to call v {length} instead of v length.

1 Like

It is not possible since let sum = List.fold_left (+) 0 is a perfectly valid argument for a function of type ('a list-> int) -> t.

There are few different encoding of ('a.'a list -> int) and you can convert back and forth between them:

type opaque_list = Opaque: 'any list -> opaque_list
let univ f = { length = (fun l -> f (Opaque l) ) }
let exist {length} (Opaque l) = length l

but if you don’t have the universal quantification in a function context, you cannot add it back afterward.

1 Like

The only thing I have to add here is that the annoying single-field records can be [@@unboxed], so at least you can avoid those costs.

Does this relate to this question?

How to write a function with polymorphic arguments?
In ML, an argument of a function cannot be polymorphic inside the body of the function; hence the following typing:

let f (g : 'a -> 'a) x y = g x, g y
val f : ('a -> 'a) -> 'a -> 'a -> 'a * 'a = <fun>

The function is not as polymorphic as we could have hoped.
Nevertheless, in OCaml it is possible to use first-order polymorphism. For this, you can use either records or objects; in the case of records, you need to declare the type before using it in the function.

let f (o : <g : 'a. 'a -> 'a>) x y = o#g x, o#g y
val f : < g : 'a. 'a -> 'a > -> 'b -> 'c -> 'b * 'c = <fun>
type id = { g : 'a. 'a -> 'a; }
type id = { g : 'a. 'a -> 'a; }
let f r x y = r.g x, r.g y
val f : id -> 'a -> 'b -> 'a * 'b = <fun>

Thanks all for your answers. I was wondering if I was missing a piece of syntax that would allow me to specify this at the signature level.

I fixed the problem with module on my side. You can have an example in encore.

module Len : sig
  type t
  module Make : functor (S: sig val length: 'a -> int end) -> sig val v: t end
end = struct
  type t = { length : 'a. 'a -> int }
  module Make (S: sig val length: 'a -> int end) = struct
    let v = { length = S.length }
  end
end

But I don’t really like this solution…