OxCaml logo Jane Street logo

Implicit Kind Declarations

Type variable names can be declared to have implicit kinds. A type variable with a name that has an implicit kind will be instantiated with that kind. Here’s an example:

[@@@implicit_kind: ('elt : word)]

type 'elt collection

val singleton : 'elt -> 'elt collection
val lenght : 'elt collection -> int

This signature is equivalent to:

type ('elt : word) collection

val singleton : ('elt : word) . 'elt -> 'elt collection
val lenght : ('elt : word) . 'elt collection -> int

You can declare implicit kinds for multiple variable names at once:

[@@@implicit_kind: ('a : immediate) * ('b : immediate)]

val swap : 'a * 'b -> 'b * 'a

Implicit kinds can’t be overridden – a variable declared with an implicit kind must always have that kind. Attempts to narrow or change it will fail:

module type S = sig
  [@@@implicit_kind: ('a : value_or_null)]
  val i : ('a : value mod external_) -> 'a
  val j : ('a : bits64) -> 'a
end

[%%expect{|
Line 3, characters 10-36:
3 |   val i : ('a : value mod external_) -> 'a
              ^^^^^^^^^^^^^^^^^^^^^^^^^^
Error: The universal type variable 'a was declared to have kind value_or_null
       But it was inferred to have kind value mod external_
         because of the annotation on the type variable 'a.
|}]

Implicit kinds are inherited by signatures:

module type Outer = sig
  [@@@implicit_kind: ('t : bits64)]

  val outer : 't -> 't

  module Inner : sig
    (* Also [bits64] *)
    val inner : 't -> 't
  end
end

You can re-declare implicit kinds:

module type Outer = sig
  [@@@implicit_kind: ('t : bits64)]

  val outer : 't -> 't

  module Inner : sig
    [@@@implicit_kind: ('t : immediate)]

    val inner : 't -> 't
  end
end

Implicit kind declarations can be made in structures (or at the module toplevel):

module M = struct
  [@@@implicit_kind: ('elt : bits64)]

  let f : 'elt -> 'elt array = fun x -> [| x |]
end

Implicit kinds are lexical defaults, not a part of the module interface. They are syntactically limited to the signature or structure they are declared in and won’t be included:

module type S = sig
  [@@@implicit_kind: ('a : value_or_null) * ('b : immediate)]

  val fst : 'a * 'b -> 'a
end

module type T = sig
  include S

  (* ['a] and ['b] are defaulted to [value] here: *)
  val snd : 'a * 'b -> 'b
end

Implicit kinds affect constraints and are, for now, the only way to set constraints to certain kind values:

module Constrained : sig
  [@@@implicit_kind ('a : value_or_null) * ('b : value_or_null)]

  type 'c t constraint 'c = 'a * 'b
                       (* the only way to get [value_or_null] here *)
end