给出以下数据类型
{-# LANGUAGE GADTs #-}
data Response a where
ResponseMap :: HashMap Text (Sum Int) -> Response (HashMap Text (Sum Int))
ResponseSum :: Sum Int -> Response (Sum Int)
我如何为它推导出一个monoid实例?对于mappend
的定义,我可以在构造函数上进行模式匹配
(ResponseSum v1) `mappend` (ResponseSum v2) = undefined
(ResponseMap v1) `mappend` (ResponseMap v2) = undefined
并轻松合并这些值,但我不知道如何实施mempty
,或者它是否确实可行或有意义?
答案 0 :(得分:8)
正如您所注意到的,您无法提供instance Monoid (Response a)
,因为您无法定义mempty :: Response a
。为什么不?好吧,mempty
必须为所有 Response a
添加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
包中的模块Data.Semigroup
中找到此类型类;在此之前,您需要使用具有相同模块名称的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
个实例。