I'm playing around with rewriting simple functions in different ways and I clearly misunderstand some core concepts. Is there a better way to work with limited types like these?
mlength :: Monoid m => m -> Int
mlength mempty = 0
mlength (l <> r) = mlength l + mlength r
It fails compilation with the following error:
Parse error in pattern: l <> r
I can see that my usage of <>
is misguided because there are multiple correct matches for l
and r
. Even though it looks like it doesn't matter which value is assigned, a value still has to be assigned in the end. Maybe there's a way for me to assert this decision for specific Monoid instances?
"ab" == "" <> "ab"
"ab" == "a" <> "b"
"ab" == "ab" <> ""
A monoid, in the general case, has 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
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With