Haskell snippet – recursive average

Greg confessed on #london.pm to having written a average function in Perl in FP style… recursively.

I asked him about this, as a perfectly good average function (using List::Util would be:

    sub avg { sum(@_) / @_ }

which is perfectly fine FP style too. As far as I could understand
it, he was reducing a tuple of sum and count. Though he said he wrote
it just-for-fun, it's not entirely unreasonable if you're using a
list-based language, where you'd otherwise have to traverse the list
twice (in Perl, arrays know how long they are).

Out of curiosity, I tried to check how Haskell defined avg, but it seems that it doesn't.

LoganCapaldo suggested the following definition:

 > import Data.List
> avg xs = sum xs / genericLength xs

We use genericLength because the normal length
function insists on returning an integer, while genericLength returns a
generic Number, which can be divided appropriately. (We'd otherwise
have to write sum xs / (fromIntegral $ length xs))

The recursive function would work by summing a tuple, so that

    avgs [1,3,5] => (9,3)

Once we have that we could divide the tuple using uncurry.

 > avg' = uncurry (/) . avgs

As we're going to fold the number values into a tuple, we need a function like:

 > sum_count s (t,c) = (t+s, c+1)

after which our definition is easily written as a fold

 > avgs = foldr sum_count (0,0)

Which works just fine. But seeing the tuples, I thought of the comments from Joao and doserj, and thought of (&&&).

 > import Control.Arrow
> sum_count s = (+s).fst &&& succ.snd

And byorgey commented that the .fst and .snd pair can be abstracted away using (***). So I tried

 > sum_count s = (+s) *** succ

and was really rather surprised that it worked! Admittedly, the
definition is now something that scares me and whose intent is far less
clear than the first naive definition above:

 > avg2 = uncurry (/) . foldr (\s -> (+s) *** succ) (0,0)

For extra bonus obfuscation points, making the lambda expression points-free is left as an exercise to the reader ;-)

Update: mauke pointed out that my Perl avg sub didn't work… added parens to sum (@_).  Looks like naughty me didn't bother testing that code…