In the last week I've been trying to grasp some of Haskell's "core" types and type classes (but have been studying Haskell for at most two weeks total), and I've found something that bugs me:
o = (.)
(<>) = mappend
These two facts alone, and the idea that both Semigroupoids and Semigroups only have a notion of combining elements (semigroupoid-composition vs semigroup-multiplication) and both Category and Monoid also have a notion of "unity" (identity vs unit element) bring to mind that one could express relations between Semigroupoids and Semigroups, and between Categories and Monoids as follows
import Prelude hiding (id, (.))
import Data.Semigroupoid
import Data.Semigroup
import Control.Category
import Data.Monoid
instance Semigroupoid c => Semigroup (c a a) where
(<>) = o
instance Category c => Monoid (c a a) where
mempty = id
mappend = (.)
main = putStrLn "Does not type-check!"
Now, I'm not sure why this doesn't compile; the ghc compiler says:
All instance types must be of the form (T a1 ... an)
where a1 ... an are *distinct type variables*,
and each type variable appears at most once in the instance head.
but it happens that including
{-# LANGUAGE FlexibleInstances #-}
on top of the file fixes everything.
The relations expressed above don't seem to be built in the libraries, while the relations between Semigroupoid and Category, and between Semigroup and Monoid are be built in.
Is there any specific reason for this, and I'm just missing it?
Maybe it has something to do with the mysterious "FlexibleInstances"?
Any insight would be greatly appreciated.
If it only required FlexibleInstances
nobody would mind (that extension is totally harmless), but unfortunately it also leads to requiring a very un-harmless other extension as soon as you add sufficiently interesting other instances. Namely,
{-# LANGUAGE FlexibleInstances #-}
import Control.Category (Category)
instance Category c => Monoid (c a a)
data Nontrivial s q = Nontrivial {
someLabel :: String
, someValues :: [(s, Int)]
, otherValues :: Maybe (String, q)
, moreStuff :: ({-...-})
}
instance (Monoid s, Monoid q) => Monoid (Nontrivial s q) where
mempty = Nontrivial "" [] Nothing ()
main = case mempty :: Nontrivial String String of
Nontrivial _ _ _ _ -> return ()
does not compile:
$ runhaskell wtmpf-file6064.hs
wtmpf-file6064.hs:17:13:
Overlapping instances for Monoid (Nontrivial String String)
arising from a use of ‘mempty’
Matching instances:
instance Category c => Monoid (c a a)
-- Defined at wtmpf-file6064.hs:5:10
instance (Monoid s, Monoid q) => Monoid (Nontrivial s q)
-- Defined at wtmpf-file6064.hs:14:10
In the expression: mempty :: Nontrivial String String
In the expression:
case mempty :: Nontrivial String String of {
Nontrivial _ _ _ _ -> return () }
In an equation for ‘main’:
main
= case mempty :: Nontrivial String String of {
Nontrivial _ _ _ _ -> return () }
Now, you could get it working by adding {-# OVERLAPPABLE #-}
Pragmas, but this is some rather fiddly business and can lead to strange behaviour. Such instances are strongly avoided.