I use type identifiers in a pretty standard way, i.e.
type _ id = ..
module type S = sig
type t
type _ id += K : t id
end
type 'a t = (module S with type t = 'a)
let make (type a) () = (module struct
type t = a
type _ id += K : t id
end : S with type t = a)
However, these aren’t polymorphic when used with phantom types:
# type 'a ex = Int : int ex ;;
type 'a ex = Int : int ex
# let ex_id : 'a ex t = make () ;;
val ex_id : '_a ex t = <module>
ex_id
is weakly polymorphic, and gets concretized when used.
I tried using explicit polymorphic notation with abstract types, but that also (predictably) fails with the following error:
# let ex_id : type a. a ex t = make () ;;
Line 1, characters 4-36:
Error: This definition has type 'a ex t which is less general than 'a0. 'a0 ex t
Is it possible to construct an identifier for 'a t
for all 'a
in this instance?
You can only resort to
let ex_id () : _ ex t = male ()
since with a GADT you cannot rely on the relaxed value restriction.
Bummer. That’s not an ideal solution, since every call to ex_id
creates a new K
and thus a new identifier, making tasks like constructing equality witnesses impossible.
Thanks again, @octachron
If it were possible to construct ex_id : type a. a ex t
, then the following function would type-check:
let cast (type a b) (x : a) : b =
let module A = (val (ex_id : a ex t)) in
let module B = (val (ex_id : b ex t)) in
match A.K with
| B.K -> x
| _ -> assert false
I don’t know what would be a type-safe form for identifiers of type families.
(EDIT: I first wrote cast : 'a 'b. 'a ex -> 'b ex
, but it is even more convincing with cast : 'a 'b. 'a -> 'b
)
Yeah, this is why I initially assumed it wasn’t possible, as proving equality between two instances of 'a ex
was my initial intent. The way I kind of solved this was by wrapping ex
in an existential (sorry, in hindsight maybe ex
was a bad choice) and using some other ID’s.
type exist = E : 'a ex -> exist
let exist_id : exist t = make ()
let int_id : int t = make ()
let inner_id : type a. a ex -> a t = function
| Int -> int_id
Using these you can pack ex
inside the existential and use the inner ID’s to recover the inner type.