Reputation:
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
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
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