Haskell 是否可以在状态monad中使用IO,而不使用StateT和ST
在下面的代码中,我管理一个游戏,它拥有一个链接列表。 在游戏的每一步,我都会改变游戏状态,更新修改的链接列表 在学习State monad时,我试图将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技术应用到这个用例中
尽管如此,在每个回合中,我都需要使用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
更改为其他monadm
,那么您就可以使用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
操作。它直接编译,现在我开始看到状态
。谢谢你的回答/评论。