1

I'm creating a game with Gloss. I have this function :

block :: IO (Maybe Picture)
block = loadJuicyPNG "block.png"

How do I take this IO (Maybe Picture) and turn it into a Picture?

  • 1
    Technically, you have an `IO` action, not a function. You call `loadJuicyPNG`, and it returns an `IO` action that, when executed, *might* result in a `Picture`. – chepner Dec 07 '18 at 15:19
  • 3
    Once you're working in the `IO` monad, you're not (normally) meant to get out of it. Whatever operation you want to apply to the resulting `Picture` (if any) should be applied within the `IO` monad. Perhaps you should clarify what you want to do with that picture in your question... – jub0bs Dec 07 '18 at 15:45
  • Just as a side note, you might also be interested in monad transformers in this case, such as `MaybeT IO Picture`. [A good read here](https://stackoverflow.com/a/32582127/4543207) – Redu Dec 07 '18 at 23:49
  • 1
    an `IO (Maybe Picture) -> Picture` type value can not exist, but `IO (IO (Maybe Picture)) -> IO Picture` can. – Will Ness Dec 08 '18 at 10:09
  • @WillNess `dontDoThis :: IO (Maybe Picture) -> Picture` `dontDoThis = fromJust . unsafePerformIO`. Technically, it can exist; it's just almost certainly a bad idea. – Joseph Sible-Reinstate Monica Dec 11 '18 at 03:13
  • @JosephSible yeah. Since I am not a Haskell expert, I pretend that it does not exist. – Will Ness Dec 11 '18 at 07:03

2 Answers2

6

You need to bind the value. This is done either with the bind function (>>=), or by do-notation:

main :: IO ()
main = do
    pic <- block
    case pic of
        Just p -> ...   -- loading succeeded, p is a Picture
        Nothing -> ...  -- loading failed

It is a Maybe Picture because the loading might fail, and you have to handle that possible failure somehow.

Bartek Banachewicz
  • 38,596
  • 7
  • 91
  • 135
  • I'm still having trouble using that picture that i'm loading on my other functions. How do I do that? – Bruno Pinto Dec 07 '18 at 15:43
  • 1
    @BrunoPinto Can you be more specific? If you have a function `foo :: Picture -> String`, for instance, you would just write `foo p`. – chepner Dec 07 '18 at 18:05
  • It's hard to explain, so I understand if you can't help me, but basically I need to use the the picture in several functions outside of the IO and I don't know how to do that. I'm not sure what to write in front of `Just p ->` and `Nothing ->` so that's probably what I'm missing here. – Bruno Pinto Dec 07 '18 at 18:47
  • 1
    After `Just p -> ...`, you just write some regular pure code that uses `p`. Keep in mind that ultimately, the entire `do` block creates a single `IO` action that *encapsulates* your other code. Put another way, if you wrote here `... Just p -> foo p`, don't think of that as immediately running `foo` on a `Picture`; instead, you are creating an `IO` action that, when the Haskell runtime executes it, extracts `pic` from the `block` action and calls `foo` on it *for* you. – chepner Dec 07 '18 at 20:01
  • 1
    @BrunoPinto It's fine to call those other functions that don't have `IO` in them in place of the three dots; it's fine to use pure functions within IO, not so much otherwise. So your loading must always reside in the main, and then the code using it must follow, which kind of of makes sense when you think about it. – Bartek Banachewicz Dec 07 '18 at 20:10
1

This is basically the same answer as Bartek's, but using a different approach.

Let's say you have a function foo :: Picture -> Picture the transforms a picture in some way. It expects a Picture as an argument, but all you have is block :: IO (Maybe Picture); there may or may not be a picture buried in there, but it's all you have.

To start, let's assume you have some function foo' :: Maybe Picture -> Maybe Picture. It's definition is simple:

foo' :: Maybe Picture -> Maybe Picture
foo' = fmap foo

So simple, in fact, that you never actually write it; wherever you would use foo', you just use fmap foo directly. What this function does, you will recall, is return Nothing if it gets Nothing, and return Just (foo x) if it gets some value Just x.

Now, given that you have foo', how do you apply it to the value buried in the IO type? For that, we'll use the Monad instanced for IO, which provides us with two functions (types here specialized to IO):

return :: a -> IO a
(>>=) :: IO a -> (a -> IO b) -> IO b

In our case, we recognize that both a and b are Maybe Picture. If foo' :: Maybe Picture -> Maybe Picture, then return . foo' :: Maybe Picture -> IO (Maybe Picture). That means we can finally "apply" foo to our picture:

> :t block  >>= return . (fmap foo)
block  >>= return . (fmap foo) :: IO (Maybe Picture)

But we aren't really applying foo ourselves. What we are really doing is lifting foo into a context where, once block is executed, foo' can be called on whatever block produces.

chepner
  • 497,756
  • 71
  • 530
  • 681