Как получить доступ как к значению, так и к аккумулятору монады 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 может только дать вам аккумулятор законченного вычисления.

Другие вопросы по тегам