In the Haskell Control.Arrow
documentation it talks about Kleisli arrows' relationship to monads, but it is not obvious to me how to use this. I have a function which I think fits with arrows except for it involving the IO monad, so I think Kleisli arrows may help.
Take the following function which returns pairs of original and modified filenames of a directory.
import System.Directory
import System.FilePath
datedFiles target = do
fns <- getDirectoryContents target
tms <- mapM (fmap show . getModificationTime) fns
return $
zip fns $
zipWith replaceBaseName fns $
zipWith (++) (map takeBaseName fns) tms
If I had to draw it out, it would be something like this:
I think it can benefit from the use of Kleisli arrows, but I don't know how. Can anyone provide guidance?
Monads are Functor
s from Hask, the category of Haskell types and functions, to Hask---an endofunctor. That means that some of the arrows in Hask look like a -> m b
for some Monad
m
. For a particular monad m
, the subcategory of Hask where arrows look like a -> m b
is the Kleisli category for m
.
We know it's a category because there's an identity arrow return :: a -> m a
and composition (>>>) :: (a -> m b) -> (b -> m c) -> (a -> m c)
defined like
(f >>> g) a = join (g <$> f a)
which is why we need this to be a Monad
---we use both return
and join
.
In Haskell, we can't just have a subcategory normally, but instead a newtype is used.
import Prelude hiding ((.), id)
import Control.Category
newtype Kleisli m a b = Kleisli { runKleisli :: a -> m b }
instance Monad m => Category (Kleisli m) where
id = Kleisli return
Kleisli g . Kleisli f = Kleisli (join . fmap g . f)
And then we can upgrade functions of type Monad m => a -> m b
to Kleisli m a b
s, arrows in a category, and compose them with (.)
arr :: Kleisli IO FilePath [String]
arr = Kleisli (mapM $ fmap show . getModificationTime) . Kleisli getDirectoryContents
Generally that's a bit syntactically noisy, though. The newtype is only valuable in order to use the Category
typeclass to overload id
and (.)
. Instead it's more likely that you'll see return
and (>=>)
which are equivalent to
return a = runKleisli (id a)
f >=> g = runKleisli $ Kleisli g . Kleisli f
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With