3

我挣扎了好几个小时才得到以下正确的东西:

to_xyz_regular :: (RealFrac t) => (RegularSpd t) -> (t, t, t)
to_xyz_regular spd@(RegularSpd lambda_min lambda_max spectrum delta inverse_delta) =  
    let
        l = length cie_x - 1
        il = 1.0 / fromIntegral l
        samp i = sample spd $ lambda_min + inverse_delta * fromIntegral i
        integrate curve = (foldr (+) 0 $
                           map (\i -> curve!!i * samp i) [0..l]) * il
    in (integrate cie_x, integrate cie_y, integrate cie_z)

(这是从颜色 SPD/光谱到 XYZ 颜色的转换例程)。

这些值cie_XXX定义如下:

cie_start = 360.0
cie_end = 830.0
cie_x = [0.0001299000, 0.0001458470, 0.0001638021, 0.0001840037,
         ....]
cie_y = ....
cie_z = ....

但后来粗略地给了我

Could not deduce (t ~ Double)
from the context (RealFrac t)
....

问题是cie_XXX存储为的值Double,我真的希望它们是多态的。

我最终找到的解决方案是为这些值添加类型签名:

cie_start :: (RealFrac t) => t
cie_end :: (RealFrac t) => t
cie_x :: (RealFrac t) => [t]
cie_y :: (RealFrac t) => [t]
cie_z :: (RealFrac t) => [t]

现在我的问题是:这是在 Haskell 中拥有多态值/文字的正确方法吗?


在网络搜索上找到解决方案对我来说有点困难,因为书籍、教程等仅提到参数函数的类型签名 -> 值只是无参数函数吗?

4

1 回答 1

4

您选择的方式是在 Haskell 中为数字文字提供多态类型的正确方式。

Double选择类型的原因cie_XXX是由于 Haskell 的默认机制(这里有更详细的描述当单态限制forall要求其他多态类型(隐式或显式包含 a )类型不明确(包含仅出现在 左侧的类型变量,例如in )类型时,调用默认值被限制为一个或标准库定义的子类的实例。默认值强制类型与中的每个条目统一(从最左边的条目开始),直到统一成功。默认 =>a(Read a, Show a) => StringPreludeNumdefault (...)default (...)

default (Integer, Double)

如果由于某种原因Float是实数的所需默认值,

default (Integer, Float)

可以定义。如果Float是所有数字的所需默认值,

default (Float)

可以定义。

在原始场景的情况下,

cie_start :: Fractional a => a

cie_start( 以及cie_end) 的最通用类型。这种类型在推断时会违反单态性限制。但是,在存在默认值的情况下,首先尝试了一些类型来代替类型变量a。鉴于默认值default (...)default (Integer, Double)Integer并且Double按该顺序尝试。

cie_start :: Fractional Integer => Integer

导致违反约束(Integer不是 的实例Fractional)。

cie_start :: Fractional Double => Double

成功,并简化为

cie_start :: Double

要使原始场景成为错误而不是默认Double(并完全禁用默认),请添加

default ()

cie_XXX在定义的模块的顶层。

要使场景不需要显式类型签名而是推断最通用的类​​型,请添加

{-# LANGUAGE NoMonomorphismRestriction #-}

cie_XXX定义的模块的顶部(有关为什么默认情况下不使用这种最通用类型的更多详细信息,请参阅单态限制)。

总的来说,您选择解决此问题的方法(添加显式类型签名)是最佳解决方案。在所有顶级定义上都有类型签名被认为是最佳实践。

于 2013-04-11T12:39:52.663 回答