15

Sometimes you want to fold a list of tuples into one tuple using different folding functions. For instance, in order to glue together a list of runState results, getting an (in some sense) combined state and a combined result.

Consider the following implementation:

wish :: (a -> a' -> a) -> (b -> b' -> b) -> (a,b) -> [(a', b')] -> (a,b)
wish lfn rfn x xs = foldl (\(a,b) -> (lfn a) *** (rfn b)) x xs

Although it works, I feel uncomfortable about this lambda. lfn *** rfn by itself has a type of (a,b) -> (a -> a', b -> b'), which I can't find a way to properly apply to a tuple without ever resorting to pattern matching. Is there a clear and elegant way I'm missing? It could be a library function of type (a,a') -> (a -> a, a' -> a') -> (a, a') or a whole different approach, maybe.

Francis Drake
  • 315
  • 2
  • 11
  • 2
    Some sort of BiApplicative class would do.. There probably is one somewhere on hackage, but I'll leave it to others to cover which ones are good and not-deprecated. – Carl May 21 '13 at 18:45
  • http://squing.blogspot.com/2008/11/beautiful-folding.html and its instantiation on Hackage, http://hackage.haskell.org/package/ZipFold – Daniel Wagner May 21 '13 at 20:24

1 Answers1

9

Control.Arrow does not pay much attention to higher-arity functions. What you really want is a function foo :: (a -> a' -> a'') -> (b -> b' -> b'') -> (a,b) -> (a',b') -> (a'',b''), the analogue of (***) for functions of arity 2. There is such a function in Data.Biapplicative (from the package bifunctor), which has the somewhat more general signature biliftA2 :: Biapplicative w => (a -> b -> c) -> (d -> e -> f) -> w a d -> w b e -> w c f. Since there is a Biapplicative instance for two-element tuples, that is all you need.

The only complaint I can see against your code as it stands is that it the currying of the lambda is non-obvious; I might prefer the more explicit \(a,b) (a',b') -> (lfn a a', rfn b b').

Edit notes: I had concluded that the needed function did not exist, and suggested defining it; spurred by Carl's comment, I found the one in Biapplicative (the more general type signature had prevented Hoogle from finding it under my suggested signature).

isturdy
  • 1,231
  • 8
  • 12
  • Going for manual tuple decomposition with `\(a,b) (a',b') -> ...` kinda defies the whole purpose of using some fancy concept to write an all-powerful one-liner. Thanks for referring to Biapplicative, I'll look into it. – Francis Drake May 21 '13 at 22:23
  • My point was that even though Biapplicative's `(<<**>>)` does have the type you need to apply `(lfn *** rfn)`, the invocation of arrows seems out of place to me here. But that is just my intuition; the arrow-based approach is correct, if not necessarily clear. But I do prefer `(lfn \`biLiftA2\` rfn)` to the explicit lambda. – isturdy May 21 '13 at 22:52