在 Haskell Control.Arrow
文档它谈到了 Kleisli 箭头与单子(monad)的关系,但对我来说如何使用它并不明显。我有一个我认为适合箭头的功能,除了它涉及 IO monad,所以我认为 Kleisli 箭头可能会有所帮助。
采用以下函数,它返回目录的原始文件名和修改后的文件名对。
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
如果我必须把它画出来,它会是这样的:
我认为它可以从使用 Kleisli 箭头中受益,但我不知道如何。任何人都可以提供指导吗?
最佳答案
单子(monad)是 Functor
s 从 Hask(Haskell 类型和函数的范畴)到 Hask——一个内仿函数。这意味着 Hask 中的一些箭头看起来像 a -> m b
对于一些 Monad
m
.对于特定的单子(monad) m
,Hask 的子类别,其中箭头看起来像 a -> m b
是 m
的 Kleisli 类别.
我们知道这是一个类别,因为有一个标识箭头 return :: a -> m a
和作文(>>>) :: (a -> m b) -> (b -> m c) -> (a -> m c)
定义为
(f >>> g) a = join (g <$> f a)
这就是为什么我们需要将其设为
Monad
---我们同时使用 return
和 join
.在 Haskell 中,我们通常不能只使用子类别,而是使用新类型。
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)
然后我们可以升级
Monad m => a -> m b
类型的功能至Kleisli m a b
s,类别中的箭头,并用 (.)
组合它们arr :: Kleisli IO FilePath [String]
arr = Kleisli (mapM $ fmap show . getModificationTime) . Kleisli getDirectoryContents
不过,通常这在语法上有点嘈杂。新类型只有在使用
Category
时才有值(value)。要重载的类型类 id
和 (.)
.相反,您更有可能看到 return
和 (>=>)
相当于return a = runKleisli (id a)
f >=> g = runKleisli $ Kleisli g . Kleisli f
关于haskell - 如何在单子(monad)中使用 Kleisli 箭头?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20165554/