set_option warn.classDefReducibility false

class magma (α) where op : α → α → α

infix:70 " ⋆ " => magma.op (self := inferInstance)

class leftIdMagma (α) extends magma α where
  identity : α
  id_op (a : α) : identity ⋆ a = a := by intros; rfl

class rightIdMagma (α) extends magma α where
  identity : α
  op_id (a : α) : a ⋆ identity = a := by intros; rfl

class semigroup (α) extends magma α where
  assoc (a b c : α) : (a ⋆ b) ⋆ c = a ⋆ (b ⋆ c) := by intros; rfl

class idMagma (α) extends leftIdMagma α, rightIdMagma α

class monoid (α) extends idMagma α, semigroup α

def magmaMonoid : leftIdMagma (base → base) := {
  op := Function.comp
  identity := id
}

def fnCompMonoid : monoid (base → base) := {
  op := Function.comp, identity := id
}

namespace Ex2

structure A (α) where
  subsingleton : ∀ a b : α, a = b := by assumption

structure B (α) where
  op : α → α → α
  idempotent : ∀ a : α, op a a = a := by assumption
  fav : α := by assumption

structure C (α) where
  op : α → α → α
  comm : ∀ a b : α, op a b = op b a := by assumption

structure D (α) extends A α, B α
structure E (α) extends C α, B α

-- Let's reuse these
theorem s (a b : Unit) : a = b := rfl
def op (_ _ : Unit) : Unit := ()
def i (a : Unit) : op a a = a := s _ a
def c (a b : Unit) : op a b = op b a := s _ _

-- Successfully defined
def d : D Unit := have := s; have := i; have := ()
                  { op }

def e : E Unit := have := c; have := i; have := ()
                  { op }

structure F (α) extends D α, E α
structure G (α) extends E α, D α

def f : F Unit := have := s; have := i; have := c; have := ()
                  { op }
-- `idempotent`, `fav` missing
def g : G Unit := have := s; have := i; have := c; have := ()
                  { op }

end Ex2
