【问题标题】:Make Monoid instance of Writer (Haskell)制作 Writer (Haskell) 的 Monoid 实例
【发布时间】:2016-07-31 22:27:56
【问题描述】:

在 Haskell 中,我想让一个 Writer monad 成为一个 monoid 的实例:

instance (Monoid a) => Monoid (Writer (Sum Int) a) where
  mempty = return mempty
  w1 `mappend` w2 = writer((s++t, s'++t'), Sum (m+n)) where
    ((s,s'), Sum m) = runWriter w1
    ((t,t'), Sum n) = runWriter w2

因此,直观地说,如果 Writer monad 的“数据”类型是一个幺半群,我希望能够将整个 Writer 事物也视为一个幺半群(由 mempty 和 mappend 实现。

但这不起作用:GHCI 编译器说

Illegal instance declaration for `Monoid (Writer (Sum Int) a)'
  (All instance types must be of the form (T t1 ... tn)
   where T is not a synonym.
   Use -XTypeSynonymInstances if you want to disable this.)
In the instance declaration for `Monoid (Writer (Sum Int) a)'

而且我真的不知道这里什么类型应该是同义词以及我如何才能符合编译器的规则。

【问题讨论】:

  • 不遵守规则:按照编译器的建议启用-XTypeSynonymInstances 来放宽规则。 -XFlexiblesInstances 也可能是必需的。

标签: haskell functional-programming


【解决方案1】:

每个人都在做很多工作。 writer monad 上的绑定运算符已经附加了ws。这也意味着它适用于任意基础 monad。

instance (Monoid w, Monoid a, Monad m) => Monoid (WriterT w m a) where
    mempty = return mempty
    mappend = liftA2 mappend

此时很明显,即使WriterT也是多余的,而这实际上是这个通用instance的“实例”

instance (Monoid a, Monad m) => Monoid (m a) where
    -- same

但是 Haskell 的类系统并没有真正允许这样的实例——它会匹配从类型构造函数构建的每个 monoid。例如,此实例将匹配 Sum Int,然后失败,因为 Sum 不是 monad。所以你必须为你感兴趣的每个 monad 单独指定它。

【讨论】:

  • 肯定有一个扩展可以做到这一点,不是吗?
  • 好尴尬!毕竟,我是提议将Ap 添加到Data.Monoidnewtype Ap f a = Ap (f a)instance (Applicative f, Monoid a) => Monoid (Ap f a) 的人。请注意,Monad 太过分了。
  • @Bergi,有重叠的实例,但它们是邪恶的。
【解决方案2】:

Writer 是一个类型别名(link)

type Writer w = WriterT w Identity

所以请改用WriterT ... Identity。您仍然需要启用 FlexibleInstances。

也许这就是你所追求的:

{-# LANGUAGE FlexibleInstances #-}

import Control.Monad.Trans.Writer
import Data.Monoid
import Data.Functor.Identity

instance (Monoid w, Monoid a) => Monoid (WriterT w Identity a) where
  mempty = return mempty
  m1 `mappend` m2 = writer (a1 <> a2, w1 <> w2)
    where
      (a1,w1) = runWriter m1
      (a2,w2) = runWriter m2

当然,这可以推广到任意 Monad 而不是 Identity。

【讨论】:

  • 我认为可以通过更一般地处理WriterT来避免扩展。
猜你喜欢
  • 2014-11-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多