How to write length function for all Monoids
A monoid, in the general case, as no notion of length. Take for instance Sum Int
, which is Int
equipped with addition for its monoidal operation. We have
Sum 3 <> Sum 4 = Sum 7 = Sum (-100) <> Sum 7 <> Sum (100)
What should be its "length"? There is no real notion of length here, since the underlying type is Int
, which is not a list-like type.
Another example: Endo Int
which is Int -> Int
equipped with composition. E.g.
Endo (\x -> x+1) <> Endo (\x -> x*2) = Endo (\x -> 2*x+1)
Again, no meaningful "length" can be defined here.
You can browse Data.Monoid
and see other examples where there is no notion of "length".
Const a
is also a (boring) monoid with no length.
Now, it is true that lists [a]
form a monoid (the free monoid over a
), and length can indeed be defined there. Still, this is only a particular case, which does not generalize.
The Semigroup
and Monoid
interfaces provide a means to build up values, (<>)
. They don't, however, give us a way to break down or otherwise extract information from values. That being so, a length
generalised beyond some specific type requires a different abstraction.
As discussed in the comments to chi's answer, while Data.Foldable
offers a generalised length :: Foldable t => t a -> Int
, it isn't quite what you were aiming at -- in particular, the connection between Foldable
and Monoid
is that foldable structures can be converted to lists/the free monoid, and not that foldables themselves are necessarily monoids.
One other possibility, which is somewhat obscure but closer to the spirit of your question, is the Factorial
class from the monoid-subclasses package, a subclass of Semigroup
. It is built around factors :: Factorial m => m -> [m]
, which splits a value into irreducible factors, undoing what sconcat
or mconcat
do. A generalised length :: Factorial m => m -> Int
can then be defined as the length of the list of factors. In any case, note that we still end up needing a further abstraction on the top of Semigroup
/Monoid
.