Связь между классами полугрупп и полугрупп
На прошлой неделе я пытался понять некоторые из "основных" типов и классов типов в Haskell (но изучал Haskell не более двух недель), и я нашел кое-что, что меня беспокоит:
- "Полугруппоид" - это обобщение "Категории", означающее, что любая Категория тривиально является Полугруппой, просто игнорируя ее собственную идентичность и определяя
o = (.)
- также "Полугруппа" - это обобщение "Моноида" в том же смысле, что и выше: нужно просто игнорировать слабо выраженное и определять
(<>) = mappend
Только эти два факта и идея о том, что и полугруппы, и полугруппы обладают только понятием объединения элементов (полугруппоидная композиция против полугруппового умножения), а категории и моноид также имеют понятие "единство" (тождество против единичного элемента), приводят к помните, что отношения между полугруппоидами и полугруппами, а также между категориями и моноидами можно выразить следующим образом
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!"
Теперь я не уверен, почему это не компилируется; компилятор GHC говорит:
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.
но бывает, что в том числе
{-# LANGUAGE FlexibleInstances #-}
поверх файла все исправляет.
Вышеуказанные отношения, похоже, не выстраиваются в библиотеках, в то время как отношения между полугруппой и категорией, а также между полугруппой и моноидом встроены.
Есть ли какая-то конкретная причина для этого, и я просто скучаю по ней?
Может быть, это как-то связано с таинственными "FlexibleInstances"?
Любое понимание будет с благодарностью.
1 ответ
Если это только требуется FlexibleInstances
никто не будет возражать (это расширение абсолютно безвредно), но, к сожалению, оно также приводит к требованию очень безобидного другого расширения, как только вы добавите достаточно интересные другие экземпляры. А именно,
{-# 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 ()
не компилируется:
$ 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 () }
Теперь вы можете заставить его работать, добавив {-# OVERLAPPABLE #-}
Pragmas, но это довольно неприятное дело и может привести к странному поведению. Таких случаев категорически избегают.