views:

241

answers:

4

I've just written this function which simply takes a pair whose second value is in some monad, and "pulls the monad out" to cover the whole pair.

unSndM :: Monad m => (a, m c) -> m (a, c)
unSndM (x, y) = do y' <- y
                   return (x, y')

Is there a nicer and/or shorter or point-free or even standard way to express this?

I've got as far as the following, with -XTupleSections turned on...

unSndM' :: Monad m => (a, m c) -> m (a, c)
unSndM' (x, y) = y >>= return . (x,)

Thanks!

+3  A: 

Hoogle is your friend. If one of the standard libraries had it then a hoogle for "Monad m => (a, m b) -> m (a,b)" would find it. Note the function could still be in a hackage package, but it often isn't worth an extra build-dep just for small functions like this.

TomMD
+7  A: 

One minor point: it's possible to write this using only fmap (no >>=), so you really only need a Functor instance:

unSndM :: (Functor f) => (a, f c) -> f (a, c)
unSndM (x, y) = fmap ((,) x) y

This version is a bit more general. To answer your question about a pointfree version, we can just ask pointfree:

travis@sidmouth% pointfree "unSndM (x, y) = fmap ((,) x) y"
unSndM = uncurry (fmap . (,))

So, yes, an even shorter version is possible, but I personally find uncurry a bit hard to read and avoid it in most cases.

If I were writing this function in my own code, I'd probably use <$> from Control.Applicative, which does shave off one character:

unSndM :: (Functor f) => (a, f c) -> f (a, c)
unSndM (x, y) = ((,) x) <$> y

<$> is just a synonym for fmap, and I like that it makes the fact that this is a kind of function application a little clearer.

Travis Brown
The tupling also happens to be ordered conveniently, allowing a definition like `unSmdM = uncurry $ fmap . (,)`. Interestingly, the type of this function is much more readable/descriptive than either implementation :)
Anthony
I agree, but `unSndM (x, y) = (x,) <$> y` is pretty close.
Travis Brown
Ooh, I didn't know about pointfree - nice, thanks! (I'd tried hoogle, of course.) Some nice versions here, without any extra imports or modifications to libraries ;-) - thank you. :-)
gimboland
Thanks for the tip about `pointfree`. I've been using `@pl` with lambdabot on #haskell the whole time.
Ollie Saunders
I just love pointfree. It should be part of every Haskell IDE.
gawi
+5  A: 

I haven't seen it written in any Haskell library (though it's probably in category-extras), but it is generally known as the "tensorial strength" of a monad. See:

http://en.wikipedia.org/wiki/Strong_monad

http://comonad.com/reader/2008/deriving-strength-from-laziness/

mokus
+10  A: 

If the Traversable and Foldable instances for (,) x) were in the library (and I suppose I must take some blame for their absence)...

instance Traversable ((,) x) where
  traverse f (x, y) = (,) x <$> f y

instance Foldable ((,) x) where
  foldMap = foldMapDefault

...then this (sometimes called 'strength') would be a specialisation of Data.Traversable.sequence.

sequence :: (Traversable t, Monad m) => t (m a) -> m (t a)

so

sequence :: (Monad m) => ((,) x) (m a) -> m (((,) x) a)

i.e.

sequence :: (Monad m) => (x, m a) -> m (x, a)

In fact, sequence doesn't really use the full power of Monad: Applicative will do. Moreover, in this case, pairing-with-x is linear, so the traverse does only <$> rather than other random combinations of pure and <*>, and (as has been pointed out elsewhere) you only need m to have functorial structure.

Conor McBride
Sounds like the birth of a libraries@ proposal to me :-)
sclv
Nice, and costrength is also sequence! http://hackage.haskell.org/packages/archive/category-extras/0.53.5/doc/html/src/Control-Functor-Strong.html
Sjoerd Visscher
Astonishing stuff. I knew it looked simple enough that a category theorist must have given it a name already. :-)
gimboland