1

Say I want to have polymorphic functions, here in a module.

  module type NewHType1 = sig
    type ('a, 'n) t
    type t'
    type ('n, 't) inj = { inj : 'a. ('a, 'n) t -> ('a, ('n, t') happ) app
    type ('n, 't) prj = { prj : 'a. ('a, ('n, t') happ) app -> ('a, 'n) t }

    val inj : ('n, 't) inj
    val prj : ('n, 't) prj
  end

Do I syntactically really have to have a polymorphic record type, with a superfluous record field ?

(I vaguely remember someone mentioning objects to avoid this extra noise (?))

nicolas
  • 9,549
  • 3
  • 39
  • 83
  • 1
    The problem solved by using records is, as far as I understand, the undecidability of type inference for higher rank polymorphism. This is described in the OCaml chapter on [Polymorphism and its limitations](https://ocaml.org/manual/polymorphism.html#s:higher-rank-poly), which shows how to work around the limitation using both records and objects. I don't see using an object being any less verbose or noisy though, quite the opposite. – glennsl Jan 13 '22 at 15:27

1 Answers1

1

You can encode existentials natively using GADT. For example, this is how you can encode higher-kinded polymorphism with GADT,

type ('p, 'f) app = ..

module Newtype1 (T : sig type 'a t end) () : sig
  type 'a s = 'a T.t
  type t

  val inj : 'a s -> ('a, t) app
  val prj : ('a, t) app -> 'a s
end = struct
  type 'a s = 'a T.t
  type t
  type (_,_) app += App : 'a s -> ('a, t) app
  let inj v = App v
  let prj (App v) = v
end

module Newtype2 (T : sig type ('a,'b) t end) () : sig
  type ('a, 'b) s = ('a, 'b) T.t
  type t

  val inj : ('a, 'b) s -> ('a, ('b, t) app) app
  val prj : ('a, ('b, t) app) app -> ('a, 'b) s
end = struct
  type ('a,'b) s = ('a,'b) T.t
  type t
  type (_,_) app += App : ('a,'b) s -> ('a, ('b,t) app) app
  let inj v = App v
  let prj (App v) = v
end

As a side note, you don't need to use records or anything else in the signature to specify that the type variable is polymorphic, as it is already polymorphic. I.e., you can describe your signature as simple as,

module type NewHType1 = sig
  type ('a, 'n) t
  type t'
  val inj : ('a, 'n) t -> ('a, ('n, t') happ) app
  val prj : ('a, ('n, t') happ) app -> ('a, 'n) t
end

It is because in value specifications (in module types) polymorphic variables denote polymorphic types, which is different from type constraints, which are used in value definitions, where type variables denote just variables that can have ground types, so if you want to prevent its unification with the ground type, you have to add a type annotation, e.g.,

module NewHType1 : sig
  type ('a, 'n) t
  type t'
  val inj : ('a, 'n) t -> ('a, ('n, t') happ) app
  val prj : ('a, ('n, t') happ) app -> ('a, 'n) t
end = struct
  type ('a,'n) t and t'
  let inj : 'a. ('a, 'n) t -> ('a, ('n, t') happ) app = fun _ -> assert false
  let prj : 'a. ('a, ('n, t') happ) app -> ('a, 'n) t = fun _ -> assert false
end

To summarize, 'a. 'a -> 'a type constraint generates the polymoprhic 'a -> 'a type. You might find the following answer useful as well.

ivg
  • 34,431
  • 2
  • 35
  • 63
  • yes, I am looking as to how get true higher kind. this, as I understand, only provide first order. but really here I am very modestly looking to just reduce the noise of polymorphism, as it gets hairy. – nicolas Jan 13 '22 at 15:39
  • in second order, we would have `(* -> *) -> (* -> *)`, not `* -> (* -> *)` and the like – nicolas Jan 13 '22 at 15:40
  • it's a very petty as a question, really. I can wrap and unwrap as well. and it's only in the library. – nicolas Jan 13 '22 at 15:43
  • 1
    Sure, but I still can't see how GADT is not solving the problem. Have you read https://ocamllabs.io/higher/lightweight-higher-kinded-polymorphism.pdf? Concerning objects, don't look in this direction, it is the same as with records but with an even more syntactic burden. – ivg Jan 13 '22 at 15:47
  • I read it. they quantify on `'t` where `'t : * -> *` (and more generally `* -> (* -> .. ->(* -> *)))` ). here i want to quantify on `'t : (*->*) -> (* ->*)`. for instance "twice" which takes any type constructor and applies it twice. lets call that a type constructor transformer. i then can quantify over "type constructor transformers" `'t` with some property, say those which transport a "mappable" type constructor `'n` to another mappable type constructor `'n 't`. I think I would need that for nested types. so i would need another kind of `app` for this level, `happ` (i think) – nicolas Jan 13 '22 at 15:59
  • 1
    I think you don't that's why I pointed you to the paper and the implementation. Though I might be wrong, of course. But anyway this is out-of-the-scope of the question. I extended the answer, with extra notes about where you need polymorphic type annotations and where you don't. Basically, in the signature, you don't need to invoke records to specify that the function is polymorphic as `'a` already means a universally quantified polymorphic variable. – ivg Jan 13 '22 at 16:04
  • yes, I saw. very enlightning thank you. – nicolas Jan 13 '22 at 16:05
  • "in value specifications (in module types) polymorphic variables denote polymorphic types, which is different from type constraints" that's a good thing to know. in haskell there is only the value signature, so that's easy to be confused here. makes perfect sense to have them poly in value specifications. – nicolas Jan 13 '22 at 16:15
  • they are polymorphic, but we still carry the type `'a` around.. – nicolas Jan 13 '22 at 16:17
  • 1
    yes, I was also very confused with `let succ : 'a -> 'a -> fun x -> x + 1`, when moved from Haskell and SML to OCaml) My suggestion is to think of them as type constraints, not type specifications. You can only specify type via the signature and nowhere else. Also, my suggestion is to use module types wherever it is possible (and it is possible everywhere). – ivg Jan 13 '22 at 16:20