Haskell 是否可以在状态monad中使用IO,而不使用StateT和ST

Haskell 是否可以在状态monad中使用IO,而不使用StateT和ST,haskell,monads,state-monad,Haskell,Monads,State Monad,在下面的代码中,我管理一个游戏,它拥有一个链接列表。 在游戏的每一步,我都会改变游戏状态,更新修改的链接列表 在学习State monad时,我试图将Statemonad技术应用到这个用例中 尽管如此,在每个回合中,我都需要使用getLine 这就给出了这样一个代码 {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE RecordWildCards #-} import Control.Monad import Control.Monad.State.Str

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

在学习State monad时,我试图将
State
monad技术应用到这个用例中

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

这就给出了这样一个代码

{-# 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
{-#语言名称字段puns}
{-#语言记录通配符}
进口管制
进口控制。单子。状态。严格
导入符合条件的数据。以列表形式列出
将符合条件的Control.Monad.IO.Class导入为IOClass
类型Node=Int
类型链接=(节点,节点)
类型Links=[Link]
类型网关=节点
类型Gateways=[Gateway]
数据游戏=游戏{nbNodes::Int,links::links,gateways::gateways}
computeNextTurn::状态游戏链接
computeNextTurn=do

正如@bheklir在他的评论中所说,你不能从
状态
使用IO。基本上,这是因为
State
(这只是
StateT
的缩写,而不是
Identity
)不是什么魔法,所以它只能使用

  • 您可以在其基本monad中执行的操作,
    Identity
  • 本身提供的新操作状态
然而,第一点也暗示了解决方案:如果您将基本monad从
Identity
更改为其他monad
m
,那么您就可以使用
m
提供的效果。在您的情况下,通过将
m
设置为
IO
,您就可以开始了

请注意,如果您的部分计算不需要执行IO,但需要访问您的状态,那么您仍然可以通过将其类型设置为

foo :: (Monad m) => Bar -> StateT Game m Baz
然后,您可以在
StateT Game IO
中使用计算编写
foo
,但它的类型也表明它不可能执行任何IO(或任何其他特定于基本monad的操作)


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

您不能在纯
状态下执行IO
monad。这是一个故意的限制。
StateT
的目的是专门从其他monad引入效果,例如
IO
。您需要将您的类型设置为
StateT Game IO Link
,然后才能像现在一样使用
liftIO getLine
。请记住,
states sa=StateT s Identity a
,其中
Identity
是Identity monad(本质上它不做任何事情)。您所做的一切就是将
标识
替换为
IO
,这样您就可以在
状态
中执行
IO
操作。它直接编译,现在我开始看到
状态
。谢谢你的回答/评论。