5

I am looking for a function with type similar to:

Monad m => (a, b) -> (b -> m c) -> m (a, c)

It appears to me as some combination of bind (>>=) and a lens operation.

I am aware that I can solve this with a pattern match after a bind, but my gut tells me there is a "simpler" way to write this by leveraging lenses.

Is there any such operation?

Bakuriu
  • 98,325
  • 22
  • 197
  • 231
Luke Cycon
  • 648
  • 4
  • 12
  • 3
    Is there a reason you want to use a lens operation? It's simple enough using `do` notation: `doit (a,b) func = do { c <- func b; return (a,c) }` – ErikR Aug 13 '15 at 02:58
  • Lenses came to mind because I'd be interested in solving this in the general sense-- that is abstracting out the shape, in this case 2-tuple. – Luke Cycon Aug 13 '15 at 03:00
  • 2
    Perhaps this SO question will help: http://stackoverflow.com/questions/22209982/how-to-modify-using-a-monadic-function-with-lenses – ErikR Aug 13 '15 at 03:02
  • Don't know how I missed that! I'm marking an answer below correct as it was a direct solution to what I asked. Your link was certainly more what I was hoping to find though, thanks! – Luke Cycon Aug 13 '15 at 03:15
  • If your `Monad`s aren't `Applicative` yet, you might want to upgrade GHC. It's nice not to have to monkey around with that silly wrapper. – dfeuer Aug 13 '15 at 03:19
  • They most definitely are. To which wrapper are you referring? – Luke Cycon Aug 13 '15 at 03:21
  • 1
    `Applicative` and even `Functor` weren't superclasses of `Monad` till `base 4.8` (GHC 7.10). In the bad old days of last year, people had to use a newtype wrapper to turn arbitrary monads into applicatives. – dfeuer Aug 13 '15 at 03:40

2 Answers2

8

This is definitely lensy. The monad is actually just a bit of a distraction because all you need is a functor:

changesecond (a, b) f = fmap (a,) (f b)

I'm pretty sure the _2 lens can be made to do your bidding with a basic lens thing like maybe over but I'm not too familiar with the library yet.

Edit

No combinator is really needed. You can write

changesecond pair f = _2 f pair

You should be able to work this out from the general definition of the Lens type.

Edit 2

This simple example demonstrates the main theme of Van Laarhoven lens construction:

  1. Extract the focus from the context.
  2. Apply the given function to produce a functorful of results.
  3. Use fmap to restore the contexts to the results.

Ed Kmett's lens library elaborates on this theme in various ways. Sometimes it strengthens the functor constraint. Sometimes it generalizes the function to a profunctor. In the case of Equality, it removes the functor constraint. It just turns out that the same basic type shape can express a lot of different ideas.

Community
  • 1
  • 1
dfeuer
  • 48,079
  • 5
  • 63
  • 167
  • Right you are. I must dig a bit deeper into the lens types. I should have immediately noticed the Functor aspect of the Lens definition. Thanks for pointing that out! – Luke Cycon Aug 13 '15 at 04:00
  • 2
    Isn't that just: `changesecond = flip _2`? – Bakuriu Aug 13 '15 at 07:05
  • Instead of invoking the `_2` lens directly, I'd suggest using `traverseOf`: `changesecond = flip (traverseOf _2)`. While [`traverseOf = id`](http://stackoverflow.com/a/22210866/1333025), it makes the code somewhat more readable. – Petr Aug 14 '15 at 19:54
6

Your function is just forM = flip mapM, or for = flip traverse if you relax the Monad constraint to Applicative. The Functor being traversed is (,) a.

Prelude> let foo :: Applicative f => (a, b) -> (b -> f c) -> f (a, c); foo p k = traverse k p
Prelude> :t foo
foo :: Applicative f => (a, b) -> (b -> f c) -> f (a, c)
Prelude> foo (1,2) (\x -> [x,2*x])
[(1,2),(1,4)]

(Also, as dfeuer points out, you don't even need Applicative in this specific case.)

duplode
  • 33,731
  • 7
  • 79
  • 150
  • 4
    Note that `flip traverse` is [`for`](https://hackage.haskell.org/package/base-4.8.1.0/docs/Data-Traversable.html#v:for). – Lambda Fairy Aug 13 '15 at 03:13