user1971598
user1971598

Reputation:

Return different first class modules in OCaml

I want to return different modules from a function. This is the general idea of what I wish would work.

module type First = sig
  val speak : unit -> unit
end
module First = struct
  let speak () = print_endline "hi"
end

module type Second = sig
  val another_speaker : unit -> unit
end
module Second = struct
  let another_speaker () = print_endline "another hi"
end

type mods = First | Second


let load = function
  | First -> (module First : First)
  | Second -> (module Second : Second)

1) But it doesn't because load's return type gets determined on the first match. How can I make this general idea work?

2) How can I write something like this and have it work.

type a = [`First of (module type First )]

Upvotes: 0

Views: 172

Answers (2)

Jeffrey Scofield
Jeffrey Scofield

Reputation: 66823

You can't treat the two module types as the same type, because they're different. But you can combine them into a variant type.

This works for me:

module type First = sig
  val speak : unit -> unit
end

module type Second = sig
  val another_speaker : unit -> unit
end

type modvar = MFirst of (module First) | MSecond of (module Second)

type mods = First | Second

module First = struct
  let speak () = print_endline "hi"
end

module Second = struct
  let another_speaker () = print_endline "another hi"
end

let load = function
| First -> MFirst (module First)
| Second -> MSecond (module Second)

I'm not sure if this is what you're looking for.

Update

Here's the same code rewritten to use polymorphic variants:

module type First = sig
  val speak : unit -> unit
end

module type Second = sig
  val another_speaker : unit -> unit
end

type mods = First | Second

module First = struct
  let speak () = print_endline "hi"
end

module Second = struct
  let another_speaker () = print_endline "another hi"
end

let load = function
| First -> `MFirst (module First: First)
| Second ->`MSecond (module Second: Second)

The key is that you need to tag the values returned by load. Otherwise they have to be the same type, which they aren't.

Upvotes: 3

Drup
Drup

Reputation: 3739

The "good" solution is to use the same module type for everyone:

module type S = sig
  val speak : unit -> unit
end

module First = struct
  let speak () = print_endline "hi"
end
module Second = struct
  let speak () = print_endline "another hi"
end

type mods = First | Second

let load = function
  | First -> (module First : S)
  | Second -> (module Second : S)

If you really can't, apart from the variant solution given by Jeffrey, there is the GADT solution:

type 'a t = First : (module First) t | Second : (module Second) t

let load : type a . a t -> a = function
  | First -> (module First : First)
  | Second -> (module Second : Second)

Upvotes: 3

Related Questions