我想要一个函数来将纯函数映射到容器或通过它对应用程序/单子操作进行排序。对于纯映射,我们有
fmap :: Functor f => (a -> b) -> (f a -> f b)
对于单子排序,我们有(来自 Data.Taversable)
mapM :: (Traversable f, Monad m) => (a -> m b) -> (f a -> m (f b))
这类似于
mapKleisli :: (Traversable f, Monad m) => Kleisli m a b -> Kleisli m (f a) (f b)
mapKleisli = Kleisli . mapM . runKleisli
我们知道 (->) 和 (Kleisli m) 都是类别(和箭头)。于是自然而然地做出一个概括:
mapCategory :: (X f, Category c) => c a b -> c (f a) (f b)
你知道有类似方法的 X 类吗?也许,在黑客攻击的某个地方?我试图 hoogle/hayoo 但没有找到任何合适的东西。
更新:
现在我更清楚我需要什么。Kleisli 箭头和 (->) 都是 ArrowApply 的实例,它与 Monad 一样强大。我想出了这个基于箭头的 Travesable 版本:
{-# LANGUAGE TypeOperators #-}
import Prelude hiding (id, (.), mapM)
import Control.Arrow
import Control.Category
class Traversable f where
traverse :: ArrowApply (~>) => f a -> (a ~> b) ~> f b
mapArrow :: (ArrowApply (~>), Traversable f) => a ~> b -> f a ~> f b
mapArrow a = arr (\x -> (traverse x, a)) >>> app
instance Traversable Maybe where
traverse Nothing = arr (const Nothing)
traverse (Just x) = arr (\a -> (a, x)) >>> app >>> arr Just
instance Traversable [] where
traverse [] = arr (const [])
traverse (x : xs) = undefined -- this is hard!
我可以只使用通常的基于 Applicative 的 Traversable,将 Identity 用于纯函数,但我不确定它是否好。将纯函数视为一元动作的特例是很奇怪的。将纯函数和单子动作解释为某些动作类(Category/Arrow/ArrowApply)的实例对我来说看起来更简单。
问题:你想完成实例[]
吗?我对 ArrowApply vs Monad 的看法有什么意义吗?