是否可以在不使用 StateT 和 ST 的情况下在 State monad 中使用 IO

Is it possible to use IO inside State monad, without using StateT and ST

在下面的代码中,我管理了一个游戏,它拥有一个链接列表。 在游戏的每一步,我都会更改游戏状态,更新已修改的链接列表。

在学习 State monad 时,我试图将 State monad 技术应用于此用例。

尽管如此,在每个回合,我都需要使用 getLine

从 IO 获取一条信息

这给出了这样一个代码

{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE RecordWildCards #-}
import Control.Monad
import Control.Monad.State.Strict
import qualified Data.List as List
import qualified Control.Monad.IO.Class as IOClass

type Node = Int 
type Link = (Node,Node)
type Links = [Link]
type Gateway = Node
type Gateways = [Gateway]

data Game = Game { nbNodes :: Int, links :: Links, gateways :: Gateways }

computeNextTurn :: State Game Link
computeNextTurn = do
    g <- get
    inputLine <- IOClass.liftIO getLine -- this line causes problem
    let node = read inputLine :: Int
    let game@(Game _ ls gs) = g
    let linkToSever = computeLinkToSever game node
    let ls' = List.delete linkToSever ls
    let game' = game{links = ls'}
    put game'
    return linkToSever

computeAllTurns :: State Game Links
computeAllTurns = do
            linkToSever <- computeNextTurn
            nextGames <- computeAllTurns
            return (linkToSever : nextGames)

computeLinkToSever :: Game -> Node -> Link
computeLinkToSever _ _ = (0,1) -- just a dummy value
        -- this function doesnt compute really anything for the moment
        -- but it will depend on the value of node i got from IO

但是我在编译时遇到错误:

No instance for (MonadIO Data.Functor.Identity.Identity) arising from a use of liftIO

如果我尝试使用 liftMlift.

,我会得到相同类型的错误

我已经阅读了一些建议 StateTST 的问题,但我还没有掌握。

我想知道我目前使用简单状态的技术是否注定要失败,而且我确实不能使用 State,但是 StateT / ST ?

或者在 State monad 中是否可以简单地从 getLine 获取值?

正如@bheklilr 在他的评论中所说,您不能使用 State 中的 IO。基本上,原因是 State(对于 StateTIdentity 只是 shorthand)不是魔法,所以它不能使用任何东西超过

  • 你已经可以在它的基础 monad 中做些什么,Identity
  • State自己提供的新操作

但是,第一点也暗示了解决方案:如果您将基础 monad 从 Identity 更改为其他一些 monad m,那么您就可以使用 m。在您的情况下,通过将 m 设置为 IO,您就可以开始了。

请注意,如果您的部分计算不需要执行 IO,但需要访问您的状态,您仍然可以通过使它们的类型像

foo :: (Monad m) => Bar -> StateT Game m Baz

然后您可以将 fooStateT Game IO 中的计算组合起来,但它的类型也表明它不可能执行任何 IO(或任何其他特定于 base monad 的操作)。

您还在问题中提到 ST 作为可能的解决方案。 ST 不是 monad 转换器,因此不允许您从某些基本 monad 导入效果。