5

I'm trying to create a "hierarchy" of algebraic type classes, as follows:

class Semigroup a where
  (.*) :: a -> a -> a
  foldr1 (.*) = foldl1 (.*)   -- GHCi error: "`foldr1' is not a (visible) method of class `Semigroup'"

class (Semigroup a) => Monoid a where
  identity :: a
  (.*) identity = id :: a -> a  -- GHCi error: "`.*' is not a (visible) method of class `Monoid'"

class (Monoid a) => Group a where
  inverse :: a -> a

Thus, groups are monoids and monoids are semigroups. However, I get errors that the classes can't see the functions of their parent class.

These errors bug me because I assumed that by writing (for example) class (Semigroup a) => Monoid a the class Monoid a would be able to see the function (.*). And furthermore, the type of foldr1 in the Prelude has no constraints, so I assumed that foldr1 would work in this context.

Chris Martin
  • 30,334
  • 10
  • 78
  • 137
  • To clarify, what I was going for was to implement the axioms for semigroups, monoids, and groups as type classes. Thus, the class `Semigroup a` must come with an associative binary operation `(.*) :: a -> a -> a`. I tried to imply associativity using `foldr1`, but this was my third try, being preceded by `(x .* y) .* z = x .* (y .* z)` and `\x y z -> (x .* y) .* z = \x y z -> x .* (y .* z)`. Then I wanted the class `Monoid a` to be a subclass of `Subgroup a`, inheriting the binary operation `(.*)` and showing how it interacts with the `identity` element (i.e., trivially). –  Jul 13 '11 at 08:46
  • 1
    Are you trying to state the laws for `(.*)`? You can't do that in the class declarations; I think you need to look at rewriting rules to get the effect that you want. – yatima2975 Jul 13 '11 at 08:54
  • 1
    @yatima2975 I looked that up and I haven't tried it yet, but that looks like exactly what I'm looking for. Thanks a lot –  Jul 13 '11 at 09:03
  • 1
    Yeah, in general you can't explicitly state requirements for associativity/commutativity/etc. in a type class. You can use rewrite rules as yatima2975 mentions to make some simple substitutions at compile-time, you can use something like QuickCheck to look for counterexamples, or you can write in Agda. :] – C. A. McCann Jul 13 '11 at 13:53

2 Answers2

6

Haskell does not allow you to declare (or enforce) equations on terms (like it seems like you want to do). This is for a very practical reason: proving equalities between arbitrary terms in a programming language as rich as Haskell is undecidable. Checking a human-constructed proof is often decidable, but it is also somewhat annoying to have to write and track these proofs while programming.

Still, if this is the kind of thing you want to do on a regular basis, there are languages that make this possible; the term to search for is "dependent types". For example, Coq and Agda are perhaps the two most popular dependently typed languages at the moment, and each would make it dead-simple to write a type inhabited only by good, law-abiding semigroups (or monoids).

Chris Martin
  • 30,334
  • 10
  • 78
  • 137
Daniel Wagner
  • 145,880
  • 9
  • 220
  • 380
4

I'm not sure what you're trying to do.

If you're trying to provide a default value for foldr1 in Semigroup, and a default value for (.*) in Monoid, then you can't.

  • foldr1 is defined in the Prelude as a non-typeclass function, so you can't give it a local definition in Semigroup
  • (.*) is part of the Semigroup class. You can give its values in Semigroup instances, and you can give a default value for it in the Semigroup class, but you can't give its value in the Monoid class (or Monoid instances)

If you're trying to to provide a default value for (.*) in Semigroup, and a default value for identity in Monoid, then you're using the wrong syntax.

Instead try something like

class Semigroup a where
    (.*) :: a -> a -> a
    (.*) = {-something-}

or

class Semigroup a where
    (.*) :: a -> a -> a
    x .* y = {-something-}
dave4420
  • 46,404
  • 6
  • 118
  • 152