Ответ 1
Первое наблюдение правильное, но это различие между тем, какие создаются грозди, важно.
Lazy
и Strict
не относятся к строгости в типе журнала, а вместо этого о строгости в паре.
Это связано с тем, что пара в Haskell имеет два возможных способа обновления.
bimap f g (a,b) = (f a, g b)
или
bimap f g ~(a,b) = (f a, g b)
Последнее совпадает с
bimap f g p = (f (fst p), g (snd p))
Разница между этими двумя заключается в том, что когда вы передаете аргументы в bimap
в первом случае, пара принудительно выполняется немедленно.
В последнем случае пара не будет принудительно принудительно, но вместо этого я передам вам (,)
назад, заполненную двумя нестрогими вычислениями.
Это означает, что
fmap f _|_ = _|_
в первом случае, но
fmap f _|_ = (_|_, _|_)
во втором случае с более ленивой парой!
Оба правильны при разных интерпретациях понятия пары. Один из вас принужден, притворяясь, что пара - это пара в категориальном смысле, что у нее нет никакого интересного _|_
в своем собственном праве. С другой стороны, интерпретация домена как нестрогая. насколько это возможно, так что вы можете иметь как можно больше программ, чтобы вы могли перейти к версии Lazy
.
(,) e
является вполне допустимым Writer
, поэтому это характеризует проблему.
Причина разграничения заключается в том, что это имеет значение для прекращения многих экзотических программ, которые фиксируют точку через монаду. Вы можете отвечать на вопросы о некоторых круговых программах с участием государства или писателя, если они ленивы.
Обратите внимание: ни в одном случае это не является строгим в аргументе "log". Когда вы проявляете строгость в том, что теряете правильную ассоциативность и технически перестаете быть Monad
. =/
Поскольку это не монада, мы не поставляем ее в mtl
!
С этим мы можем обратиться к вашему второму вопросу:
Однако есть некоторые обходные пути. Вы можете создать подделку Writer
поверх State
. В основном притворяйтесь, что вам не передан государственный аргумент. и просто переходите в состояние, как вы бы tell
. Теперь вы можете сделать это строго, потому что это не происходит за вашей спиной как часть каждого связывания. State
просто проходит через состояние, не измененное между действиями.
shout :: Monoid s => s -> Strict.StateT s m ()
shout s' = do
s <- get
put $! s <> s'
Это означает, однако, что вы вынуждаете всю вашу монаду State
получать результат и не можете создавать части Monoid
лениво, но вы получаете то, что оперативно приближается к тому, что ожидал бы строгий программист. Интересно, что это работает даже с помощью Semigroup
, поскольку единственное использование mempty
эффективно начинается, когда вы runState
.