Search code examples
haskelliomonadspurely-functional

How to interact with pure algorithm in IO code


To illustrate the point with a trivial example, say I have implemented filter:

filter :: (a -> Bool) -> [a] -> [a]

And I have a predicate p that interacts with the real world:

p :: a -> IO Bool

How do it make it work with filter without writing a separate implementation:

filterIO :: (a -> IO Bool) -> [a] -> IO [a]

Presumably if I can turn p into p':

p': IO (a -> Bool)

Then I can do

main :: IO ()
main = do 
  p'' <- p'
  print $ filter p'' [1..100]

But I haven't been able to find the conversion.

Edited: As people have pointed out in the comment, such a conversion doesn't make sense as it would break the encapsulation of the IO Monad.

Now the question is, can I structure my code so that the pure and IO versions don't completely duplicate the core logic?


Solution

  • How do it make it work with filter without writing a separate implementation

    That isn't possible and the fact this sort of thing isn't possible is by design - Haskell places firm limits on its types and you have to obey them. You cannot sprinkle IO all over the place willy-nilly.

    Now the question is, can I structure my code so that the pure and IO versions don't completely duplicate the core logic?

    You'll be interested in filterM. Then, you can get both the functionality of filterIO by using the IO monad and the pure functionality using the Identity monad. Of course, for the pure case, you now have to pay the extra price of wrapping/unwrapping (or coerceing) the Identity wrapper. (Side remark: since Identity is a newtype this is only a code readability cost, not a runtime one.)

     ghci> data Color = Red | Green | Blue deriving (Read, Show, Eq)
    

    Here is a monadic example (note that the lines containing only Red, Blue, and Blue are user-entered at the prompt):

     ghci> filterM (\x -> do y<-readLn; pure (x==y)) [Red,Green,Blue]
     Red
     Blue
     Blue
     [Red,Blue] :: IO [Color]
    

    Here is a pure example:

     ghci> filterM (\x -> Identity (x /= Green)) [Red,Green,Blue]
     Identity [Red,Blue] :: Identity [Color]