Как вы заметили, вы не можете предоставить instance Monoid (Response a)
, потому что вы не можете определить mempty :: Response a
. Почему нет? Ну, mempty
должен иметь тип Response a
для всеa
, включая, скажем, Bool
. Но вы не можете построить значение типа Response Bool
, только Response (HashMap Text (Sum Int))
и Response (Sum Int)
. Таким образом, вы не сможете создать mempty
. Это не проблема для mappend
, потому что вы даны a 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
экземпляра.
Ваш GADT на самом деле не GADT. Это похоже на 'newtype Response a = Response a', но не так полезно, потому что' a' может быть только 'Sum Int' или' HashMap Text (Sum Int) '. Если вы используете 'newtype', вы можете использовать' GeneralizedNewtypeDeriving', чтобы получить экземпляр «Monoid» бесплатно –
@BenjaminHodgson, если бы я определял «Response a» как новый тип, у меня был бы способ определить функцию над ней, которая знает с чем он работает? – ppb
Я увлекаюсь старомодным способом 'f :: Response (Sum Int) -> Foo' –