是否可以为 GADT 创建一个幺半群实例?
Is it possible to create a monoid instance for a GADT?
给定以下数据类型
{-# LANGUAGE GADTs #-}
data Response a where
ResponseMap :: HashMap Text (Sum Int) -> Response (HashMap Text (Sum Int))
ResponseSum :: Sum Int -> Response (Sum Int)
我如何为它派生一个幺半群实例?对于 mappend
的定义,我可以在构造函数上进行模式匹配
(ResponseSum v1) `mappend` (ResponseSum v2) = undefined
(ResponseMap v1) `mappend` (ResponseMap v2) = undefined
并轻松组合这些值,但我不知道我将如何实施 mempty
,或者它是否确实可行或有意义?
如您所见,您无法提供 instance Monoid (Response a)
,因为您无法定义 mempty :: Response a
。为什么不?那么,对于 all a
,mempty
必须具有类型 Response a
,包括 Bool
。但是你不能构造Response Bool
类型的值,只能构造Response (HashMap Text (Sum Int))
和Response (Sum Int)
。所以你将无法创建 mempty
。这对 mappend
来说不是问题,因为你 给了 一个 Response a
,所以你可以检查给了你哪个 a
。但是mempty
没有什么可分析的
那你能做什么?嗯,首先,你可以提供一个instance Semigroup (Response a)
。一个半群就是一个没有 mempty
的幺半群,所以这正是你想要的。从 GHC 8 开始,您可以在 base
包中找到此类型 class,在模块 Data.Semigroup
; prior to that, you need to use the semigroups
包中,具有相同的模块名称。它使用二元运算符 (<>)
而不是 mappend
。所以你有
import Data.Semigroup
import Data.Monoid hiding ((<>))
-- ...
instance Semigroup (Response a) where
ResponseMap v1 <> ResponseMap v2 = ResponseMap $ v1 <> v2
ResponseSum v1 <> ResponseSum v2 = ResponseSum $ v1 <> v2
您还可以为您可以构造的类型索引提供特定 Monoid
实例。使用 FlexibleInstances
,看起来像
{-# LANGUAGE FlexibleInstances #-}
instance Monoid (Response (HashMap Text (Sum Int))) where
mempty = ResponseMap mempty
mappend = (<>)
instance Monoid (Response (Sum Int)) where
mempty = ResponseSum mempty
mappend = (<>)
现在,对于您 知道单位是什么的情况,您有一个 Monoid
实例。
给定以下数据类型
{-# LANGUAGE GADTs #-}
data Response a where
ResponseMap :: HashMap Text (Sum Int) -> Response (HashMap Text (Sum Int))
ResponseSum :: Sum Int -> Response (Sum Int)
我如何为它派生一个幺半群实例?对于 mappend
的定义,我可以在构造函数上进行模式匹配
(ResponseSum v1) `mappend` (ResponseSum v2) = undefined
(ResponseMap v1) `mappend` (ResponseMap v2) = undefined
并轻松组合这些值,但我不知道我将如何实施 mempty
,或者它是否确实可行或有意义?
如您所见,您无法提供 instance Monoid (Response a)
,因为您无法定义 mempty :: Response a
。为什么不?那么,对于 all a
,mempty
必须具有类型 Response a
,包括 Bool
。但是你不能构造Response Bool
类型的值,只能构造Response (HashMap Text (Sum Int))
和Response (Sum Int)
。所以你将无法创建 mempty
。这对 mappend
来说不是问题,因为你 给了 一个 Response a
,所以你可以检查给了你哪个 a
。但是mempty
没有什么可分析的
那你能做什么?嗯,首先,你可以提供一个instance Semigroup (Response a)
。一个半群就是一个没有 mempty
的幺半群,所以这正是你想要的。从 GHC 8 开始,您可以在 base
包中找到此类型 class,在模块 Data.Semigroup
; prior to that, you need to use the semigroups
包中,具有相同的模块名称。它使用二元运算符 (<>)
而不是 mappend
。所以你有
import Data.Semigroup
import Data.Monoid hiding ((<>))
-- ...
instance Semigroup (Response a) where
ResponseMap v1 <> ResponseMap v2 = ResponseMap $ v1 <> v2
ResponseSum v1 <> ResponseSum v2 = ResponseSum $ v1 <> v2
您还可以为您可以构造的类型索引提供特定 Monoid
实例。使用 FlexibleInstances
,看起来像
{-# LANGUAGE FlexibleInstances #-}
instance Monoid (Response (HashMap Text (Sum Int))) where
mempty = ResponseMap mempty
mappend = (<>)
instance Monoid (Response (Sum Int)) where
mempty = ResponseSum mempty
mappend = (<>)
现在,对于您 知道单位是什么的情况,您有一个 Monoid
实例。