If I have the following two Kleisli arrows:
stdoutProcessA :: Kleisli Maybe String (IO String)
writeToFileA :: Kleisli Maybe (FilePath, String) (IO ())
I would like to be able to write someting like:
compile = proc src -> do
output <- stdoutProcessA -< "..."
writeToFileA -< ("...", output)
...
which of course doesn't work, because String
doesn't match with IO String
. On the other hand, it is possible to define both stdoutProcessA
and writeToFileA
to be of type Kleisli IO ...
, but then I wouldn't be able to compose them with arrows of type Kleisli Maybe ...
, which I need for other things.
I am not very experienced with arrows yet, so I'm probably missing something obvious. How would one go about doing the above?
These arrows don't make much sense to me:
stdoutProcessA :: Kleisli Maybe String (IO String)
writeToFileA :: Kleisli Maybe (FilePath, String) (IO ())
They represent functions with result Maybe (IO a)
, when you probably meant IO (Maybe a)
. The latter type represents IO
actions which may fail, while in the former the failure or success cannot depend on the IO
at all.
The correct way to combine IO
and Maybe
is to use the MaybeT
monad transformer, like so:
stdoutProcessA :: Kleisli (MaybeT IO) String String
writeToFileA :: Kleisli (MaybeT IO) (FilePath, String) ()
If you write your other arrows as Monad m => Kleisli (MaybeT m) a b
, they should compose nicely with these ones without any lifting. Alternatively, you can use
lift' :: Monad m => Kleisli Maybe a b -> Kleisli (MaybeT m) a b
lift' (Kleisli f) = Kleisli $ \x -> MaybeT (return $ f x)
to lift your existing Kleisli Maybe
arrows to Kleisli (MaybeT IO)
where needed.