imagehaskellrepa

REPA: computeS and computeP?


I am trying this REPA library, and i want to process an image in both ways, parallel and sequentially.

I can read the image (with other library, DevIL) and process it with computeP (parallel). Here is the code (is from a example on the wiki of haskell).

import Foreign.Ptr
import System.Environment
import Data.Word
import Data.Array.Repa hiding ((++))
import Data.Array.Repa.IO.DevIL
import Data.Array.Repa.Repr.ForeignPtr

main :: IO () 
main = do
    [f] <- getArgs
    (RGB v) <- runIL $ readImage f
    rotated <- (computeP $ rot180 v) :: IO (Array F DIM3 Word8)
    runIL $ writeImage ("flip-"++f) (RGB rotated)

rot180 :: (Source r e) => Array r DIM3 e -> Array D DIM3 e
rot180 g = backpermute e flop g
    where
        e@(Z :. x :. y :. _)   = extent g
        flop (Z :. i         :. j         :. k) =
             (Z :. x - i - 1 :. y - j - 1 :. k)

Now i want to do it sequentially changing "computeP" with "computeS". But, when i try to compile it, this error appears:

Couldn't match expected type ‘IO (Array F DIM3 Word8)’
                with actual type ‘Array r20 DIM3 Word8’
    In a stmt of a 'do' block:
      rotated <- (computeS $ rot180 v) :: IO (Array F DIM3 Word8)

As you can probably guess, i am new at functional programming. I dont know why this error is happening. Any help would be great.

Thanks in advance.


Solution

  • You are doing everything almost right. The error you are getting is due to the fact that computeP is monadic, while computeS is not. If you compare their type signatures closely the difference that is biting you is the Monad m restriction and return type m (Array r2 sh e) for computeP vs (Array r2 sh e) for computeS. So long story short, just change

    rotated <- (computeP $ rot180 v) :: IO (Array F DIM3 Word8)
    

    to:

    let rotated = (computeS $ rot180 v) :: (Array F DIM3 Word8)
    

    The reason why parallel computation in Repa must be monadic has to do partially with lazyness, but mostly with Repa's inability to deal with nested parallelism. Sequential property of a Monad solves it for the most part:

    rotated1 <- (computeP $ rot180 v) :: IO (Array F DIM3 Word8)
    rotated2 <- (computeP $ rot180 rotated1) :: IO (Array F DIM3 Word8)
    

    Using do notation above (and the fact that computeP uses deepSeqArray under the hood) makes sure rotated1 is evaluated before getting to second call to computeP. But since computeS doesn't use Repa's parallel scheduler it doesn't have that issue, thus doesn't need to use that property of a Monad and this code will work just fine:

    let rotated1 = computeS (rot180 v) :: Array F DIM3 Word8
        rotated2 = computeS (rot180 rotated1) :: Array F DIM3 Word8