Difficulties with functors and abstract types

I think my problem is that having a module type intended for use by a Module Function, when having an abstract type, the Module Function only sees the abstract type as abstract (Ok that probably didn’t make any sense), a better explanation is in the comments

module type FunctorType = {
  type a
  // "type a = string" works but I want to set the type in MyMod
  let injectedRun: a => unit
}

module Functor = (R: FunctorType) => {
  let runInjectedRun = x => R.injectedRun(x)
}

module MyMod: FunctorType = {
  type a = string // this is where I want to set the type
  let injectedRun = (x: a) => Console.log(x)
}

module NewMod = Functor(MyMod)
NewMod.runInjectedRun("hello") // This has type: string But this function argument is expecting: MyMod.a

I think this is just simply this problem below. I’m probably wrongly thinking of MyModType as kind of a base module that has abstract members that need to be overwritten, but I guess that’s not how we do things around here.

module type MyModType = {
  type a
}

module MyMod: MyModType = {
  type a = int
}

let x: MyMod.a = 1 // This has type: int But it's expected to have type: MyMod.a
1 Like

Just remove the FunctorType type annotation on MyMod

module MyMod = {
  type a = string // this is where I want to set the type
  let injectedRun = (x: a) => Console.log(x)
}

Modules are structurally typed. Using a module type annotation can therefore only remove information. You do not need to (and cannot) specify that MyMod implements or conforms with FunctorType.

4 Likes

It is possible to get this to work for concrete types while retaining the module type signature. It relies on explicitly setting the type of FunctorType.a in MyMod’s type signature.

You would need to create a differently named version of MyMod separately for each type you want to support.

module type FunctorType = {
  type a
  let injectedRun: a => unit
}

module Functor = (R: FunctorType) => {
  let runInjectedRun = x => R.injectedRun(x)
}

// This is where you set an explicit type
// Specifying the a in FunctorType helps the compiler match things up
module MyMod: FunctorType with type a = string = {
  type a = string
  let injectedRun = (x: a) => Console.log(x)
}

module NewMod = Functor(MyMod)
NewMod.runInjectedRun("hello")

Playground Link

1 Like

@glennsl has it right, you just need to remove the type annotations on the modules.

module type FunctorType = {
  type a
  // "type a = string" works but I want to set the type in MyMod
  let injectedRun: a => unit
}

module Functor = (R: FunctorType) => {
  let runInjectedRun = x => R.injectedRun(x)
}

module MyMod = {
  type a = string // this is where I want to set the type
  let injectedRun = (x: a) => Console.log(x)
}

module MyModInt = {
  type a = int // this is where I want to set the type

  let injectedRun = (x: a) => Console.log(x)
}

module NewMod = Functor(MyMod)
module NewModInt = Functor(MyModInt)
NewMod.runInjectedRun("hello")
NewModInt.runInjectedRun(42)

Playground link: ReScript Playground

3 Likes