State monad:如何在Haskell中“打印”中间值

时间:2017-07-05 16:47:43

标签: haskell

我是haskell的新手,我有以下代码

module StateTest where

import Control.Monad.State.Lazy

tick :: State Int Int
tick = do n <- get
          put (n+1)
          return n

plusOne :: Int -> Int
plusOne = execState tick

main = print $ plusOne 1

我想在put (n+1)之后打印状态值并继续像这样计算

tick = do n <- get
          put (n+1)
          print
          return n

整个代码如何看待这个?

2 个答案:

答案 0 :(得分:5)

如果要在状态计算中运行IO操作,可以更改tick的类型以返回StateT Int IO Int并使用liftIO。然后,您可以使用execStateT

运行它
import Control.Monad.State.Lazy
import Control.Monad.IO.Class (liftIO)

tick :: StateT Int IO Int
tick = do n <- get
          put (n+1)
          liftIO $ print (n+1)
          return n

plusOne :: Int -> IO Int
plusOne = execStateT tick

main = plusOne 1 >> pure ()

答案 1 :(得分:0)

另一种选择,因为你必须使用IO来打印中间状态的值,才能使用IORef。它是一个具有可更新值的容器。

module Main where

import Data.IORef

tick :: IORef Int -> IO (IORef Int)
tick ref = do
  modifyIORef' ref (+1)
  -- you can also print here since it is IO
  pure ref

main :: IO ()
main = do
  counter <- newIORef 0

  tick counter
  v2 <- readIORef counter
  print v2

  tick counter
  v2 <- readIORef counter
  print v2

然后,您可以使用ReaderT清除它。

module Main where

import Data.IORef
import Control.Monad.Reader

readerTick :: ReaderT (IORef Int) IO ()
readerTick = do
  ref <- ask
  -- can also print here with liftIO $ print ...
  liftIO $ modifyIORef' ref (+1)

main :: IO ()
main = do
  counter <- newIORef 0

  runReaderT readerTick counter
  v1 <- readIORef counter
  print v1

  runReaderT readerTick counter
  v2 <- readIORef counter
  print v2