如何在 'do' 块 (PureScript) 中访问 Writer monad 的值和累加器?
How do I access both the value and accumulator of a Writer monad within a 'do' block (PureScript)?
我现在正在学习 Writer monad,我不确定在 do
块中读取 monad 的值和累加器是否正确。例如,在下面的 coltzSeq 函数中,我想读取数组累加器的长度作为函数的最终计算。是否有可能这样做或这是对 Writer 的不正确使用?显然,我可以让调用者读取最终数组的长度,或者我可以使用 State monad,但这只是我的练习。
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
编辑:
我尝试了 gb. 的建议并尝试使用类型为 (Monoid w, Monad m) => forall w m a b. MonadWriter w m => (w -> b) -> m a -> m (Tuple a b)
的 listens
函数。如果我们在此上下文中使用 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)
,returns一个值为当前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
编辑2:
我想出了我需要做什么。出于某种原因,我需要在使用 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
我认为 listens
是您正在寻找的功能:https://pursuit.purescript.org/packages/purescript-transformers/1.0.0/docs/Control.Monad.Writer#v:listens,如果我理解正确的话,您正在尝试做什么。
如果您对转换值不感兴趣,可以传入 id
,如果您只想获取累加值,那么 a <- snd <$> listens id
应该可以。
您无法访问当前累加器。 Writer
是只写的。如果您需要电流累加器,请使用 State
而不是 Writer
。 listen
只能给你一个计算完成的累加器。
我现在正在学习 Writer monad,我不确定在 do
块中读取 monad 的值和累加器是否正确。例如,在下面的 coltzSeq 函数中,我想读取数组累加器的长度作为函数的最终计算。是否有可能这样做或这是对 Writer 的不正确使用?显然,我可以让调用者读取最终数组的长度,或者我可以使用 State monad,但这只是我的练习。
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
编辑:
我尝试了 gb. 的建议并尝试使用类型为 (Monoid w, Monad m) => forall w m a b. MonadWriter w m => (w -> b) -> m a -> m (Tuple a b)
的 listens
函数。如果我们在此上下文中使用 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)
,returns一个值为当前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
编辑2:
我想出了我需要做什么。出于某种原因,我需要在使用 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
我认为 listens
是您正在寻找的功能:https://pursuit.purescript.org/packages/purescript-transformers/1.0.0/docs/Control.Monad.Writer#v:listens,如果我理解正确的话,您正在尝试做什么。
如果您对转换值不感兴趣,可以传入 id
,如果您只想获取累加值,那么 a <- snd <$> listens id
应该可以。
您无法访问当前累加器。 Writer
是只写的。如果您需要电流累加器,请使用 State
而不是 Writer
。 listen
只能给你一个计算完成的累加器。