student422
student422

Reputation: 145

How to avoid type mismatches in lookup?

I am trying to write a linear interpolation in Haskell:

linear_interpolation:: [(Double, Double)] -> Double -> Maybe Double
linear_interpolation list x
    | x1 /= Nothing && x2 /= Nothing && y1 /= Nothing && y2 /= Nothing
      = Just (y1+ (y2-y1)*(x - x1)/(x2 - x1))
    | otherwise = Nothing
      where
        x2 = find (> x) (x_list list)
        x1 = max_elem $ filter (< x) (x_list list)
        y1 = lookup x1 list
        y2 = lookup x2 list

But I get this error:

• Couldn't match type ‘Double’ with ‘Maybe Double’
      Expected: [(Maybe Double, Double)]
        Actual: [(Double, Double)]
    • In the second argument of ‘lookup’, namely ‘list’
      In the expression: lookup x2 list
      In an equation for ‘y2’: y2 = lookup x2 list
   |
29 |         y2 = lookup x2 list
   |                        ^^^^

I don't really understand what's going on, because lookup type is like that:

lookup :: Eq a => a -> [(a, b)] -> Maybe b

P.S

The same problem for y1, x1, x2

Upvotes: 0

Views: 52

Answers (1)

chi
chi

Reputation: 116174

Testing x1 /= Nothing doesn't change the type of x1, which is still Maybe Double, so we can't perform arithmetic operations on x1.

For this reason, testing x1 /= Nothing, isNothing x1, or isJust x1 is most likely to be the wrong thing to do. Most often what we really need to use is pattern matching:

linear_interpolation
   :: [(Double, Double)] -> Double -> Maybe Double
linear_interpolation list x = let
   x2 = find (> x) (x_list list)
   x1 = max_elem $ filter (< x) (x_list list)
   in case (x1, x2) of
      (Just x1', Just x2') -> let
         y1 = lookup x1' list
         y2 = lookup x2' list
         in case (y1, y2) of
            (Just y1', Just y2') ->
               Just (y1' + (y2' - y1')*(x - x1')/(x2' - x1'))
            -> Nothing
      _ -> Nothing

Here, x1',x2',y1',y2' will have the wanted type Double.

The nested let and case is not so readable. In this case, it's simpler if we exploit the Maybe monad and use do notation.

linear_interpolation
   :: [(Double, Double)] -> Double -> Maybe Double
linear_interpolation list x = do
   x2 <- find (> x) (x_list list)
   x1 <- max_elem $ filter (< x) (x_list list)
   y1 <- lookup x1 list
   y2 <- lookup x2 list
   Just (y1 + (y2 - y1)*(x - x1)/(x2 - x1))

Here, using <-, we make x1,x2,y1,y2 :: Double removing the Maybe from the type. The Nothing case is automatically dealt with, which makes it rather convenient.

Upvotes: 2

Related Questions