在StateT中访问'a'

Kev*_*ith 0 haskell

我正在尝试编写一个函数,StateT只是为了更多地了解它.

f,我想访问Int最后一个类型的参数StateT [Int] IO 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) 
Run Code Online (Sandbox Code Playgroud)

以下是我想称之为:

let x = return 55 :: StateT [Int] IO Int

参考runStateT:

*Main> :t runStateT
runStateT :: StateT s m a -> s -> m (a, s)
Run Code Online (Sandbox Code Playgroud)

我希望能运行它:

runStateT (f x) [1,2,3]

从GHCI获得以下内容,即IO (Int, [Int])获得打印:

(56, [2,3])
Run Code Online (Sandbox Code Playgroud)

由于内a,即55,+ 1即从[1,2,3],退货(56, [2,3]).

如何编写上述函数,获取访问权限a

ram*_*ion 5

好的,这就是你想要的:

>>> let x = return 55 ::  StateT [Int] IO Int
>>> runStateT (f x) [1,2,3]
(56, [2,3])
Run Code Online (Sandbox Code Playgroud)

所以让我们从那里开始倒退.

从使用上f,我们可以推断出它的类型 -

f :: StateT [Int] IO Int -> StateT [Int] IO Int
Run Code Online (Sandbox Code Playgroud)

请注意f问题中给定类型的差异- 即f类型值之间的函数StateT [Int] IO Int,而不是该类型的值.

要定义f,我们需要(>>=) :: Monad m => m a -> (a -> m b) -> m b.这将允许我们获取类型的输入StateT [Int] IO IntInt在输入计算上运行一些计算.

f x = x >>= \i -> state (splitAt 1) >>= \[j] -> return (i + j)
Run Code Online (Sandbox Code Playgroud)

或者,使用do-notation:

f x = do
  i <- x
  [j] <- state (splitAt 1)
  return (i + j)
Run Code Online (Sandbox Code Playgroud)

这给了我们我们想要的结果.

虽然这有效,但它非常非惯用.不是将monadic值作为输入传递给函数并将它们绑定到函数内部,而是使用外部的bind运算符(>>=)来定义采用常规值并返回monadic值的函数.

所以定义更为正常

shiftAdd :: Int -> StateT [Int] IO Int
shiftAdd i = do
  [j] <- state (splitAt 1)
  return (i + j)
Run Code Online (Sandbox Code Playgroud)

所以现在我们不仅可以运行

>>> runStateT (shiftAdd 55) [1,2,3]
(56,[2,3])
Run Code Online (Sandbox Code Playgroud)

但是也

>>> runStateT (shiftAdd 55 >>= shiftAdd >>= shiftAdd)
(61,[])
Run Code Online (Sandbox Code Playgroud)

它仍然不像以下那样惯用:

  • 我通过使用它不必要的部分splitAt(如果状态列表为空,它将抛出异常)
  • 它是不必要的特定(根本不使用IO,但我们不能将它与其他基础monad一起使用)

解决这个问题给了我们:

shiftAdd' :: (Monad m, Num a) => a -> StateT [a] m a
shiftAdd' i = state $ \js -> case js of
  [] -> (i, [])
  j : js -> (i + j, js)
Run Code Online (Sandbox Code Playgroud)

哪个工作得很好:

>>> 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,[])
Run Code Online (Sandbox Code Playgroud)