Search code examples
haskellmonadsarrow-abstraction

Can we define a Monad instance for WrappedArrow?


WrappedArrow is an instance of Applicative, but can it be made a Monad (probably if the arrow is ArrowApply)?

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

EDIT:

My initial purpose was to have a Monad instance for (wrapped) Kleisli, so I could write

runWithInput input $ do
  action1
  action2
  action3
  ...

instead of

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

But I realized that won't make desirable sense. Stripped of newtypes, Kleisli f a b >> Kleisli f a c is

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

And what I need is analog of >>>, i.e. b instead of second a:

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

So I suppose I will have to use StateT or a custom monad if I want to sequence actions in this manner with do.


Solution

  • Since this is an XY problem, I'll answer both the question you asked, and the question you probably wanted to ask:

    WrappedArrow is an instance of Applicative, but can it be made a Monad (probably if the arrow is ArrowApply)?

    Yes it can, but you'll need more constraints. In fact, I think there are several ways to do this. For example, you could take the approach suggested by @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
    

    You can get some intuition for ArrowSwap via its instance for (->):

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

    Of course, it isn't immediately clear that this is useful...

    My initial purpose was to have a Monad instance for (wrapped) Kleisli, so I could write

    runWithInput input $ do
      action1
      action2
      action3
      ...
    

    instead of

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

    This is what RebindableSyntax is for:

     {-# 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