Search code examples
haskelltypessicp

Type declaration for number division


I tried all possible type declarations but I can't make this code even compile. The trick is in handling types for division. I tried Num a, Fractional a, Float a etc.

cube x = x * x * x

sum' term a next b =
    if a > b
    then 0
    else term a + sum' term (next a) next b

integral f a b n = (h / 3) * (sum' term 0 succ n)  where
    h = (b - a) / n
    y k = f $ a + (k * h)
    term k
        | k == 0 || k == n  = y k
        | odd  k            = 4 * y k
        | even k            = 2 * y k

main = do
    print $ integral cube 0 1 100      -- 0.25
    print $ (\x -> 3 * x * x) 1 3 100  -- 26

I isolated problem by deleting (/) function. This code compiles without any type declaration at all:

cube x = x * x * x

sum' term a next b =
    if a > b
    then 0
    else term a + sum' term (next a) next b

integral f a b n = (sum' term 0 succ n)  where
    h = (b - a)
    y k = f $ a + (k * h)
    term k
        | k == 0 || k == n  = y k
        | odd  k            = 4 * y k
        | even k            = 2 * y k

main = do
    print $ integral cube 0 1 100

Another question is how to debug cases like this? Haskell's error messages doesn't help much, it's kind of hard to understand something like The type variable a0 is ambiguous or Could not deduce (a1 ~ a).

P. S. It's ex. 1.29 from SICP.

Update

Final answer is:

cube :: Num a => a -> a
cube x = x * x * x

sum' :: (Int -> Double) -> Int -> (Int -> Int) -> Int -> Double
sum' term a next b =
    if a > b
    then 0
    else term a + sum' term (next a) next b

integral :: (Double -> Double) -> Double -> Double -> Int -> Double
integral f a b n = (h / 3) * sum' term 0 (+1) n  where
    h = (b - a) / n'  where n' = fromIntegral n
    y k = f $ a + (k * h)
    term k
        | k == 0 || k == n  = y k'
        | odd  k            = 4 * y k'
        | even k            = 2 * y k'
        where k' = fromIntegral k

main = do
    print $ integral cube 0 1 100               -- 0.25
    print $ integral cube 0 1 1000              -- 0.25
    print $ integral (\x -> 3 * x * x) 1 3 100  -- 26

Solution

  • / is only used for types that are instances of Fractional, for Integral types use quot. You can use quot as an infix operator using backticks:

    h = (b - a) `quot` n
    

    The types of the two are

    (/) :: Fractional a => a -> a -> a
    quot :: Integral a => a -> a -> a
    

    There are no types that are instances of both Fractional and Integral, which is why none of the type signatures would work. Unfortunately GHC doesn't know that it's impossible for a type to be an instance of both classes, so the error messages are not very intuitive. You get used to the style of GHC error messages though, and the detail they give helps a lot.

    Also, as was suggested in the comments, I completely agree that all top level definitions should be given type signatures (including main). It makes error messages a lot easier to read.

    Edit: Based on the comments below, it looks like what you want is something more like this (type signature-wise)

    cube :: Num a => a -> a
    
    sum' :: (Int -> Double) -> Int -> (Int -> Int) -> Int -> Double
    
    integral :: (Double -> Double) -> Double -> Double -> Int -> Double
    

    You will need to use fromIntegral to convert from Int to Double in h and in k. The type errors should be at least a bit more readable with these type signatures though.