Это должно объяснить это:
> runWriter (return 15) :: (Int, [String])
(15,[]) -- == runWriter $ writer (15, mempty)
> runWriter (logNumber 3)
(3,["Got number: 3"]) -- == runWriter $ writer (3, ["Got number: 3"])
> runWriter (logNumber 5)
(5,["Got number: 5"]) -- == runWriter $ writer (5, ["Got number: 5"])
> runWriter (logNumber 3 >> logNumber 5)
(5,["Got number: 3","Got number: 5"]) -- == ["Got number: 3"] ++ ["Got number: 5"]
> runWriter (logNumber 3 >> logNumber 5 >> return 15 )
(15,["Got number: 3","Got number: 5"]) -- == ["Got number: 3"] ++ ["Got number: 5"] ++ []
> runWriter (logNumber 3 >>= (\_ -> logNumber 5 >>= (\_ -> return 15 ) ) )
(15,["Got number: 3","Got number: 5"])
> runWriter (logNumber 3 >>= (\i -> logNumber 5 >>= (\j -> return (i*j) ) ) )
(15,["Got number: 3","Got number: 5"])
И монадическое выражение последней строки эквивалентно блоку multWithLog
do
.
Обратите внимание на вложенность лямбда-функций: лямбда-функция
(\j -> return (i*j) )
находится внутри лямбда-функция
(\i -> logNumber 5 >>= (\j -> return (i*j) ) )
Именно поэтому i
в этом return (i*j)
относится к i
внешнему аргументу лямбда-функции *1022*, полученному им из самого внешнего выражения монадического действия, logNumber 3
.
Как? Поскольку в соответствии с определением >>=
, как вы указали в своем вопросе, у нас есть
runWriter ( Writer (x,v) >>= f )
=
runWriter ( let (Writer (y, u)) = f x in Writer (y, v `mappend` u) )
=
let (Writer (y, u)) = f x in runWriter ( Writer (y, v `mappend` u) )
=
let (Writer (y, u)) = f x in (y, v `mappend` u)
1032 * т.е. *
runWriter ( logNumber 5 >>= (\j -> return j) )
= -------- f -----
runWriter ( writer (5, ["Got number: 5"]) >>= (\j -> writer (j, mempty)) )
= -- x ------- v ------- -------- f ---------------
let Writer (y, u) = ( (\j -> writer (j, mempty)) 5 )
-------- f --------------- x
in (y, ["Got number: 5"] `mappend` u)
= ------- v -------
let (y, u) = (5, mempty) in (y, ["Got number: 5"] `mappend` u)
=
(5, ["Got number: 5"] `mappend` mempty)
«Моноидальные значения» от каждого Writer
действия делают не «измененными». Каждое действие вносит свое «моноидальное значение» в общее «моноидное значение» комбинированного вычисления Writer
-типа блока do
, построенного из его суб-вычислений Writer
-типа с помощью mappending
моноидальные значения, вносимые каждым подвычислением ( семантика из Writer
), найденные в поле snd
кортежей, представляющих действия ( реализация из Writer
).
Опять же, это общее значение объединяется путем объединения частей моноидального значения каждого кортежа, а именно их полей snd
. Моноидальная комбинация - mappend
, что делается для нас за кадром с помощью вычислений типа Writer
.
А для списков mappend [a] [b] == [a] ++ [b] == [a,b]
, а mappend [a,b] [] == [a,b] ++ [] == [a,b]
.
Ваши вопросы, тогда:
не должно return (a*b)
составлять (15,[])
?
так и должно быть, и это так, как мы видели в начале ответа.
Writer
против WriterT
оболочки
это не имеет значения. Оба изоморфны, потому что Identity
не является опцией. WriterT
является частью монад-трансформеров реализация монады Writer; тот, что приведен в книге, проще и понятнее.
Как эти операции привели к изменению [String]
части монады Writer?
не изменено, но объединено mappend
определенного моноида, используемого конкретным писателем; как часть монадной комбинации , то есть определение монадической привязки, >>=
, определение; будучи как Monads, обобщают протокол вызова функций, а Writer Monad собирает значения Monoid за кулисами, чтобы их можно было добавлять в тени, в дополнение к пользовательским функциям, выполняющим свою работу в открытом режиме:
do { a <- logNumber 3
; b <- logNumber 5
; return (a*b)
}
= ----- user area ------ ---- hidden area ---
do { a <- writer (3 , ["Got number: 3"] )
; b <- writer (5 , ["Got number: 5"] )
; writer (
(a*b) , [] )
}
=
Writer
( (3*5) , mconcat [..., ..., ...] )
Объятия do
- примечание, это твой друг. Это помогает нам мыслить абстрактно .