На прошлой неделе я пытался понять некоторые из «основных» типов и классов типов 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 #-}
поверх файла все исправляет.
Выраженные выше отношения не кажутся встроенными в библиотеки, в то время как отношения между Semigroupoid и Category, а также между Semigroup и Monoid встроены.
Есть ли для этого какая-то конкретная причина, и я просто скучаю по ней?
Может быть, это как-то связано с загадочными «FlexibleInstances»?
Приветствуется любое понимание.