我试图用StateT编写一个函数,只是为了了解更多有关它的信息。
在f中,我想访问上一种类型的StateT [Int] IO Int参数中的Int
f :: StateT [Int] IO Int
f = state $ \xs -> update (error "I want a") xs
update :: Int -> [Int] -> (Int, [Int])
update x [] = (x, [])
update x (y:ys) = (x+y, ys) 我想这样称呼它:
let x = return 55 :: StateT [Int] IO Int
参考runStateT
*Main> :t runStateT
runStateT :: StateT s m a -> s -> m (a, s)我想要运行它:
runStateT (f x) [1,2,3]
要从GHCI获取以下内容,即打印IO (Int, [Int]):
(56, [2,3])因为内部a,即55,+ 1,即从[1,2,3]返回(56, [2,3])。
如何编写上面的函数,访问a
发布于 2017-05-17 15:38:50
好吧,下面是你想要的:
>>> let x = return 55 :: StateT [Int] IO Int
>>> runStateT (f x) [1,2,3]
(56, [2,3])因此,让我们从这倒向后看。
从f的使用,我们可以推断出它的类型-
f :: StateT [Int] IO Int -> StateT [Int] IO Int注意问题中给定的f类型的不同--即f是StateT [Int] IO Int类型的值之间的函数,而不是该类型的值。
要定义f,我们需要(>>=) :: Monad m => m a -> (a -> m b) -> m b。这将允许我们接受StateT [Int] IO Int类型的输入,并在输入计算的Int上运行一些计算。
f x = x >>= \i -> state (splitAt 1) >>= \[j] -> return (i + j)或者使用do-表示法:
f x = do
i <- x
[j] <- state (splitAt 1)
return (i + j)这正好给了我们想要的结果。
虽然这是有效的,但它是高度非惯用的。与将一元值作为输入传递给函数并将其绑定到函数内部,使用外部的绑定运算符(>>=)定义接受常规值并返回一元值的函数要常见得多。
所以更正常的是
shiftAdd :: Int -> StateT [Int] IO Int
shiftAdd i = do
[j] <- state (splitAt 1)
return (i + j)所以现在我们不仅可以
>>> runStateT (shiftAdd 55) [1,2,3]
(56,[2,3])但同时也
>>> runStateT (shiftAdd 55 >>= shiftAdd >>= shiftAdd)
(61,[])它仍然不像它可能是那样的习语:
splitAt进行了不必要的局部处理(如果状态列表为空,它将引发异常)解决这个问题会让我们:
shiftAdd' :: (Monad m, Num a) => a -> StateT [a] m a
shiftAdd' i = state $ \js -> case js of
[] -> (i, [])
j : js -> (i + j, js)效果很好:
>>> runStateT (return 55 >>= shiftAdd') [1,2,3]
(56,[2,3])
>>> runStateT (return 55 >>= shiftAdd' >>= shiftAdd' >>= shiftAdd') [1,2,3]
(61,[])
>>> runStateT (return 55 >>= shiftAdd' >>= shiftAdd' >>= shiftAdd') []
(55,[])https://stackoverflow.com/questions/44025232
复制相似问题