{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE ScopedTypeVariables #-} module Data.Metric ( Metric(..), closest, ) where import Data.Proxy import Data.Ratio import Data.Word class Metric a where dist :: (Fractional n) => a -> a -> n closest :: (Fractional n, Ord n, Metric a) => Proxy n -> [a] -> a -> a closest p xs target = case xs of [] -> error "closest: empty list" best : xs' -> closest' p target xs' best closest' :: forall n a. (Fractional n, Ord n, Metric a) => Proxy n -> a -> [a] -> a -> a closest' p target xs best = case xs of [] -> best x : xs' -> let dx = dist target x :: n db = dist target best :: n in closest' p target xs' $ case dx < db of True -> x False -> best instance Metric Int where dist x y = realToFrac $ if x > y then x - y else y - x instance Metric Integer where dist x y = realToFrac $ if x > y then x - y else y - x instance Metric Word8 where dist x y = realToFrac $ if x > y then x - y else y - x instance Metric Word where dist x y = realToFrac $ if x > y then x - y else y - x instance Metric Float where dist x y = realToFrac $ if x > y then x - y else y - x instance Metric Double where dist x y = realToFrac $ if x > y then x - y else y - x instance Metric (Ratio Integer) where dist x y = realToFrac $ if x > y then x - y else y - x