
Ugh – sent too soon! Hello – I wanted to add some comments. mean is as you describe. mean1 as defined can take a list of any Real type, and return any Fractional type. These types need not be the same, and it’s up to the *caller* of mean1 to say what types they want to give and get returned, e.g. the following is possible:
:m +Data.Ratio :m +Data.Complex mean1 [6%5, 2%3] :: Complex Double 0.9333333333333333 :+ 0.0
This may not be what you were expecting? There’s also no need to restrict to Real, since it is valid to calculate the mean of a list of e.g. complex numbers. So maybe you want something like this:
mean2 :: (Fractional a) => [a] -> a
mean2 xs = sum xs / genericLength xs
(the realToFrac is now unnecessary). The caller still decides the type, but the argument and result type now have to be the same.
You can’t now do mean2 foo, but you can do mean2 [1,2,3] (and the 1, 2, 3 are interpreted as the default fractional type, probably Double).
I personally prefer to write “utility” functions to be as generic as possible. (Imagine you’re including them in some standard library for the whole world to use). But I’m sure there are other opinions.
Re performance, there is a comment against “genericLength” to say that it is not as efficient as length. And, as used above, this is the case. So maybe you actually want:
mean3 :: (Fractional a) => [a] -> a
mean3 xs = sum xs / fromIntegral (length xs)
which is as efficient as mean.
If you are especially interested in performance, you might want to read thishttp://book.realworldhaskell.org/read/profiling-and-optimization.html. It’s not really “beginner level” but does look at some issues with mean in some detail (and in particular why it can use so much memory and what you can do about that). Performance of Haskell code is often more difficult to predict than for other languages, for example due to lazy evaluation and some amazing transformations of you code done by GHC.
Incidentally, I am in the middle of drafting a page about numbershttps://en.wikibooks.org/wiki/User:Davjam2/Numbers to include in the Haskell wikibook that might be interesting. It discusses types, classes, defaults numeric conversions, etc. I would be very happy if for any feedback (please leave any on the “Discussion” tab on the page).
FYI: I tested performance (on GHC 8.4.3 on Windows) with this:
{-
compile: ghc -O2 -prof -fprof-auto -rtsopts Mean.hs
run: Mean +RTS -p > nul
-}
module Main (main) where
import Data.List
mean :: [Double] -> Double
mean xs = sum xs / fromIntegral (length xs)
mean1 :: (Real a, Fractional b) => [a] -> b
mean1 xs = realToFrac (sum xs) / genericLength xs
mean2 :: (Fractional a) => [a] -> a
mean2 xs = sum xs / genericLength xs
mean3 :: (Fractional a) => [a] -> a
mean3 xs = sum xs / fromIntegral (length xs)
mean4 :: (Fractional a) => [a] -> a
mean4 xs = sum xs / fromIntegral (genericLength xs :: Int)
main :: IO ()
main = do
let xs = [1 .. 10000000] :: [Double] --may default to Integer unless specified as Double.
print $ mean xs --change to mean1, etc, for different runs
And got:
Total time
Total alloc
mean
0.10
1,680,096,640 bytes
mean1
0.17
2,560,096,656 bytes
mean2
0.17
2,560,096,656 bytes
mean3
0.10
1,680,096,640 bytes
mean4
0.10
1,680,096,640 bytes
mean4 also uses genericLength but is as fast as length. This is due to some cleaverness in GHC, where it uses more efficient code when it knows the required result is integral.
From: Beginners