Гарантирует ли Writer Monad правильную ассоциативную конкатенацию?

В Проверки в Haskell утверждалось, что использование Writer гарантирует правильно-ассоциативную конкатенацию. Однако этот пример, похоже, свидетельствует об обратном. Какой правильный ответ?

{-# LANGUAGE OverloadedStrings #-}

import Control.Monad.Writer
import Data.String

data TM = TMempty
        | TMappend TM TM
        | TMfromString String

instance IsString TM where
  fromString = TMfromString

instance Monoid TM where
  mempty  = TMempty
  mappend = TMappend

instance Show TM where
  showsPrec d TMempty = showString "\"\""
  showsPrec d (TMfromString s) = showString $ show s
  showsPrec d (TMappend a b) = showParen (d > 0) $
    showsPrec 1 a .
    showString " ++ " .
    showsPrec 0 b

theWriter :: Writer TM ()
theWriter = do
  tell "Hello"
  replicateM_ 2 $ tell "World"
  tell "!"

main = print $ execWriter theWriter

Производит:

"Hello" ++ ("World" ++ "World" ++ "") ++ "!"

person pat    schedule 04.01.2012    source источник
comment
+1 за простой пример использования и реализации showsPrec.   -  person Joey Adams    schedule 04.01.2012
comment
Интересно, что если вы замените replicateM_ на replicateM, вывод станет "Hello" ++ ("World" ++ ("World" ++ "" ++ "") ++ "") ++ "!"   -  person pat    schedule 05.01.2012
comment
Это разница между sequence и sequence_: sequence = foldr (liftM2 (:)) (return []) но sequence_ = foldr (>>) (return ()); первый генерирует больше привязок, потому что он работает с результатами.   -  person ehird    schedule 05.01.2012
comment
В любом случае >> должен быть ассоциативным. Но я не монадический юрист.   -  person Joey Adams    schedule 05.01.2012
comment
@JoeyAdams: Действительно; если w следует законам моноидов, Writer w будет следовать законам монад. Но неправоассоциативные mappend обычно неэффективны.   -  person ehird    schedule 05.01.2012


Ответы (2)


Да, это действительно неверно. Из исходного кода :

m >>= k  = WriterT $ do
    ~(a, w)  <- runWriterT m
    ~(b, w') <- runWriterT (k a)
    return (b, w `mappend` w')

...

-- | @'tell' w@ is an action that produces the output @w@.
tell :: (Monoid w, Monad m) => w -> WriterT w m ()
tell w = WriterT $ return ((), w)

Таким образом, цепочка mappends будет отражать цепочку (>>=)s.

person ehird    schedule 04.01.2012

Writer [a] не гарантирует правоассоциативной конкатенации, но вы можете получить гарантированную правоассоциативную конкатенацию с помощью Writer (Endo [a]).

person Jeremy List    schedule 26.12.2014