为阅读器实现monoid

时间:2018-08-01 18:43:51

标签: haskell higher-kinded-types monoids

我在这里的想法可能很幼稚,但我认为,如果Reader的右手边的值是Monoid的实例,那么可以为{{1} } ...这是我的实现:

Monoid

但这会导致以下错误:

Reader

我不确定此错误的实际含义,为什么我无法为instance Monoid a => Monoid (Reader r a) where mempty = pure mempty mappend ra rb = (<>) <$> ra <*> rb 实现 • Illegal instance declaration for ‘Monoid (Reader r a)’ (All instance types must be of the form (T t1 ... tn) where T is not a synonym. Use TypeSynonymInstances if you want to disable this.) • In the instance declaration for ‘Monoid (Reader r a)’ | 413 | instance Monoid a => Monoid (Reader r a) where | ^^^^^^^^^^^^^^^^^^^ ,尽管我认为这与Monoid是一种更高种类的类型有关?

2 个答案:

答案 0 :(得分:4)

有两个问题。首先是这样:

type Reader r = ReaderT r Identity

由于历史原因,实例声明中不允许使用类型同义词。这是

where T is not a synonym.

部分错误。幸运的是,我们可以扩展同义词。这会给我们

instance Monoid a => Monoid (ReaderT r Identity a)

但是现在我们将了解错误的另一部分,即:

All instance types must be of the form (T t1 ... tn)

具体来说,Identity不是类型变量,因此不适合这种形式。同样,这一限制主要是出于历史原因。您可以通过启用两种语言扩展来消除这两个限制:

{-# LANGUAGE TypeSynonymInstances #-}
{-# LANGUAGE FlexibleInstances #-}

但是,在这种情况下不需要。最好的方法是实际使用规定的实例声明形式,因此:

instance (Applicative f, Monoid m) => Monoid (ReaderT r f m) where
    mempty = pure mempty
    mappend = liftA2 mappend

这不需要扩展,不仅适用于Reader,而且还适用于ReaderT转换任何Applicative实例。

但是它确实会创建一个孤立实例;因此,您应该考虑编写另一个新型包装器。

{-# LANGUAGE GeneralizedNewtypeDeriving #-}
-- or, instead of GeneralizedNewtypeDeriving, write the obvious instances by hand
newtype App f m = App { getApp :: f m } deriving (Functor, Applicative)
instance (Applicative f, Monoid m) => Monoid (App f m) where
    mempty = pure mempty
    mappend = liftA2 mappend

然后,只要App (Reader r) a是一个单节体,就可以使用a作为一个单节体。我似乎记得它已经存在于标准库中的某个地方,但是我再也找不到了...

答案 1 :(得分:2)

这里的问题是您的Reader是类型别名,而不是newtype

Haskell2010不允许使用前者(这在允许的范围内非常保守),但是如果您打开在所发布的错误中报告的扩展名,则GHC允许在实例中使用类型别名。请注意,在这种情况下,它将为别名的扩展定义一个实例,例如

instance Monoid a => Monoid (r -> a) where ...

对于Reader类型,我更喜欢使用一个新类型,即使使用时需要包装/解开它。