haskellstate-monadjuicy-pixels

how do I write pixels with JuicyPixels? (in ST monad)


I'd like to write some pixels into an image, and write the image to disk. I've been following the advice I've heard from many Haskellers to follow the type signatures, and essentially play "type tetris" until I get where I'm going. Its mostly working for me, but I'm running into some trouble.

To write a pixel, there's a function:

writePixel :: PrimMonad m => MutableImage (PrimState m) a -> Int -> Int -> a -> m ()

I can tell from reading the signature that I need to pass it a MutableImage, so I look for a function that with a MutableImage:

createMutableImage :: (PrimMonad m, Pixel px) => Int -> Int -> px -> m (MutableImage (PrimState m) px)

this seems to run inside some kind of state monad.

writeIt = runST $ createMutableImage 100 100 (100::Pixel8)  >>=
                  freezeImage

This works, and returns nice grey image I can write to disk. But I have no idea how to get a hold of the MutableImage so I can write pixels to it! Simply interposing the writePixel call gives me an error I can't quite make out:

writeIt = runST $ createMutableImage 100 100 (100::Pixel8)  >>=
                  writePixel 100 100 255 >>=
                  freezeImage

Results in:

Couldn't match type `MutableImage (PrimState (GHC.ST.ST s)) px0'
              with `()'
Expected type: () -> GHC.ST.ST s (Image px0)
  Actual type: MutableImage (PrimState (GHC.ST.ST s)) px0
               -> GHC.ST.ST s (Image px0)
In the second argument of `(>>=)', namely `freezeImage'
In the second argument of `($)', namely
  `createMutableImage 100 100 (100 :: Pixel8)
   >>= writePixel 100 100 255
   >>= freezeImage'
In the expression:
  runST
  $ createMutableImage 100 100 (100 :: Pixel8)
    >>= writePixel 100 100 255
    >>= freezeImage

I can tell from the writePixel signature that I'm missing the first argument for writePixel. How do I get a reference to the MutableImage so I can write to it? And more importantly, how do I get a foothold on the types inside this monad so I can work this stuff out for myself?


Solution

  • You have done it basically correctly, you just mixed up the positions of the arguements:

    writeIt = runST $ do 
      pic <- createMutableImage 100 100 (100::Pixel8)
      writePixel pic 100 100 255 
      freezeImage pic 
    

    Or if you don't like do notation:

    writeIt = runST $
      createMutableImage 100 100 (100::Pixel8) >>=
      \pic -> writePixel pic 100 100 255       >>=
      \nothing -> freezeImage pic 
    

    What you had written would be equivalent to

    writeIt = runST $ createMutableImage 100 100 (100::Pixel8)  >>=
                      \pic -> writePixel 100 100 255 pic        >>=
                      \nothing -> freezeImage nothing 
    

    but this is completely wrong, since nothing has type () and pic should be in the first arguement position.