First of all, that style is actually rather idiomatic. Since you're doing two things to two different values, there is some irreducible complexity; the actual pattern match does not introduce much on its own. Besides, I personally find the explicit style very readable most of the time.
However, there is an alternative. Control.Arrow
has a bunch of functions for working with tuples. Since the function arrow ->
is an Arrow
as well, all these work for normal functions.
So you could rewrite your second example using (***)
to combine two functions to work over tuples. This operator has the following type:
(***) :: a b c -> a b' c' -> a (b, b') (c, c')
If we replace a
with ->
, we get:
(***) :: (b -> c) -> (b' -> c') -> ((b, b') -> (c, c'))
So you could combine (+ x)
and (- x)
into a single function with (+ x) *** (- x)
. This would be equivalent to:
\ (a, b) -> (a + x, b - x)
Then you could use it in your recursion. Unfortunately, the -
operator is stupid and doesn't work in sections, so you would have to write it with a lambda:
(+ x) *** (\ a -> a - x) $ go xs
You can obviously imagine using any other operator, all of which aren't quite as stupid :).
Honestly, I think this version is less readable than the original. However, in other cases, the ***
version can be more readable, so it's useful to know about it. In particular, if you were passing (+ x) *** (- x)
into a higher-order function instead of applying it immediately, I think the ***
version would be better than an explicit lambda.