2016-11-11 19 views
2

WrappedArrow - это пример Applicative, но может ли он быть сделан Monad (возможно, если стрелка ArrowApply)?Можем ли мы определить экземпляр Monad для WrappedArrow?

instance ArrowApply a => Monad (WrappedArrow a b) where 
    return = pure 
    (>>) = (*>) 
    (>>=) = ??? 

EDIT:

Моя первоначальная цель состояла в том, чтобы иметь экземпляр Monad для (обернутые) Клейсли, так что я мог бы написать

runWithInput input $ do 
    action1 
    action2 
    action3 
    ... 

вместо

do 
    output1 <- action1 input 
    output2 <- action2 output1 
    output3 <- action3 output2 
    ... 

Но я понял, что не будет иметь желаемого смысла. Лишенный ньютайпов, Kleisli f a b >> Kleisli f a c является

(a -> f b) -> (a -> f c) -> (a -> f c) 

И что мне нужно, это аналог >>>, т.е. б вместо второго a:

(a -> f b) -> (b -> f c) -> (a -> f c) 

Так что я полагаю, я должен буду использовать StateT или пользовательский monad, если я хочу последовательно выполнить действия таким образом с помощью do.

+0

Возможно, вы хотите ['Control.Arrow.ArrowMonad'] (https://hackage.haskell.org/package/base-4.9.0.0/docs/Control-Arrow.html#t:ArrowMonad)? Когда 'b' не'() 'Я думаю, сложнее определить разумный экземпляр монады. – chi

+0

Чтобы определить такой экземпляр, вам также потребуется функция 'swap :: forall x y z. arr x (arr y z) -> arr y (arr x z) '; затем '>> = = \ x f -> swap (arr f) &&& x >>> app'. Достаточно легко определить такую ​​функцию для экземпляров 'Arrow' в' base', но я не могу сказать, насколько это полезно в целом. – user2407038

+1

Что касается вашего редактирования: вы, вероятно, уже знаете это, но в любом случае стоит упомянуть, что вместо этого вы можете написать 'action1> => action2> => action3' (' (> =>) 'буквально' (>>>) 'для стрелок Kleisli). – duplode

ответ

3

Поскольку это проблема XY, я отвечу как вопрос, который вы просили, и вопрос, который вы, вероятно, хотел спросить:

WrappedArrow является экземпляром Applicative, но это может быть сделано Monad (возможно, если стрелка ArrowApply)?

Да, это возможно, но вам нужно больше ограничений. На самом деле, я думаю, есть несколько способов сделать это. Например, вы могли бы принять подход, предложенный @ user2407038:

class Arrow a => ArrowSwap a where 
    swap :: a b (a c d) -> a c (a b d) 

instance (ArrowApply a, ArrowSwap a) => Monad (WrappedArrow a b) where 
    return = pure 
    WrapArrow m >>= f = WrapArrow $ swap (arr (unwrapArrow . f)) &&& m >>> app 

Вы можете получить некоторую интуицию для ArrowSwap с помощью своего экземпляра для (->):

instance ArrowSwap (->) where 
    swap :: (b -> (c -> d)) -> c -> b -> d 
    swap = flip 

Конечно, это не сразу понятно, что это полезно ...

Моя первоначальная цель состояла в том, чтобы иметь экземпляр Monad для (обернутые) Клейсли, , так что я мог бы написать

runWithInput input $ do 
    action1 
    action2 
    action3 
    ... 

вместо

do 
    output1 <- action1 input 
    output2 <- action2 output1 
    output3 <- action3 output2 
    ... 

Это то, что RebindableSyntax для:

{-# LANGUAGE RebindableSyntax #-} 

import Control.Monad (>=>) 

action1 :: Monad m => T1 -> m T2 
action2 :: Monad m => T2 -> m T3 
action3 :: Monad m => T3 -> m T4 

action4 :: Monad m => T1 -> m T4 
action4 = let (>>) = (>=>) in do 
    action1 
    action2 
    action3 
+0

Учитывая аналогичные функции в * base *, 'ArrowFlip' и' flip', возможно, будут более прозрачными именами. – duplode

+0

@duplode Я полностью согласен!Первоначально я написал это как таковое, но это означало, что для моего экземпляра экземпляра мне пришлось бы импортировать Prelude, а затем использовать квалифицированный 'flip'. Я думал, что это может не стоить того. :) – Alec

+0

Это было бы очень неприятно. Фактически, я на мгновение забыл, что в какой-то момент, играя с этим вопросом, я фактически определил функцию 'fleep' в GHCi :) – duplode

1

WrappedArrow является экземпляром Applicative, но он может быть сделан Monad (возможно, если стрелка ArrowApply)?

вложу WrappedArrow в сторону на мгновение и рассмотрим тонко другой вопрос «Можно ли успешно реализовать instance ArrowApply y => Monad (y r)?». Ответ на этот вопрос «да». Один из способов демонстрации он опирается на ArrowMonad Newtype хи упоминает ...

newtype ArrowMonad a b = ArrowMonad (a() b) 

... и изоморфизм (см this cstheory.SE question и страница 18 из Idioms are oblivious, arrows are meticulous, monads are promiscuous):

kleislify :: ArrowApply y => y r a -> (r -> y() a) 
kleislify af = \r -> arr (const r) >>> af 

unkleislify :: ArrowApply y => (r -> y() a) -> y r a 
unkleislify f = arr f &&& arr (const()) >>> app 

-- unkleislify . kleislify = kleislify . unkleislify = id 

ArrowMonad дает нам экземпляр монады, который мы можем использовать с помощью kleislify, - используя стрелки и предоставляя общий аргумент полученным функциям (другими словами, мы используем привязку ArrowMonad через аппликативный экземпляр для функций):

bindY :: ArrowApply y => y r a -> (a -> y r b) -> y r b 
bindY af h = unkleislify $ (\(ArrowMonad am) -> am) . (\r -> 
    ArrowMonad (kleislify af r) >>= \x -> ArrowMonad (kleislify (h x) r)) 

Соответствующий return также является ArrowMonad один, выдержано в соответствующих слоях шаблонного:

returnY :: ArrowApply y => a -> y r a 
returnY x = unkleislify $ \r -> (\(ArrowMonad am) -> am) (return x) 

Это, однако, не отвечает на вопрос. Для этого bindY и returnY должны соответствовать экземпляру ApplicativeWrappedArrow; то есть мы должны иметь returnY x = arr (const x), а ap мы можем написать bindY, а returnY должен быть эквивалентен (<*>) для WrappedMonad. Например, мы могли бы попытаться использовать определение соответствующих ArrowMonad экземпляров ...

instance Arrow a => Applicative (ArrowMonad a) where 
    pure x = ArrowMonad (arr (const x)) 
    ArrowMonad f <*> ArrowMonad x = ArrowMonad (f &&& x >>> arr (uncurry id)) 

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

... расширить (а затем, надеюсь, упростить) returnY:

returnY 
unkleislify $ \r -> (\(ArrowMonad am) -> am) (return x) 
unkleislify $ \r -> (\(ArrowMonad am) -> am) (ArrowMonad (arr (const x))) 
unkleislify $ \r -> arr (const x) 
arr (\r -> arr (const x)) &&& arr (const()) >>> app 
arr (const (arr (const x))) &&& arr (const()) >>> app 
arr (\r -> (r, r)) >>> arr (const (arr (const x))) *** arr (const()) >>> app 
arr (\r -> (arr (const x),())) >>> app 

Я понятия не имею, что ли могут быть упрощены до arr (const x) для любых ArrowApply. Возможно, возможность переворачивать стрелки (как предлагает Alec и user2407038) поможет избавиться от (), но я не справился с этим. В любом случае, для Kleisli по крайней мере, мы можем продолжить:

arr (\r -> (arr (const x),())) >>> app 
Kleisli (\r -> return (arr (const x),())) >>> Kleisli (\(Kleisli f, x) -> f x) 
Kleisli ((\r -> return (arr (const x),())) >=> (\(Kleisli f, x) -> f x)) 
Kleisli ((\r -> return (Kleisli (return . const x),())) 
    >=> (\(Kleisli f, x) -> f x)) 
Kleisli (\r -> return (Kleisli (return . const x),()) 
    >>= (\(Kleisli f, x) -> f x)) 
Kleisli (\r -> (\(Kleisli f, x) -> f x) (Kleisli (return . const x),())) 
Kleisli (\r -> (return . const x)()) 
Kleisli (\r -> return x) 
Kleisli (return . const x) 
arr (const x) 

Я не пытался делать то же самое с bindY, но мой несведущий догадаться, что подобный сценарий приведет.

+0

Я делал что-то подобное прошлой ночью, и я подумал о добавлении ограничения «Monoid» или «Default» (для решения проблемы '() '). Но я не уверен, что даст что-то, удовлетворяющее законам Монады. Моя интуиция не говорит ... Хорошее объяснение! – Alec