Связь между классами полугрупп и полугрупп

На прошлой неделе я пытался понять некоторые из "основных" типов и классов типов в 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, но это довольно неприятное дело и может привести к странному поведению. Таких случаев категорически избегают.

Другие вопросы по тегам