3

I am wiriting simple application which has functionality of saving/loading its current state. Save function looks as below:

doSave :: BoardType -> Field -> [Char] -> Bool 
doSave board player fileName = do
    let x = encodeFile fileName (board :: BoardType, player :: Field)
    True -- there will be exception handling 

And my load function:

doLoad :: [Char] -> IO (BoardType, Field)
doLoad fileName = decodeFile fileName :: IO (BoardType, Field)

And there's my problem, after loading, I have IO (BoardType, Field) which does not fit my program and other functions which probably should not accept IO parameters. If I have followed this IO escalation, there would be all IOs in my application - is it necessary (or - normal in haskell language)?

And finally - is there a simple way I can get rid of this IO?

Maciej Dobrowolski
  • 11,561
  • 5
  • 45
  • 67
  • possible duplicate of [Why can't a monad be decomposed?](http://stackoverflow.com/questions/23901801/why-cant-a-monad-be-decomposed) – amalloy May 29 '14 at 00:21

2 Answers2

10

It takes a little while to get used to.

Some monads let you extract the "inner value" after some work, but IO never does.

There is no way that e.g. returning the system time can ever be "pure" so any calculations you make with it need to remain wrapped in IO.

However, that doesn't mean most of your code lives in IO-land.

main = do
  (bt, fld) <- doLoad "somefilename"
  let bResult = doSomethingPureWithBoard bt
  let fResult = doSomethingPureWithField fld
  let bt2     = updateBoard bt bResult fResult
  doSave "someFilename" bt2 fld                -- This should also be in IO

You can always call pure functions from IO, just not the other way around. The <- gives you the "unwrapped" values while you are in an IO function. Actually it's passing the results as parameters to the next "statement" - google around "de-sugaring do notation" and similar.

Richard Huxton
  • 21,516
  • 3
  • 39
  • 51
2

is it necessary (or - normal in haskell language)

Your application will typically have an outer wrapper of IO actions, beginning with main :: IO () and the repeatedly restricted code that has less and less privledges, until you're only dealing with pure code.

Don Stewart
  • 137,316
  • 36
  • 365
  • 468
  • these variables are fundamental part of my application (i.e. game board), so - should all of them be declared everywhere as `IO` ? – Maciej Dobrowolski May 28 '14 at 11:25
  • It depends how you want to structure the application. You could write an interpreter for your abstract command set to IO, that would again push the IO back to the outer layer. – Don Stewart May 28 '14 at 11:29