Semigroupoid和Semigroup类之间的关系

3
在过去的一周里,我一直在试图理解Haskell的“核心”类型和类型类(但总共只学习了不到两周的时间),我发现了一个问题:
  • “Semigroupoid”是“Category”的概括,这意味着任何“Category”都可以通过忽略它自己的身份并定义

o = (.)

  • 来轻松地成为“Semigroupoid”。
同时,“Semigroup”也是“Monoid”的概括,具有与上述完全相同的内涵:只需忽略mempty并定义
  • (<>) = mappend
这两个事实以及Semigroupoids和Semigroups仅具有元素组合的概念(semigroupoid-组合与semigroup-乘法),而Category和Monoid也具有“统一”概念(identity vs unit element),使人们想到可以如下表达Semigroupoids和Semigroups之间以及Categories和Monoids之间的关系。
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”有关?

任何见解都将不胜感激。

1个回答

3
如果只需要使用 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 #-} pragma来使其工作,但这是一些相当琐碎的事情,并且可能会导致奇怪的行为。强烈建议避免使用这样的实例。


我不确定我理解为什么会发生这种情况:当然(一般来说)Nontrivial不是一个范畴,但这就是我的观点:任何范畴都会自动成为一个幺半群,而只要其类型参数也是如此,Nontrivial也将成为一个幺半群。由于Nontrivial不是(被制作成)一个范畴,我不明白ghc在抱怨什么。 - LorenzoPerticone
你知道Nontrivial不是一个类别。编译器无法知道这一点;实际上,任何人都可以通过定义孤儿instance Category Nontrivial来使它成为一个类别。当然,真正的问题是,这不可能是“良好形式”的,因为sq参数实际上是协变的,但是第一个参数必须是逆变的要求并没有在Category类中以编译器能够理解的形式明确提到,那么它拒绝该实例的依据是什么? - leftaroundabout

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接