Подтвердить что ты не робот

Стрела и Монада, две независимые точки зрения для составления вычислений?

Я читал "The Typeclassopedia" Брент Йорги в Monad.Reader # 13 и обнаружил, что "the functor hierachy" является взаимозависимым от "категории hierachy", как показано на рисунке .1.

Figure.1

И, согласно автору, ArrowApply == Monad, особенно, что предыдущий - это просто экземпляр класса типа, который можно использовать, когда

"мы хотели бы вычислить стрелку из промежуточных результатов и использовать эту вычисленную стрелку для продолжения вычисления. Это сила, данная нам ArrowApply."

Но как мы можем объединить эти вещи? Я имею в виду, что есть некоторые функции управления потоком как в Monad, так и в Arrow (например, if и else vs. ArrowChoice, или forM vs. ArrowLoop), а некоторые функции кажутся "отсутствующими" в Monad ((***), (|||) или first). Все это похоже на то, что нам нужно сделать выбор между использованием системы Monad или Arrow для построения потока вычислений побочных эффектов и потерять некоторые функции в другой системе.

4b9b3361

Ответ 1

Ответ заключается в следующем (все это из Документов Control.Arrow)

newtype ArrowApply a => ArrowMonad a b = ArrowMonad (a () b)
instance Monad ArrowApply a => Monad (ArrowMonad a)

Новый тип ArrowMonad - это средство, с помощью которого мы определяем экземпляр Monad для стрелок ArrowApply. Мы могли бы использовать

instance Monad ArrowApply a => Monad (a ())

но это могло бы вызвать проблемы с ограниченным типом класса Haskell (он будет работать с расширением UndecideableInstances, я понял).

Вы можете придумать экземпляр Monad для ArrowApply стрелок как перевод монадических операций в эквивалентные операции со стрелками, как показывает источник:

instance ArrowApply a => Monad (ArrowMonad a) where
        return x = ArrowMonad (arr (\_ -> x))
        ArrowMonad m >>= f = ArrowMonad (m >>>
                        arr (\x -> let ArrowMonad h = f x in (h, ())) >>>
                        app)

Знаем, что мы знаем, что ArrowApply так же эффективен, как Monad, так как мы можем реализовать все операции Monad в нем. Удивительно, но верно и обратное. Это дается Kleisli newtype, как отмечал @hammar. Обратите внимание:

newtype Kleisli m a b = Kleisli { runKleisli :: a -> m b }

instance Monad m => Category (Kleisli m) where
        id = Kleisli return
        (Kleisli f) . (Kleisli g) = Kleisli (\b -> g b >>= f)

instance Monad m => Arrow (Kleisli m) where
        arr f = Kleisli (return . f)
        first (Kleisli f) = Kleisli (\ ~(b,d) -> f b >>= \c -> return (c,d))
        second (Kleisli f) = Kleisli (\ ~(d,b) -> f b >>= \c -> return (d,c))

instance Monad m => ArrowApply (Kleisli m) where
        app = Kleisli (\(Kleisli f, x) -> f x)

instance Monad m => ArrowChoice (Kleisli m) where
    left f = f +++ arr id
    right f = arr id +++ f
    f +++ g = (f >>> arr Left) ||| (g >>> arr Right)
    Kleisli f ||| Kleisli g = Kleisli (either f g)

В предыдущем приведена реализация для всех обычных операций со стрелками с использованием операций монады. (***) не упоминается, поскольку имеет реализацию по умолчанию usin first и second:

f *** g = first f >>> second g

Итак, теперь мы знаем, как реализовать операции стрелки (Arrow, ArrowChoice, ArrowApply) с использованием операций Monad.


Чтобы ответить на вопрос о том, почему мы имеем Monad и Arrow, если они оказываются эквивалентными:

Менее мощные стрелки полезны, когда нам не нужна полная сила монады, так же как аппликативные функторы могут быть полезны. И хотя ArrowApply и Monad эквивалентны, Arrow или ArrowChoice без app - это то, что не представляется в иерархии Monad. И наоборот, Applicative не представляется в иерархии стрелок. Это связано с тем, что ap входит в первую очередь в иерархию монады и "последний" в иерархии стрелок.

Основное семантическое различие между мирами монады и стрелки состоит в том, что стрелки захватывают преобразование (arr b c означает, что мы создаем c из b), в то время как монады захватывают операцию (monad a создает a a). Эта разница хорошо отражена в новинках Kleisli и ArrowMonad:

newtype Kleisli m a b = Kleisli { runKleisli :: a -> m b }
newtype ArrowApply a => ArrowMonad a b = ArrowMonad (a () b)

В Kleisli мы должны добавить тип источника a, а в ArrowMonad мы установим его в ().


Я надеюсь, что это удовлетворит вас!