一般情况下,最好是对某个函数使用最严格或最宽松的类型定义吗?每种方法的优缺点是什么?我发现当我使用严格双打重写my pearson correlation code时,我更容易写出,跟随和推理(这可能只是缺乏经验)。但是我也可以看到,如果有更广泛的类型定义会使这些功能更普遍适用。更严格的类型定义是否被视为技术债务的一种形式?功能类型限制
随着类型类:
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)
没错,虽然有时候最普遍的多态签名只是有点太疯狂了。如果单独的约束列表比完整的专用类型长两倍,我会考虑它是否真的合理。 (虽然'ConstraintKind'''type'defs可以使这样的签名更具可读性,但是这在错误信息等方面的代价是晦涩难懂的。) – leftaroundabout