Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Haskell - How does this average function work?

I found this implementation of the average function:

avg :: [Int] -> Int
avg = div . sum <*> length

How does this work? I looked at the function that was produced as a result of div . sum:

(div . sum) :: (Integral a, Foldable t) => t a -> a -> a

I understand that, but I'm unable to tell how <*> length does the work.

like image 370
xilpex Avatar asked Aug 23 '20 21:08

xilpex


People also ask

How do you calculate average in Haskell?

Count the number of elements in the rates list: let countRates = length rates. Divide sumRates by countRates . But before you do that, convert countRates to a floating point number. Otherwise Haskell will complain: sumRates / (fromIntegral countRates)

What does () mean in Haskell?

() is very often used as the result of something that has no interesting result. For example, an IO action that is supposed to perform some I/O and terminate without producing a result will typically have type IO () .

What does fromIntegral do in Haskell?

The workhorse for converting from integral types is fromIntegral , which will convert from any Integral type into any Num eric type (which includes Int , Integer , Rational , and Double ): fromIntegral :: (Num b, Integral a) => a -> b.

Is there an average function in Scala?

You can calculate the average of the second elements of the list of tuples, you don't need to do the sum yourself because Scala has a builtin function for that. First we need to transform the list of tuples to a list of Int values, we can do that using the map function as shown below. val average = list.


2 Answers

<*> :: Applicative f => f (a -> b) -> f a -> f b is the sequential application function that works on an Applicative structure. For a function, this is implemented as [src]:

instance Applicative ((->) r) where
    pure = const
    (<*>) f g x = f x (g x)
    liftA2 q f g x = q (f x) (g x)

so f <*> g is short for \x -> f x (g x). This thus means that in the case of avg:

avg = div . sum <*> length

is equivalent to:

avg x = (div . sum) x (length x)

which is thus equivalent to:

avg x = div (sum x) (length x)

so it divides the sum x with length x.

like image 51
Willem Van Onsem Avatar answered Oct 12 '22 09:10

Willem Van Onsem


I'm not a fan of this particular pointfree-trick. It uses the Applicative (a->) instance as a “fanout”, to pass the argument to two separate functions. Essentially those two functions are sum and length, and the results are then combined back by div, which can be expressed nicely with arrow combinators (albeit a bit more verbose, because arrows don't really play in Haskell's default curried style):

import Control.Arrow

avg = uncurry div . (sum &&& length)

In the applicative trick, you merge the combining function into the first argument-sharing one. So div . sum in this case, and the result of the other function length is then passed into the second argument of the first function.

You can also use

avg = liftA2 div sum length

which uses the Applicative instance too.

like image 9
leftaroundabout Avatar answered Oct 12 '22 09:10

leftaroundabout