Haskell do clause with multiple monad types

I am using a graphics library in Haskell called Threepenny-GUI . In this library, the main function returns a UI monad object. This causes me a big headache when I try to unpack IO values โ€‹โ€‹into local variables. I get errors complaining about different types of monad.

Here is an example of my problem. This is a slightly modified version of the standard core function, as shown in the 3penny-GUI 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)) 

Pay attention to the fifth line:

 labelsAndValues <- shuffle [1..10] 

It 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 for my question, how can I unpack the IO function using the standard arrow notation ( <- ), and continue to have these variables as IO () , not UI () , so I can easily pass them to other functions.

Currently, the only solution I found was to use liftIO , but this leads to conversion to the UI monad type, while I actually want to continue using the IO type.

+6
source share
3 answers

A do block is for a specific type of monad, you cannot just change the type in the middle.

You can either transform the action, or you can paste it inside do . In most cases, the conversion will be ready for you. You can, for example, have a nested do that works with io , and then convert it only at the point of interaction.

In your case, the liftIOLater function is liftIOLater to handle this for you with ThreePennyUI.

liftIOLater :: IO () -> UI ()

Schedule an I / O action that will be launched later.

To perform the inverse conversion, you can use runUI :

runUI :: Window -> UI a -> IO a

Performing a user interface action in a specific browser window. All scheduled I / O actions are also performed.

+7
source

This is a more detailed comment - it does not address the main question, but your implementation of shufffle . There are 2 problems:

  • Your implementation is inefficient - O (n ^ 2).
  • IO not suitable for him - shuffle has no common side effects, he just needs a source of randomness.

There are several solutions for (1): one is to use Seq and index , which is O (log n), which would make shuffle O (n log n). Or you can use ST arrays and one of the standard algorithms to get O (n).

For (2), all you need is a cutting random generator, rather than full power IO . There is already a good MonadRandom library that defines a monad (and type class) for randomized computing. And another package already provides the shuffle function. Since IO is an instance of MonadRandom , you can simply use shuffle directly as a replacement for your function.

+4
source

Under the cover do is just syntactic sugar for โ†’ = (bind) and let:

 do { x<-e; es } = e >>= \x -> do { es } do { e; es } = e >> do { es } do { e } = e do {let ds; es} = let ds in do {es} 

And binding type:

 (>>=) :: Monad m => a -> (a -> mb) -> mb 

So yes, he only "supports" one Monad

+2
source

Source: https://habr.com/ru/post/989526/


All Articles