Haskell: Writing a function that can take Ints or Doubles
In my continued reading of SICP I wanted to recreate a 'sum' function used to demonstrate a function which could take another function as one of its parameters.
In Scheme the function is defined like this:
(define (sum term a next b)
(if (> a b)
0
(+ (term a)
(sum term (next a) next b))))
And can be used like this to sum the values between two numbers:
(define (identity x) x)
(define (sum-integers a b)
(sum identity a inc b))
> (sum-integers 1 10)
55
I translated it into Haskell as the following:
sicpSum :: (Int -> Int) -> Int -> (Int -> Int) -> Int -> Int
sicpSum term a next b | a > b = 0
| otherwise = term a + sicpSum term (next a) next b
The 'sum-integers' function translates like this:
sumIntegers :: Int -> Int -> Int
sumIntegers a b = sicpSum id a inc b
> sumIntegers 1 10
55
It works fine with integers but later on in the chapter it’s used to define a function which returns a double:
(define (pi-sum a b)
(define (pi-term x)
(/ 1.0 (* x (+ x 2))))
(define (pi-next x)
(+ x 4))
(sum pi-term a pi-next b))
> (* 8 (pi-sum 1 1000))
3.139592655589783
I tried writing my 'piSum' function to call the existing 'sumIntegers':
piSum a b = sicpSum piTerm a piNext b where
piTerm x = 1 / (x * (x + 2))
piNext x = x + 4
Which unfortunately doesn’t compile because of our use of '/':
sicp.hs:124:22:
No instance for (Fractional Int)
arising from a use of `piTerm'
Possible fix: add an instance declaration for (Fractional Int)
In the first argument of `sicpSum2', namely `piTerm'
In the expression: sicpSum2 piTerm a piNext b
In an equation for `piSum':
piSum a b
= sicpSum2 piTerm a piNext b
where
piTerm x = 1 / (x * x + 2)
piNext x = x + 4
> :t (/)
(/) :: Fractional a => a -> a -> a
I need to make 'sicpSums' generic enough to take in a Double or Int in order to reuse it here.
Most of the functions I’ve written have been for very specific types although a lot of the Haskell examples I’ve come across tend to have very generic type signatures.
I found quite a cool diagram on a Haskell wiki which shows which types inherit different type classes.
In this case both Int and Double derive from the Num type class so we can redefine 'sicpSum' in terms of that:
sicpSum :: (Ord a, Num a1) => (a -> a1) -> a -> (a -> a) -> a -> a1
sicpSum term a next b | a > b = 0
| otherwise = term a + sicpSum term (next a) next b
We had to also make sure that 'a' inherits the 'Ord' type class because of the comparison between a and b that we do on the second line.
'piSum' can then make use of the new 'sicpSum' in its definition:
piSum :: (Ord a1, Fractional a1) => a1 -> a1 -> a1
piSum a b = sicpSum piTerm a piNext b where
piTerm x = 1 / (x * (x + 2))
piNext x = x + 4
We can then use it like this:
> 8 * (piSum 1 1000)
3.139592655589783
Obviously this is a very simple example but I haven’t written any functions which could take in different types so I thought I’d document how I did it, especially because the diagram of the type classes is really useful!
About the author
I'm currently working on short form content at ClickHouse. I publish short 5 minute videos showing how to solve data problems on YouTube @LearnDataWithMark. I previously worked on graph analytics at Neo4j, where I also co-authored the O'Reilly Graph Algorithms Book with Amy Hodler.