0
votes

I am writing a Haskell code to find a list of the k nearest neighbours, in distance order, according to metric d to point p in the list xs of points. My code is below

import Data.List
import Data.Function

type Point a = (a,a)
type Metric a = (Point a) -> (Point a) -> Double

neighbours :: Int -> Metric a -> Point a -> [Point a] -> [Point a]
neighbours k calDistance p [] = []
neighbours k calDistance p ps
 | k < 0     = error "k cannot be negative"
 | otherwise = take k (neighbours' (sortBy (compare `on` snd) [ (poi,dis) | (poi,dis) <- points p ps]))

neighbours' :: [(Point a, Double)] -> [Point a]
neighbours' xs = [ x | (x,y)<-xs]

points :: Point a -> [Point a] -> [(Point a, Double)]
points _ [] = []
points p1 (p2:px) = (p2,(calDistance p1 p2)): points p1 px

calDistance :: Metric Double
calDistance (x1,y1) (x2,y2) = sqrt((x1-x2)**2 + (y1-y2)**2)

The error is

    • Couldn't match type ‘a’ with ‘Double’
      ‘a’ is a rigid type variable bound by
        the type signature for:
          points :: forall a. Point a -> [Point a] -> [(Point a, Double)]
        at A4.hs:27:1-53
      Expected type: Point Double
        Actual type: Point a
    • In the first argument of ‘calDistance’, namely ‘p1’
      In the expression: (calDistance p1 p2)
      In the first argument of ‘(:)’, namely ‘(p2, (calDistance p1 p2))’
    • Relevant bindings include
        px :: [Point a] (bound at A4.hs:29:15)
        p2 :: Point a (bound at A4.hs:29:12)
        p1 :: Point a (bound at A4.hs:29:8)
        points :: Point a -> [Point a] -> [(Point a, Double)]
          (bound at A4.hs:28:1)
   |
29 | points p1 (p2:px) = (p2,(calDistance p1 p2)): points p1 px
   |     

However, if I change 'Metric Double' to 'Double a' there is also a compile error: Couldn't match expected type ‘Double’ with actual type ‘a’. Please can someone tell me how to solve it?

1

1 Answers

3
votes

See in the implementation of

points :: Point a -> [Point a] -> [(Point a, Double)]

you call

calDistance :: Metric Double

This means, that a has to be Double, as what should happen if someone called points with a value of type Point Int as the first argument? How should calDistance handle that, as it can only handle values of Point Double?

The error tells you, that calDistance was called with a value of type Point a, where a could be any type. And that doesn't match as calDistance expects a Point Double as its first argument.