I'm using a graphic library in Haskell called Threepenny-GUI. In this library the main function returns a UI
monad object. This causes me much headache as when I attempt to unpack IO
values into local variables I receive errors complaining of different monad types.
Here's an example of my problem. This is a slightly modified version of the standard main function, as given by Threepenny-GUI's code example:
main :: IO ()
main = startGUI defaultConfig setup
setup :: Window -> UI ()
setup w = do
labelsAndValues <- shuffle [1..10]
shuffle :: [Int] -> IO [Int]
shuffle [] = return []
shuffle xs = do randomPosition <- getStdRandom (randomR (0, length xs - 1))
let (left, (a:right)) = splitAt randomPosition xs
fmap (a:) (shuffle (left ++ right))
Please notice the fifth line:
labelsAndValues <- shuffle [1..10]
Which returns the following error:
Couldn't match type ‘IO’ with ‘UI’
Expected type: UI [Int]
Actual type: IO [Int]
In a stmt of a 'do' block: labelsAndValues <- shuffle [1 .. 10]
As to my question, how do I unpack the IO
function using the standard arrow notation (<-
), and keep on having these variables as IO ()
rather than UI ()
, so I can easily pass them on to other functions.
Currently, the only solution I found was to use liftIO
, but this causes conversion to the UI
monad type, while I actually want to keep on using the IO
type.
A do
block is for a specific type of monad, you can't just change the type in the middle.
You can either transform the action or you can nest it inside the do
. Most times transformations will be ready for you. You can, for instance have a nested do
that works with io
and then convert it only at the point of interaction.
In your case, a liftIOLater
function is offered to handle this for you by the ThreePennyUI package.
liftIOLater :: IO () -> UI ()
Schedule an IO action to be run later.
In order to perform the converse conversion, you can use runUI
:
runUI :: Window -> UI a -> IO a
Execute an UI action in a particular browser window. Also runs all scheduled IO action.