为 Reader 实现幺半群

dan*_*oks 1 haskell higher-kinded-types monoids

我在这里的想法可能很幼稚,但我认为如果 a 的右侧值是 aReader的实例,Monoid则可Monoid以为Reader...定义...这是我的实现:

instance Monoid a => Monoid (Reader r a) where
  mempty = pure mempty
  mappend ra rb = (<>) <$> ra <*> rb
Run Code Online (Sandbox Code Playgroud)

然而,这会导致以下错误:

    • 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
    |                      ^^^^^^^^^^^^^^^^^^^
Run Code Online (Sandbox Code Playgroud)

我不确定这个错误实际上意味着什么,以及为什么我无法实现MonoidReader尽管我认为这与Reader成为更高级的类型有关?

Dan*_*ner 5

有两个问题。第一个是这样的:

type Reader r = ReaderT r Identity
Run Code Online (Sandbox Code Playgroud)

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

where T is not a synonym.
Run Code Online (Sandbox Code Playgroud)

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

instance Monoid a => Monoid (ReaderT r Identity a)
Run Code Online (Sandbox Code Playgroud)

但现在我们会陷入错误的另一部分,即:

All instance types must be of the form (T t1 ... tn)
Run Code Online (Sandbox Code Playgroud)

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

{-# LANGUAGE TypeSynonymInstances #-}
{-# LANGUAGE FlexibleInstances #-}
Run Code Online (Sandbox Code Playgroud)

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

instance (Applicative f, Monoid m) => Monoid (ReaderT r f m) where
    mempty = pure mempty
    mappend = liftA2 mappend
Run Code Online (Sandbox Code Playgroud)

这不需要扩展,并且不仅适用ReaderReaderT转换任何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
Run Code Online (Sandbox Code Playgroud)

然后,App (Reader r) a只要a是幺半群,就可以将其用作幺半群。我似乎记得这已经存在于标准库中的某个地方,但我再也找不到它了......