Я хотел бы иметь функцию для сопоставления чистой функции с контейнером или для последовательного выполнения аппликативных / монадических действий через него . Для чистого отображения мы имеем
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 с аналогичным методом? Может, где-то на взломе? Я пытался обмануть / hayoo, но не нашел ничего подходящего.
Обновление:
Теперь я лучше знаю, что мне нужно. И стрелки Клейсли, и (->) являются экземплярами 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!
Я мог бы использовать просто обычный Traversable на основе Applicative, с Identity для чистых функций, но я не уверен, что это хорошо. Странно рассматривать чистые функции как частный случай монадических действий. Интерпретация как чистых функций, так и монадических действий как экземпляров некоторого класса действий (Category / Arrow / ArrowApply) кажется мне более простой.
Вопросы: не хотите ли вы закончить экземпляр для []
? Имеет ли смысл мое мнение о ArrowApply и Monad?