代码之家  ›  专栏  ›  技术社区  ›  danbroooks

为读者实现monoid

  •  1
  • danbroooks  · 技术社区  · 6 年前

    我在这里的想法可能是幼稚的,但我认为如果右手的价值 Reader 是的一个实例 Monoid 然后一个 幺半群 可以为定义 读者 …以下是我的实现:

    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
        |                      ^^^^^^^^^^^^^^^^^^^
    

    我不确定这个错误实际上意味着什么,以及为什么我不能实现 幺半群 对于 读者 虽然我认为这与 读者 是一个更高血统的人吗?

    2 回复  |  直到 6 年前
        1
  •  4
  •   Daniel Wagner    6 年前

    有两个问题。第一个问题是:

    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 实例。

    然而,它确实是一个孤立的实例,因此您应该考虑编写 另一个 NewType包装。

    {-# 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 是幺半群。我似乎还记得在标准库中的某个地方已经存在过这个问题,但是我再也找不到它了…

        2
  •  2
  •   chi    6 年前

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

    Haskell2010不允许使用前者(这在允许的情况下是非常保守的),但是如果您打开在您发布的错误中报告的扩展名,那么ghc允许在实例中使用类型别名。注意,在这种情况下,它将为 膨胀 别名,例如

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

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