# Small exercise for understanding modules

As a small ReScript learning exercise I’m using modules to model abstract algebraic semigroups. So far I have:

``````// A module interface for abstract semigroups:
module type Semigroup = {
type t

let op: (t, t) => t
}

// module interfaces can't have default functions so the
// semigroup laws need to be in a separate module:
module SemigroupLaws = (S : Semigroup) => {

let associative = (x: S.t, y: S.t, z: S.t) =>
S.op(x, S.op(y, z)) == S.op(S.op(x, y), z)
}

// Integers form a semigroup under addition (ignoring overflow):
module IntSemigroup: Semigroup with type t = int = {
type t = int

let op = (x: int, y: int) => x + y
}

// Integer semigroup addition as expected:
Console.log(IntSemigroup.op(2, 3))
``````

But I think I’m missing or misunderstanding a technique to create an `int` instance of `SemigroupLaws`. Is this a use case for module functors?

``````module IntSemigroupLaws: SemigroupLaws(???)
``````

1 Like

You already created a module functor in your example, i.e. `SemigroupLaws`. So you just need to apply it to get the instance you want. Functor application looks exactly like function application:

``````module IntSemigroupLaws = SemigroupLaws(IntSemigroup)
``````

Btw, you can simplify a few things in your code thanks to ReScript type inference and operators being first-class values:

``````module SemigroupLaws = (S : Semigroup) => {
let associative = (x, y, z) =>
S.op(x, S.op(y, z)) == S.op(S.op(x, y), z)
}

// Integers form a semigroup under addition (ignoring overflow):
module IntSemigroup = {
type t = int
let op = \"+"
}
``````
3 Likes

That’s very neat. Thanks you for those insights!