我挣扎了好几个小时才得到类似下面的内容:
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
,我真的希望它们是poylmorphic。
我最终找到的解决方案是为这些值添加类型签名:
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中使用多态值/文字的正确方法吗?
在网络搜索上找到解决方案对我来说有点困难,因为书籍,教程等仅提及参数函数的类型签名 - >值只是无参数函数吗?
答案 0 :(得分:4)
您选择的方式是在Haskell中为数字文字提供多态类型的正确方法。
为Double
类型选择cie_XXX
的原因是由于Haskell的默认机制(更详细地描述了here)。当一个其他多态类型(包含forall
,无论是隐式还是显式)需要由monomorphism restriction 或单态时类型是不明确的(包含类型变量)时调用默认值仅显示在=>
左侧的内容,例如a
中的(Read a, Show a) => String
,和该类型被限制为Prelude
的实例或标准库定义的Num
子类。默认强制将类型与default (...)
中的每个条目统一(从最左边的条目开始),直到统一成功。默认default (...)
是
default (Integer, Double)
如果由于某种原因Float
是实数的理想默认值,
default (Integer, Float)
可以定义。如果Float
是所有数字的所需默认值,
default (Float)
可以定义。
在原始方案的情况下,
cie_start :: Fractional a => a
是cie_start
(以及cie_end
)的最常规类型。推断出此类型会违反monomorphism restriction。但是,在存在默认情况下,首先尝试使用类型变量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
的模块顶部(有关默认情况下未使用此最常用类型的原因的更多详细信息,请参阅Monomorphism restriction。)
总的来说,您选择解决此问题的方法(添加显式类型签名)是最佳解决方案。在所有顶级定义上都有类型签名被认为是最佳实践。