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