如果我有以下两个 Kleisli 箭头:
stdoutProcessA :: Kleisli Maybe String (IO String)
writeToFileA :: Kleisli Maybe (FilePath, String) (IO ())
我希望能够写一些类似的东西:
compile = proc src -> do
output <- stdoutProcessA -< "..."
writeToFileA -< ("...", output)
...
这当然不起作用,因为
String
与 IO String
不匹配。另一方面,可以将 stdoutProcessA
和 writeToFileA
都定义为 Kleisli IO ...
类型,但是这样我就无法用 Kleisli Maybe ...
类型的箭头来组合它们,而我需要其他类型的箭头东西。
我对箭头还不太有经验,所以我可能错过了一些明显的东西。怎样做才能做到上述呢?
这些箭头对我来说没有多大意义:
stdoutProcessA :: Kleisli Maybe String (IO String)
writeToFileA :: Kleisli Maybe (FilePath, String) (IO ())
它们代表结果为
Maybe (IO a)
的函数,而您可能指的是 IO (Maybe a)
。后一种类型代表可能失败的 IO
操作,而前一种类型的失败或成功根本不依赖于 IO
。
组合
IO
和 Maybe
的正确方法是使用 MaybeT
monad 转换器,如下所示:
stdoutProcessA :: Kleisli (MaybeT IO) String String
writeToFileA :: Kleisli (MaybeT IO) (FilePath, String) ()
如果您将其他箭头写为
Monad m => Kleisli (MaybeT m) a b
,它们应该与这些箭头很好地组合在一起,而无需任何提升。或者,您可以使用
lift' :: Monad m => Kleisli Maybe a b -> Kleisli (MaybeT m) a b
lift' (Kleisli f) = Kleisli $ \x -> MaybeT (return $ f x)
将现有的
Kleisli Maybe
箭头提升至需要的 Kleisli (MaybeT IO)
。