Как получить доступ как к значению, так и к аккумулятору монады Writer в блоке do (PureScript)?
Я изучаю монаду Writer прямо сейчас, и я не уверен, правильно ли хотеть читать как значение, так и аккумулятор монады в пределах do
блок. Например, в приведенной ниже функции coltzSeq я хочу считать длину аккумулятора Array в качестве окончательного вычисления функции. Возможно ли сделать это или это неправильное использование Writer? Очевидно, я мог позволить вызывающей стороне прочитать длину окончательного массива, или я мог бы использовать монаду State, но для меня это всего лишь упражнение.
module Main where
import Prelude
import Data.Tuple
import Control.Monad.Writer
import Math (remainder, (%))
import Data.Int (toNumber, fromNumber)
import Control.Monad.Eff.Console (logShow)
coltz :: Number -> Number
coltz n = case (n % 2.0 == 0.0) of
true -> n / 2.0
false -> 3.0 * n + 1.0
coltzW :: Number -> Writer (Array Number) Number
coltzW n = do
tell [n]
pure $ coltz n
-- Computes a coltz sequence and counts how many
-- steps it took to compute
coltzSeq :: Number -> Writer (Array Number) Int
coltzSeq n = do
-- Can read the value in the writer
-- but not the writer's internal state
v <- (coltzW n)
let l = 1
-- Can read the value and the internal
-- state, but it's not bound to the monad's context.
-- let a = runWriter (coltzW n)
-- let v = fst a
-- let l = length (snd a)
case (v) of
1.0 -> pure $ l
_ -> to1 v
Изменить: я попробовал предложение Г.Б. и попытался с помощью listens
функция, которая имеет тип (Monoid w, Monad m) => forall w m a b. MonadWriter w m => (w -> b) -> m a -> m (Tuple a b)
, Если мы используем id
в этом контексте тип будет...
MonadWriter w m => (w -> b) -> m a -> m (Tuple a b)
w = Array Number
m = WriterT (Array Number) Identity (alias: Writer (Array Number) )
b = Array Number
a = Number
(Array Number -> Array Number) ->
Writer (Array Number) Number ->
Writer (Array Number) (Tuple Number (Array Number))
Так listens id
принимает Writer (Array Number) Number)
и возвращает Writer со значением, являющимся текущим состоянием Writer (так как мы использовали id
). Тем не менее, я продолжаю получать ошибки типа со всеми способами, которые я пытаюсь использовать listens
to1 :: Number -> (Writer (Array Number)) Int
to1 n = do
v <- (coltzW n)
-- a <- snd <$> listens id
-- let l = snd <$> (listens id (execWriter (coltzW n)))
-- let l = execWriter (listens id (coltzW n))
-- Seems like this one should work to get Array Number
-- let l = snd <$> (listens id (coltzW n))
case (v) of
1.0 -> pure 1
_ -> to1 v
Edit2: я понял, что мне нужно было сделать. По какой-то причине мне нужно было добавить аннотацию типа при использовании listens
,
lengthOfSeq :: Writer (Array Number) Int -> Writer (Array Number) Int
lengthOfSeq c = do
-- Without type annotation, I get this error...
-- No type class instance was found for
--
-- Control.Monad.Writer.Class.MonadWriter (Array t0)
-- (WriterT (Array Number) Identity)
--
-- The instance head contains unknown type variables. Consider adding a type annotation.
Tuple a w <- (listens id c :: Writer (Array Number) (Tuple Int (Array Number)))
pure $ length w
to1 :: Number -> (Writer (Array Number)) Int
to1 n = lengthOfSeq $ seq n
where
seq n = do
v <- coltzW n
case (v) of
1.0 -> do
pure 1
_ -> seq v
2 ответа
Я думаю listens
это функция, которую вы ищете: https://pursuit.purescript.org/packages/purescript-transformers/1.0.0/docs/Control.Monad.Writer, если я правильно понимаю, что это вы пытаюсь сделать.
Вы можете пройти id
если вы не заинтересованы в преобразовании значения, и если вы хотите получить только накопленное значение, a <- snd <$> listens id
должен сделать свое дело.
Вы не можете получить доступ к текущему аккумулятору. Writer
только для записи. использование State
вместо Writer
если вам нужен аккумулятор тока. listen
может только дать вам аккумулятор законченного вычисления.