Reputation: 39906
In the code below I manage a game, which owns a list of links. At each step of the game, I change the game state updating the list of links modified.
As I am learning the State monad, I was trying to apply the State
monad technique to this use case.
Nonetheless, at each turn, I need to get a piece of info from IO, using getLine
this gives such a code
{-# 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
However I get an error at compilation:
No instance for
(MonadIO Data.Functor.Identity.Identity)
arising from a use ofliftIO
and I get the same style of error, if I try to use liftM
and lift
.
I have read some questions that are suggesting StateT
and ST
, which I don't grasp yet.
I am wondering if my current techique with a simple State is doomed to fail, and that indeed I can not use State
, but StateT
/ ST
?
Or is there a possible operation to simply get the value from getLine
, inside the State
monad ?
Upvotes: 3
Views: 1229
Reputation: 27626
As @bheklilr said in his comment, you can't use IO from State
. The reason for that, basically, is that State
(which is just shorthand for StateT
over Identity
) is no magic, so it's not going to be able to use anything more than
Identity
State
itselfHowever, that first point also hints at the solution: if you change the base monad from Identity
to some other monad m
, then you get the capability to use the effects provided by m
. In your case, by setting m
to IO
, you're good to go.
Note that if you have parts of your computation that don't need to do IO, but require access to your state, you can still express this fact by making their type something like
foo :: (Monad m) => Bar -> StateT Game m Baz
You can then compose foo
with computations in StateT Game IO
, but its type also makes it apparent that it can't possibly do any IO (or anything else base monad-specific).
You also mentioned ST
in your question as possible solution. ST
is not a monad transformer and thus doesn't allow you to import effects from some base monad.
Upvotes: 4