制作Monoid的Writer实例(Haskell)

时间:2016-07-31 22:27:56

标签: haskell functional-programming

在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的类型是一个monoid,我希望能够将整个Writer事物视为一个monoid(由mem​​pty和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)'

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

2 个答案:

答案 0 :(得分:3)

每个人都做了这么多工作。编写器monad上的绑定运算符已附加w s。这也意味着它适用于任意基础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单独指定它。

答案 1 :(得分: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。