from category theory. The Case for Arrows Can provide interface to true nondeterminisAc parallel computaAon. Think of both Monads and Arrows as interfaces to computaAon. They specify the general structure of a computaAon.
m where (>>=) :: m a -> (a -> m b) -> m b (>>) :: m a -> m b -> m b return :: a -> m a e.g : Maybe Kind = * ->* Arrows specifies an input and an output type. (Don't look at the definitions now. They are just to show that it takes two parameters) class Category a where idA :: a b b (>>>) :: a b c -> a c d -> a b d class Category a => Arrow a where arr :: (b -> c) -> a b c (>>>) :: a b c -> a c d -> a b d first :: a b c -> a (b,d) (c,d) second :: a b c -> a (d,b) (d,c) (***) :: a b c -> a b' c' -> a (b,b') (c,c') (&&&) :: a b c -> a b c' -> a b (c,c') e.g : functions (->) a b == a -> b Kind = *-> * -> *
Is there any relation between them? Category theory: A category is a mathematical structure defined as a collection of objects and associations, and obeys certain laws. A monad on a category is a functor with associated natural transformations. Haskell: It is a nice interface to structure your computations around so that very little assumptions are made. Implementation: class Category idA, (>>>) class Arrow arr, first class ArrowApply app (== Monad)
a function (pure) arr:: (b->c) a b c Compose two arrows together (>>>) :: a b c -> a c d -> a b d A product (***):: a b c -> a b’ c’ ->a (b,b’) (c,c’) f ***g = first f >>> second g A fanout &&& (&&&)::a b c -> a b c’ ->a b (c,c’)
b (>>>) :: a b c -> a c d ->a b d class Arrow a where arr :: (b -> c) -> a b c (>>>) :: a b c-> a c d -> a b d first :: a b c -> a (b,c) (b’,c) second ::a b c -> a (b,c) (b,c’) Product functor f x g = \(a,b) → (f a, g b) iden+ty idA >>> f = f >>> idA = f associativity (f >>> g ) >>> h = f >>> (g >>>h) functor-iden+ty pure id = idA functor-composi+on arr (g . f) = arr f >>> arr g extension first (arr f) = arr (f x id) functor first (f>>>g) = first f >>> first g exchange first f >>>arr (id x g) =arr (id x g) >>> first f unit first f >>> arr fst = arr fst >>> f associa+on first (first f)>>>arr assoc=arr assoc>>>first f
first? Functor : first (f>>>g) = first f >>> first g If it is not satisfied, then you will no longer be able to rely on their properties. import Control.Arrow cA w = Kleisli readFile >>> arr words >>> arr (filter (==w)) >>> arr length >>> Kleisli print cA2 w = Kleisli readFile >>> arr (words >>> (filter (==w)) >>> length) >>> Kleisli print xA = runKleisli (cA "module") "Cl.lhs" xA2 = runKleisli (cA2 "module") "Cl.lhs"
Arrow (->) where arr = id (>>>) = flip (.) first f = f x id we can define a state transformer as an arrow. type State z a b = (z,a) -> (z,b) instance Arrow (State z) where arr f = ST (id x f) ST f >>> ST g = ST (g . f) first (ST f) = ST (assoc . (f x id) . unassoc where unassoc (val, (state, c)) = ((val,state),c) fetch :: State z () z fetch = ST (\(s,()) -> (s,s)) store :: State z z () store = ST (\(s,s’) ->(s’,()))
app :: a (a c d, c) d Functions are an instance of ArrowApply instance ArrowApply (->) where app (f,c) = f c For any monad m, functions of type a - > m b are potential arrows newtype Kleisli m a b = K (a -> m b) instance Monad m => ArrowApply (Kleisli m) where arr f = K (\b -> return (f b)) K f >>> K g = K (\b -> f b >>= g) app = K ( \(K f, x) -> f x) Conversely ArrowApply can be used to construct a monad newtype ArrowApply a => ArrowMonad a b = M (a () b) instance ArrowApply a => Monad (ArrowMonad a) where return y = M (arr (\z -> y)) M m >>= f = M (m >>> arr (\y -> let M h = f y in (h, ())) >>> app) Hence ArrowApply == Monad All Arrows are not monads. Only those that support the ArrowApply interface (app)
Int -> a b Int -> a b Int addA f g = proc x -> do y <- f -< x z <- g -< x returnA -< y + z Same as addA f g = arr (\ x -> (x, x)) >>> first f >>> arr (\ (y, x) -> (x, y)) >>> first g >>> arr (\ (z, y) -> y + z) or addA f g = f &&& g >>> arr (\ (y, z) -> y + z) compare to monadic add addM a b = do x <- a y <- b return (x+y) proc () do, arrow head <- and arrow tail -<
a -> a idA = proc a -> returnA -< a plusOne :: Int -> Int plusOne = proc a -> returnA -< (a+1) *Main> idA 3 3 *Main> idA "foo" "foo" *Main> plusOne 3 4 plusOne = proc a -> returnA -< (a+1) plusTwo = proc a -> plusOne -< (a+1) plusFive = proc a -> do b <- plusOne -< a c <- plusOne -< b d <- plusOne -< c e <- plusOne -< d plusOne -< e
a => a b Int -> a b Int -> a b Int addA f g = proc x -> do y <- f -< x z <- g -< x returnA -< y + z Same as addA f g = arr (\ x -> (x, x)) >>> first f >>> arr (\ (y, x) -> (x, y)) >>> first g >>> arr (\ (z, y) -> y + z) or addA f g = f &&& g >>> arr (\ (y, z) -> y + z) compare to monadic add addM a b = do x <- a y <- b return (x+y)
Control.Monad import Control.Arrow count w = (>>= print) . liftM (length . filter (==w) . words ) . readFile cM w f = do x <- readFile f y <- return $ length (filter (==w) (words x)) print y Return () xM = cM "module" "Cl.lhs" a # b = a >>> b cA w = Kleisli readFile # arr words # arr (filter (==w)) # arr length # Kleisli print cA2 w = Kleisli readFile # arr (words # (filter (==w)) # length) # Kleisli print xA = runKleisli (cA "module") "Cl.lhs" xA2 = runKleisli (cA2 "module") "Cl.lhs"