{-# LANGUAGE RankNTypes, GADTs, NoMonomorphismRestriction #-}
-- |
-- Module : AI.CV.Processor
-- Copyright : (c) Noam Lewis, 2010
-- License : BSD3
--
-- Maintainer : Noam Lewis <jones.noamle@gmail.com>
-- Stability : experimental
-- Portability : tested on GHC only
--
-- Framework for expressing monadic actions that require initialization and finalizers.
-- This module provides a *functional* interface for defining and chaining a series of processors.
--
-- Motivating example: bindings to C libraries that use functions such as: f(foo *src, foo *dst),
-- where the pointer `dst` must be pre-allocated. In this case we normally do:
--
-- foo *dst = allocateFoo();
-- ...
-- while (something) {
-- f(src, dst);
-- ...
-- }
-- releaseFoo(dst);
--
-- You can use the 'runUntil' function below to emulate that loop.
--
-- Processor is an instance of Category, Functor, Applicative and Arrow.
module AI.CV.Processor where
import Prelude hiding ((.),id)
import Control.Category
import Control.Applicative hiding (empty)
import Control.Arrow
import Control.Monad(liftM, join)
-- | The type of Processors
--
-- The semantic model is:
--
-- > [[ Processor m o a b ]] = a -> b
--
-- The idea is that the monad m is usually IO, and that a and b are usually pointers.
-- It is meant for functions that require a pre-allocated output pointer to operate.
--
-- * a, b = the input and output types of the processor (think a -> b)
--
-- * m = monad in which the processor operates
--
-- * x = type of internal state
--
-- The arguments to the constructor are:
--
-- 1. Processing function: Takes input and internal state, and returns new internal state.
--
-- 2. Allocator for internal state (this is run only once): Takes (usually the first) input, and returns initial internal state.
--
-- 3. Convertor from state x to output b: Takes internal state and returns the output.
--
-- 4. Releaser for internal state (finalizer, run once): Run after processor is done being used, to release the internal state.
--
data Processor m a b where
Processor :: Monad m => (a -> x -> m x) -> (a -> m x) -> (x -> m b) -> (x -> m ()) -> (Processor m a b)
processor :: (Monad m) =>
(a -> x -> m x) -> (a -> m x) -> (x -> m b) -> (x -> m ())
-> Processor m a b
processor = Processor
-- | Chains two processors serially, so one feeds the next.
chain :: (Monad m) => Processor m a b' -> Processor m b' b -> Processor m a b
chain (Processor pf1 af1 cf1 rf1) (Processor pf2 af2 cf2 rf2) = processor pf3 af3 cf3 rf3
where pf3 a (x1,x2) = do
x1' <- pf1 a x1
b' <- cf1 x1
x2' <- pf2 b' x2
return (x1', x2')
af3 a = do
x1 <- af1 a
b' <- cf1 x1
x2 <- af2 b'
return (x1,x2)
cf3 (_,x2) = do
b <- cf2 x2
return b
rf3 (x1,x2) = do
rf2 x2
rf1 x1
-- | A processor that represents two sub-processors in parallel (although the current implementation runs them
-- sequentially, but that may change in the future)
parallel :: (Monad m) => Processor m a b -> Processor m c d -> Processor m (a,c) (b,d)
parallel (Processor pf1 af1 cf1 rf1) (Processor pf2 af2 cf2 rf2) = processor pf3 af3 cf3 rf3
where pf3 (a,c) (x1,x2) = do
x1' <- pf1 a x1
x2' <- pf2 c x2
return (x1', x2')
af3 (a,c) = do
x1 <- af1 a
x2 <- af2 c
return (x1,x2)
cf3 (x1,x2) = do
b <- cf1 x1
d <- cf2 x2
return (b,d)
rf3 (x1,x2) = do
rf2 x2
rf1 x1
-- | Constructs a processor that: given two processors, gives source as input to both processors and runs them
-- independently, and after both have have finished, outputs their combined outputs.
--
-- Semantic meaning, using Arrow's (&&&) operator:
-- [[ forkJoin ]] = &&&
-- Or, considering the Monad instance of functions (which are the semantic meanings of a processor):
-- [[ forkJoin ]] = liftM2 (,)
-- Alternative implementation to consider: f &&& g = (,) <&> f <*> g
forkJoin :: (Monad m) => Processor m a b -> Processor m a b' -> Processor m a (b,b')
forkJoin (Processor pf1 af1 cf1 rf1) (Processor pf2 af2 cf2 rf2) = processor pf3 af3 cf3 rf3
where pf3 a (x1,x2) = do
x1' <- pf1 a x1
x2' <- pf2 a x2
return (x1', x2')
af3 a = do
x1 <- af1 a
x2 <- af2 a
return (x1,x2)
cf3 (x1,x2) = do
b <- cf1 x1
b' <- cf2 x2
return (b,b')
rf3 (x1,x2) = do
rf2 x2
rf1 x1
-------------------------------------------------------------
-- | The identity processor: output = input. Semantically, [[ empty ]] = id
empty :: Monad m => Processor m a a
empty = processor pf af cf rf
where pf _ = do return
af = do return
cf = do return
rf _ = do return ()
instance Monad m => Category (Processor m) where
(.) = flip chain
id = empty
instance Monad m => Functor (Processor m a) where
-- |
-- > [[ fmap ]] = (.)
--
-- This could have used fmap internally as a Type Class Morphism, but monads
-- don't neccesary implement the obvious: fmap = liftM.
fmap f (Processor pf af cf rf) = processor pf af cf' rf
where cf' x = liftM f (cf x)
-- | Splits (duplicates) the output of a functor, or on this case a processor.
split :: Functor f => f a -> f (a,a)
split = (join (,) <$>)
-- | 'f --< g' means: split f and feed it into g. Useful for feeding parallelized (***'d) processors.
-- For example, a --< (b &&& c)
(--<) :: (Functor (cat a), Category cat) => cat a a1 -> cat (a1, a1) c -> cat a c
f --< g = split f >>> g
infixr 1 --<
instance (Monad m) => Applicative (Processor m a) where
-- |
-- > [[ pure ]] = const
pure b = processor pf af cf rf
where pf _ = do return
af _ = do return ()
cf _ = do return b
rf _ = do return ()
-- |
-- [[ pf <*> px ]] = \a -> ([[ pf ]] a) ([[ px ]] a)
-- (same as '(<*>)' on functions)
(<*>) (Processor pf af cf rf) (Processor px ax cx rx) = processor py ay cy ry
where py a (stateF, stateX) = do
f' <- pf a stateF
x' <- px a stateX
return (f', x')
ay a = do
stateF <- af a
stateX <- ax a
return (stateF, stateX)
-- this is the only part that seems specific to <*>
cy (stateF, stateX) = do
b2c <- cf stateF
b <- cx stateX
return (b2c b)
ry (stateF, stateX) = do
rx stateX
rf stateF
-- | A few tricks by Saizan from #haskell to perhaps use here:
-- first f = (,) <$> (arr fst >>> f) <*> arr snd
-- arr f = f <$> id
-- f *** g = (arr fst >>> f) &&& (arr snd >>> g)
instance Monad m => Arrow (Processor m) where
arr = flip liftA id
(&&&) = forkJoin
(***) = parallel
first = (*** id)
second = (id ***)
-------------------------------------------------------------
-- | Runs the processor once: allocates, processes, converts to output, and deallocates.
run :: (Monad m) => Processor m a b -> a -> m b
run = runWith id
-- | Keeps running the processing function in a loop until a predicate on the output is true.
-- Useful for processors whose main function is after the allocation and before deallocation.
runUntil :: (Monad m) => Processor m a b -> a -> (b -> m Bool) -> m b
runUntil (Processor pf af cf rf) a untilF = do
x <- af a
let repeatF y = do
y' <- pf a y
b <- cf y'
b' <- untilF b
if b' then return b else repeatF y'
d <- repeatF x
rf x
return d
-- | Runs the processor once, but passes the processing + conversion action to the given function.
runWith :: Monad m => (m b -> m b') -> Processor m a b -> a -> m b'
runWith f (Processor pf af cf rf) a = do
x <- af a
b' <- f (pf a x >>= cf)
rf x
return b'