-1

I'd like to define structure like "Monoid". (This is a word which appears in Group-Theory.)

Here's one example of Monoid-Structure.

Example(1):

type monoid_sum struct{
    val int
}

func op(x,y monoid_sum) monoid_sum {
    return monoid_sum{x.val + y.val}
}

func ide() monoid_sum{
    return monoid_sum{0}
}

Example(2):

import "math"

func max(a,b int) int{
    if a > b{
            return a
    }else{
            return b
    }
}

type monoid_max struct {
    val int
}

func op(x,y monoid_max) monoid_max {
    return monoid_max{max(x.val,y.val)}
}

func ide() monoid_max {
    return monoid_max{math.MinInt}
}

Is there any good way to define a monoid interface? I want to make an interface like this:

type monoid interface{
    op func(monoid) monoid // mapping_function(monoid,monoid) -> monoid(binary operations in monoid)
    ide() monoid          // identity_function() -> monoid (return Identity element)

}

though the codes does not work(just Pseudo code)

JFMR
  • 23,265
  • 4
  • 52
  • 76
sgsw
  • 23
  • 3
  • I want to make interface like: type monoid interface{ op func(monoid) monoid // mapping_function(monoid,monoid) -> monoid(binary operations in monoid) ide () monoid // identity_function() -> monoid (return Identity element) } though the codes does not work(this is just Pseudo code) – sgsw Aug 12 '21 at 03:04
  • in this case define methods (op,ide) with receivers, and change `return` type and `arg` type to `monoid`. – Oleg Butuzov Aug 12 '21 at 03:12
  • 3
    `type monoid interface { Op(monoid) monoid; ide() monoid }` – Burak Serdar Aug 12 '21 at 03:16
  • Not sure I would encourage trying to implement such algebraic structures in Go, but this is relevant: https://stackoverflow.com/questions/64468241/recursive-type-constraint-using-a-defined-type-rather-than-a-type-literal – jub0bs Apr 30 '23 at 16:21

3 Answers3

1

You could define the Monoid interface as the following generic interface:

type Monoid[T any] interface {
    Identity() T
    Combine(T) T
}

Combine() corresponds to the monoid's (associative) binary operation, and Identity() returns the monoid's identity element. You called them op() and ide(), respectively.

CombineMonoids is a generic convenience function for combining an arbitrary number of monoids into a single monoid:

func CombineMonoids[M Monoid[M]](ms ...M) M {
    var res M
    res = res.Identity()
    for _, m := range ms {
        res = res.Combine(m)
    }
    return res
}

The constraint Monoid[M] on the type parameter M – i.e., M Monoid[M] – means that it works with any type M that has the methods Identity() M and Combine(M) M, i.e., it satisfies Monoid[M].

Examples

For example, here are the types SumMonoid and MaxMonoid that correspond to your monoid_sum and monoid_max, respectively.

SumMonoid

type SumMonoid int

func (s SumMonoid) Identity() SumMonoid {
    return 0
}

func (s SumMonoid) Combine(r SumMonoid) SumMonoid {
    return s + r
}

The type SumMonoid satisfies the interface Monoid[SumMonoid].

func TestSumMonoid(t *testing.T) {
    a := SumMonoid(3)
    b := SumMonoid(7)
    c := SumMonoid(10)
    want := SumMonoid(20)
    got := CombineMonoids(a, b, c)
    if got != want {
        t.Fail()
    }
}

MaxMonoid

type MaxMonoid int

func (m MaxMonoid) Identity() MaxMonoid {
    return math.MinInt
}

func (m MaxMonoid) Combine(n MaxMonoid) MaxMonoid {
    if m > n {
        return m
    } else {
        return n
    }
}

The type MaxMonoid satisfies the interface Monoid[MaxMonoid].

func TestMaxMonoid(t *testing.T) {
    a := MaxMonoid(-100)
    b := MaxMonoid(500)
    c := MaxMonoid(100)
    want := MaxMonoid(500)
    got := CombineMonoids(a, b, c)
    if got != want {
        t.Fail()
    }
}

What about a non-generic Monoid interface?

Similarly to what you suggested, you could, in principle, define the Monoid as a non-generic interface:

type Monoid interface {
    Identity() Monoid
    Combine(Monoid) Monoid
}

The problem is that the original type of the particular monoid (e.g., SumMonoid or MaxMonoid) will be erased to just the interface Monoid.

Combining a SumMonoid with a MaxMonoid makes no sense – you would place type assertions inside of the Combine method implementation that will lead to a panic if the dynamic type of the two monoids to be combined differ. So, the solution based on the generic interface seems to be more robust.

JFMR
  • 23,265
  • 4
  • 52
  • 76
0

You'll have to follow the interface definitions as they are defined for struct to implement the interface. When interface defines return type as monoid for your op method, you'll have to return monoid in when implementing that interface. See if following helps:

type monoid interface{
    get() int
    op(monoid, monoid) monoid   // mapping_function(monoid,monoid) -> monoid(binary operations in monoid)
    ide() monoid                // identity_function() -> monoid (return Identity element)
}

func max(a,b int) int{
    if a > b{
        return a
    }else{
        return b
    }
}

type monoid2 struct{
    val int
}

func (m monoid2) get() int {
    return m.val
}

func (monoid2) op(x,y monoid) monoid{
    return monoid2{max(x.get(),y.get())}
}

func (monoid2) ide() monoid{
    return monoid2{-math.MaxInt8}
}

advay rajhansa
  • 1,129
  • 9
  • 17
0

Thank you for everyone's help and kindness!

I successfully defined monoid structure.

package main

import (
    "fmt"
)

type monoid interface {
    get() interface{}         // type of interface{} depends on each monoid type.
    op(monoid, monoid) monoid // mapping_function(monoid,monoid) -> monoid(binary operations in monoid)
    ide() monoid              // identity_function() -> monoid (return Identity element)
}

type monoid1 struct {
    val int
    sum int
}

type monoid2 struct {
    name  string
    power int
}

func (m monoid2) get() interface{} {
    return m
}

func (m monoid2) op(x, y monoid) monoid {
    a := x.get().(monoid2)
    b := y.get().(monoid2)
    if len(a.name) > len(b.name) {
        return monoid2{a.name, a.power + b.power}
    } else {
        return monoid2{b.name, a.power + b.power}
    }
}

func (m monoid2) ide() monoid {
    return monoid2{"", 0}
}

func (m monoid1) get() interface{} {
    return m
}
func (m monoid1) op(x, y monoid) monoid {
    a := x.get().(monoid1)
    b := y.get().(monoid1)
    return monoid1{a.val + b.val, a.sum + b.sum}
}

func (m monoid1) ide() monoid {
    return monoid1{0, 0}
}

func main() {
    a := []monoid{monoid2{"Jame", 100}, monoid2{"Tom", 1010}, monoid2{"BOB SMITH", 1111}, monoid1{1, 1}, monoid1{2, 2}, monoid1{3, 3}}
    b := []monoid{monoid2{"Trump", 111}, monoid2{"MaryJames", 1234}, monoid2{"Cachy", 123245}, monoid1{1, 1}, monoid1{2, 2}, monoid1{3, 3}}
    for i := 0; i < 6; i++ {
        fmt.Println(a[i].op(b[i], a[i]))
    }
    return
}
sgsw
  • 23
  • 3