Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Type constructor aliases without "polluting" a module in OCaml

I'd like to define a type alias for constructors to be reused in a given module, without having to prefix them by the path to the module where they're defined. I'd also like to avoid "polluting" the latter module by doing an open/include that would import too many definitions.

Here's an example of what I'd like (which does not compile):

module A = struct
  type t = Red | Blue
end

module B = struct
  type t = A.t
  let f = function
    | Red -> 1  (*Error: Unbound constructor Red*)
    | Blue -> 0
  let
end

Doing include A or open A inside B would both work here (Red and Blue would be valid), but both will add definitions to B, "polluting" it.

open will do the least amount of damage, but still allows mistakes to be made, e.g. if A defines a symbol that is accidentally used in B due to the open, I won't have a compilation error.

Is there a way to avoid this "pollution" while preventing me from having to type A.Red/A.Blue in module B?

like image 626
anol Avatar asked Jul 03 '15 13:07

anol


3 Answers

Yes there is.

You can, when defining t in B, state that it is equal to A.t and rewrite the list of its constructors:

module B = struct
  type t = A.t = Red | Blue
  let f = function
    | Red -> 1
    | Blue -> 0
end

The typechecker will verify that the definition is the exact same as in A so even if you modify your type definition in A you'll be reminded to change it in B as well (yes, you type twice the same thing but it's a simple dumb copy/paste).

like image 50
PatJ Avatar answered Nov 18 '22 18:11

PatJ


With OCaml 4.02.x and later you can use ppx_import (https://github.com/whitequark/ppx_import).

a.ml

type t = A | B

b.ml

(* This gives a similar result to PatJ's answer *)
type a_t = [%import: A.t]
let foo = function
  | A -> 0
  | B -> 1

It's less explicit than PatJ's suggested approach. The ppx may be simpler if you're working with a large type definition or if A.t is changing frequently during development.

A downside of the ppx approach is that you can't [%import] types from within the same source file. The imported type needs to be defined in a separate compilation unit.

like image 21
hcarty Avatar answered Nov 18 '22 18:11

hcarty


The answer by @PatJ is a good one. Here is another approach that is sometimes useful: you can define three modules, one containing only your type, and then A and B. Open the first module in A and B.

like image 21
antron Avatar answered Nov 18 '22 19:11

antron