Question

I'm using phantom types to emulate the state of a stack, as a wrapper module for ocaml-lua (Lua communicates with C/OCaml through a stack). Small code example:

type 's t
type empty
type 's table
type top

val newstate : unit -> empty t           (* stack empty *)
val getglobal : empty t -> top table t   (* stack: -1 => table *)

Certain stack operations are possible on both tables and array-tables (no real arrays in Lua); some aren't. So if I have types

type 's table
type 's array

I want a kind like table-or-array for functions that operate on both types, but still being able to forbid e.g. rawgeti (array operation) on tables. objlen is a stack operation that returns the "length" of the element at the top of the stack. This element can be both table or array-table. At the moment, the wrapper function is defined as follows:

val objlen : (top table) t -> int

What I want is

val objlen : (top table-or-array) t -> int

That is, array and table is subtypes of table-or-array.

Any ideas?

Regards Olle

Edit

After considerations, I came up with this:

module M : sig
  type ('s, 't) t

  (* New Lua state with empty stack *)
  val newstate : unit -> (unit, unit) t

  (* Get table *)
  val getglobal : ('a) t -> ([< `table | `string | `number | `fn], 'a) t

  (* Get array index and put "anything" on top of stack *)
  val rawgeti : ([`table], 'a) t -> ([< `table | `string | `number | `fn], [`table] * 'a) t

  (* String on top of stack *)
  val tostring : ([`string], _) t -> string

  (* Table or array-table on top of stack *)
  val objlen : ([`table], _) t -> int

  val pop : ('a, 'b * 'c) t -> ('b, 'c) t
end = struct
  type top
  type ('s, 't) t = string      (* Should really be Lua_api.Lua.state *)

  (* Dummy implementations *)
  let newstate () = "state"
  let gettable s = s
  let getarray s = s
  let rawgeti s = s
  let tostring s = "Hello phantom world!"
  let objlen s = 10
  let pop s = s
end

The stack at type level should now know neither more nor less than the stack itself. E.g. rawgeti will push on stack any type.

Était-ce utile?

La solution

What about the following structure?

type ('data, 'kind) t
type array_kind
type stack_kind

(* use tuples as type-level lists:
   (a * (b * (c * unit))) for the stack of type-shape [a;b;c] *)
val newstate : unit -> (unit, stack) t
val getglobal : (unit, stack) t -> (top * unit, stack) t

val objlen : (top * 'a, 'k) t -> int

This uses polymorphism (on 'k) to express that "any kind is fine". Using polymorphic variants, it is possible to use subtyping instead, but I would rather advise against it, because it is more complex and its interaction with GADTs (which you want to use internally to implement your phantom-types signature, or maybe directly expose GADTs) is more problematic.

PS: this is exactly how the standard Bigarray module uses "kind types" for a similar purpose.

Edit: the formulation above is a bit awkward as polymorphic variant also use polymorphism (subtyping being used in restricted specific cases), and it's possible to only use polymorphism in type-level variants. My remark about excessive complexity of this solution still stands.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top