haskell - Semigroup 和 Monoid 实例中约束的差异
问题描述
为什么 Monoid 实例需要 (Ord a, Ord b) 约束,而 Semigroup 实例不需要?
这是否取决于 Category.Constrained 类或使用 GADT 来定义数据类型?
{-# LANGUAGE GADTs, TypeFamilies, ConstraintKinds, StandaloneDeriving #-}
module Question3 where
import Control.Category.Constrained as CC
import Data.Set as S
import Data.Map as M
data RelationMS a b where
IdRMS :: RelationMS a a
RMS :: (Ord a, Ord b) => Map a (Set b) -> RelationMS a b
deriving instance (Show a, Show b) => Show (RelationMS a b)
RMS mp2 `compRMS` RMS mp1
| M.null mp2 || M.null mp1 = RMS M.empty
| otherwise = RMS $ M.foldrWithKey
(\k s acc -> M.insert k (S.foldr (\x acc2 -> case M.lookup x mp2 of
Nothing -> acc2
Just s2 -> S.union s2 acc2
) S.empty s
) acc
) M.empty mp1
instance Category RelationMS where
type Object RelationMS o = Ord o
id = IdRMS
(.) = compRMS
instance Semigroup (RelationMS a b) where
RMS r1 <> RMS r2 = RMS $ M.foldrWithKey (\k s acc -> M.insertWith S.union k s acc) r1 r2
instance (Ord a, Ord b) => Monoid (RelationMS a b) where
mempty = RMS $ M.empty
mappend = (<>)
解决方案
这当然与类别实例无关。
Semigroup
至少在概念上,该实例确实也需要Ord
,但是您已经将其打包在 GADT 中(除非在这种Id
情况下,因为它很简单而不需要它),因此无需在实例头中提及约束。
mempty
但是,您手头没有可以读RelationMS
出(Ord a, Ord b)
约束的值。恰恰相反:您需要提供这些约束,因为您现在正试图结束这样的 GADT!这就是为什么Monoid
实例需要在其头部约束,而实例Semigroup
不需要。
推荐阅读
- memory-management - 如何在内核启动时找出伙伴分配器分配了每个订单的多少块内存?
- python - 如何在python中阅读单词(docx)?
- c++ - cv::Point3f 的构造函数是如何工作的?
- python - Jupyter Hub:启动器中的致命错误,...系统找不到指定的文件
- mysql - 如何在包含不同数据的相同列的两个不同表上创建视图
- angular - 从多选择角反应形式中删除选定的值
- c# - 在具有不同大小写的 switch-case 块中比较 C# 中的字符串
- javascript - 无法将值从 Google 工作表显示到 Google Web App
- mysql - 如何将跳过主键列的文本文件的数据导入 MySQL 数据库
- java - Spring Boot异步线程未完成任务