功能类型限制

Jef*_*own 3 haskell types typeclass

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

使用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)
Run Code Online (Sandbox Code Playgroud)

双打:

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)
Run Code Online (Sandbox Code Playgroud)

Ale*_*lec 8

可读性是一个意见问题.一般来说,我发现更通用的类型签名更具可读性,因为可能的定义较少(有时甚至只有一个非分歧定义).例如,看到mean只有Fractional约束会立即限制在该函数中执行的操作(与Double可能sqrt为我所知的所有操作执行操作的版本相比).当然,泛化类型并不总是 更具可读性.(只是为了好玩)

拥有更多通用版本的函数的主要缺点是它们可能在运行时保持不优化,因此每次调用时都必须传递函数Double的字典.Floatingmean

通过添加SPECIALIZE编译指示,您可以拥有所有世界中最好的.这告诉编译器基本上复制了一些实例化的类型变量的函数代码.如果你知道你mean几乎只打算调用你的函数Double,那么这就是我要做的

{-# SPECIALIZE mean :: [Double] -> Double #-}
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)
Run Code Online (Sandbox Code Playgroud)

您还可以在代码中看到签名的专用版本!好极了!