为 Reader 实施幺半群
Implementing monoid for Reader
我在这里的想法可能很天真,但我认为如果 Reader
的右手值是 Monoid
的实例,那么 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
| ^^^^^^^^^^^^^^^^^^^
我不确定这个错误的实际含义,以及为什么我无法为 Reader
实施 Monoid
,尽管我认为这与 Reader
是一个更高等的人有关类型?
这里的问题是您的 Reader
是类型别名而不是 newtype
。
Haskell2010 不允许使用前者(在允许的情况下非常保守),但是如果您打开您发布的错误中报告的扩展,GHC 允许在实例中使用类型别名。请注意,在这种情况下,它将为别名的 扩展 定义一个实例,例如
instance Monoid a => Monoid (r -> a) where ...
对于 Reader
类型,我更愿意使用新类型,即使在使用时需要 wrap/unwrap 它。
有两个问题。第一个是这样的:
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 wrapper.
{-# 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
那么只要 a
是一个幺半群,你就可以使用 App (Reader r) a
作为一个幺半群。我似乎记得这已经存在于标准库的某个地方,但我再也找不到了...
我在这里的想法可能很天真,但我认为如果 Reader
的右手值是 Monoid
的实例,那么 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
| ^^^^^^^^^^^^^^^^^^^
我不确定这个错误的实际含义,以及为什么我无法为 Reader
实施 Monoid
,尽管我认为这与 Reader
是一个更高等的人有关类型?
这里的问题是您的 Reader
是类型别名而不是 newtype
。
Haskell2010 不允许使用前者(在允许的情况下非常保守),但是如果您打开您发布的错误中报告的扩展,GHC 允许在实例中使用类型别名。请注意,在这种情况下,它将为别名的 扩展 定义一个实例,例如
instance Monoid a => Monoid (r -> a) where ...
对于 Reader
类型,我更愿意使用新类型,即使在使用时需要 wrap/unwrap 它。
有两个问题。第一个是这样的:
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 wrapper.
{-# 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
那么只要 a
是一个幺半群,你就可以使用 App (Reader r) a
作为一个幺半群。我似乎记得这已经存在于标准库的某个地方,但我再也找不到了...