功能类型限制

时间:2017-02-21 20:57:55

标签: haskell types typeclass

通常最好是对函数使用最严格或最宽松的类型定义吗?每种方法的优缺点是什么?我发现当我用严格的双打重写my pearson correlation code时,我更容易写,跟随和推理(这可能只是缺乏经验)。但我也可以看到,更广泛的类型定义如何使函数更普遍适用。是否将更严格的类型定义描述为一种技术债务?

使用Typeclasses:

import Data.List

mean :: Fractional a => [a] -> a
mean xs = s / n
    where
        (s , n) = foldl' k (0,0) xs
        k (s, n) x = s `seq` n `seq` (s + x, n + 1)

covariance :: Fractional a => [a] -> [a] -> a
covariance xs ys = mean productXY
  where
   productXY = zipWith (*) [x - mx | x <- xs] [y - my | y <- ys]
   mx        = mean xs
   my        = mean ys

stddev :: Floating a => [a] -> a
stddev xs = sqrt (covariance xs xs)

pearson :: RealFloat a => [a] -> [a] -> a
pearson x y = fifthRound $ covariance x y / (stddev x * stddev y)

pearsonMatrix :: RealFloat a => [[a]] -> [[a]]
pearsonMatrix (x:xs) = [pearson x y | y <- x:xs]:(pearsonMatrix xs)
pearsonMatrix [] = []

fifthRound :: RealFrac a => a -> a
fifthRound x = (/100000) $ fromIntegral $ round (x * 100000)

双打:

import Data.List

mean :: [Double] -> Double
mean xs = s / n
    where
        (s , n) = foldl' k (0,0) xs
        k (s, n) x = s `seq` n `seq` (s + x, n + 1)

covariance :: [Double] -> [Double] -> Double
covariance xs ys = mean productXY
  where
   productXY = zipWith (*) [x - mx | x <- xs] [y - my | y <- ys]
   mx        = mean xs
   my        = mean ys

stddev :: [Double] -> Double
stddev xs = sqrt (covariance xs xs)

pearson :: [Double] -> [Double] -> Double
pearson x y = fifthRound (covariance x y / (stddev x * stddev y))

pearsonMatrix :: [[Double]] -> [[Double]]
pearsonMatrix (x:xs) = [pearson x y | y <- x:xs]:(pearsonMatrix xs)
pearsonMatrix [] = []

fifthRound :: Double -> Double
fifthRound x = (/100000) $ fromIntegral $ round (x * 100000)

1 个答案:

答案 0 :(得分:8)

可读性是一个意见问题。一般来说,我发现更通用的类型签名更具可读性,因为可能的定义较少(有时甚至只有一个非分歧定义)。例如,看到mean只有Fractional约束会立即限制在该函数中执行的操作(与可能正在执行Double操作的sqrt版本相比我知道)。当然,概括类型is not always more readable。 (And just for fun

拥有更多通用版本的函数的主要缺点是它们可能在运行时保持未经优化,因此Double函数的Floating字典必须传递给{{1每次调用它。

通过添加SPECIALIZE pragma,您可以拥有最好的世界。这告诉编译器基本上复制了一些实例化的类型变量的函数代码。如果您知道仅使用mean来调用mean函数,那么这就是我要做的事情

Double

您也可以在代码中看到签名的专用版本!耶!