在下面的代码中,我管理一个拥有链接列表的游戏。 在游戏的每一步,我都会更改游戏状态,更新修改后的链接列表。
当我正在学习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
但是我在编译时遇到错误:
引起的
(MonadIO Data.Functor.Identity.Identity)
没有实例 使用liftIO
如果我尝试使用liftM
和lift
,我会遇到同样的错误。
我已经阅读了一些建议StateT
和ST
的问题,我还没有掌握这些问题。
我想知道我现在的技术状态是否注定会失败,而且我确实无法使用State
,而是StateT
/ ST
?
或者是否可以在getLine
monad中从State
获取值?
答案 0 :(得分:3)
正如@bheklilr在评论中所说,你不能使用来自State
的IO。其原因基本上是State
(这只是StateT
优于Identity
的简写)并不神奇,所以它不能再使用} p>
Identity
State
本身提供的新操作但是,第一点也暗示了解决方案:如果您将基本monad从Identity
更改为其他monad m
,那么您将能够使用{{1}提供的效果}}。在您的情况下,通过将m
设置为m
,您就可以了。
请注意,如果您的计算部分不需要执行IO,但需要访问您的州,您仍然可以通过使其类型为
IO
然后,您可以使用foo :: (Monad m) => Bar -> StateT Game m Baz
中的计算编写foo
,但其类型也明显表明它不可能执行任何IO(或其他任何基于monad特定的任何内容)。
您还在问题中提到了StateT Game IO
作为可能的解决方案。 ST
不是monad转换器,因此不允许您从某些基础monad导入效果。