module type Log = {
type t
let toString: t => string
}
let log = (content: 'a, logMod: module(Log with type t = 'a)) => {
module LogMod = unpack(logMod) // here is not allowed, how to fix it?
content->LogModule.toString->Js.log
}
log(
"a",
module(
{
type t = string
let toString = v => v
}
),
)
log(
1,
module(
{
type t = int
let toString = v => v->Belt.Int.toString
}
),
)
You need to introduce a locally abstract type, eg type a
. Basically it introduces a type constructor named a
that is abstract within its scope, and it gets replaced by a fresh type variable. You can then use it in places where type variables are not allowed. One example where this is not allowed is the one you found…in the first-class module with the abstract type.
You can use it like this (playground) :
// type a is locally abstract type
let log = (type a, content: a, log: module(Log with type t = a)) => {
module Log = unpack(log)
content->Log.toString->Js.log
}
And, btw, you can “unpack” the first-class module right in the function parameter to make it a bit more terse:
// Unpack in right in the function param...
let log' = (type a, content: a, module(Log: Log with type t = a)) => {
content->Log.toString->Js.log
}
More reading…
- Here is a good explanation about the difference between the type variable and the locally abstract types
- "generic" modules - #2 by yawaramin
- Rescript advance types - escaping scope - #3 by johnj
- How to use the first class module in rescript? - #9 by flo-pereira
2 Likes