重新定义(==)的Eq实例

jua*_*cho 0 haskell operator-overloading custom-data-type

我有以下数据声明来代表温度:

data Temp = Kelvin Float | Celsius Float | Fahrenheit Float deriving Show

-- Functions for conversion between temperatures
kelvToCels :: Temp -> Temp
kelvToCels (Kelvin k) = Celsius (k-273.15)

kelvToFahr :: Temp -> Temp
kelvToFahr (Kelvin k) = Fahrenheit (((9/5)*(k-273.15))+32)

celsToKelv :: Temp -> Temp
celsToKelv (Celsius c) = Kelvin (c+273.15)

celsToFahr :: Temp -> Temp
celsToFahr (Celsius c) = Fahrenheit (((9/5)*c)+32)

fahrToKelv :: Temp -> Temp
fahrToKelv (Fahrenheit f) = Kelvin ((5/9)*(f-32)+273.15) 

fahrToCels :: Temp -> Temp
fahrToCels (Fahrenheit f) = Celsius ((f-32)/(9/5))
Run Code Online (Sandbox Code Playgroud)

我希望能够比较温度,这样

> (Celsius 100) == (Fahrenheit 212.0) 评估为true。

这是我的尝试:

instance Eq Temp where
   Celsius c == Fahrenheit f = 
    (celsToFahr c) == f
Run Code Online (Sandbox Code Playgroud)

结果:ghci错误,因为RHS上的c和f是Floats而不是Temps,因此这里是一个“修复”:

instance Eq Temp where
   Celsius c == Fahrenheit f = 
    (celsToFahr (Celsius c)) == (Fahrenheit f)
Run Code Online (Sandbox Code Playgroud)

编译没有错误,但是(Celsius 100) == (Fahrenheit 212.0) 引发了异常:函数==中的非穷举模式

我还想创建一个Ord实例,以compare类似的方式重新定义。

我已经走到了尽头,我找不到与我类似的任何示例,因此非常感谢任何建议。提前致谢。

Dan*_*ner 11

我建议您不要写不完整的模式匹配。考虑一下这对您的xToY功能意味着什么,这意味着它们应该能够处理任何输入-因此它们的名称应更改为just toY

我还要保证通过返回一个Float(显然不能用错误的构造函数标记)而不是一个Temp(可以)来知道使用哪个构造函数。所以:

toKelvin :: Temp -> Float
toKelvin (Fahrenheit f) = (5/9)*(f-32)+273.15
toKelvin (Celsius c) = c+273.15
toKelvin (Kelvin k) = k
Run Code Online (Sandbox Code Playgroud)

toCelsius和和类似toFahrenheit。如果您确实想要,则可以分别编写类似

normalizeKelvin :: Temp -> Temp
normalizeKelvin = Kelvin . toKelvin
Run Code Online (Sandbox Code Playgroud)

但是这是否明智,在很大程度上取决于您打算如何使用此代码。

鉴于此,我们现在可以Eq通过选择一个比例尺作为自然比例尺并将其转换为*来编写一个非递归实例。所以:

instance Eq Temp where
    t == t' = toKelvin t == toKelvin t'
Run Code Online (Sandbox Code Playgroud)

请注意,这里我们从调度Temp实例的Float实例Eq,当我们打电话(==),不像你的代码,它从派遣Temp实例回到另一个调用Temp的实例Eq

*如果对四舍五入有偏执,可以先检查是否完全需要转换。所以:

instance Eq Temp where
    Fahrenheit f == Fahrenheit f' = f == f'
    Celsius c == Celsius c' = c == c'
    t == t' = toKelvin t == toKelvin t'
Run Code Online (Sandbox Code Playgroud)

  • @juancho我喜欢你的英语“ Kelvin”发音。toKelvin`。我想我已经回答了为什么我更愿意在答案中将“ Float”从“ toKelvin”返回到“ Temp”,但是我会尝试对其进行扩展。我认为“温度”是某种程度的温度类型,直到运行时才可能知道。但是我们*知道*在编译时用于totoKelvin返回值的标度,因此使用表示我们不会的类型会产生误导。1/2 (2认同)
  • 您可以想象它的更高级版本具有不同的类型,分别是“我们知道它是开氏温度”,“我们知道它是摄氏温度”和“我们知道它是华氏温度”。我也可以。但是,走这条路很快就会导致相当高级的类型级别的主题。简单的版本可以以很少的成本为您带来很多好处。不过,我绝对希望将“ normalizeKelvin”保留为单独的操作:程序员应将其使用视为显式声明,他们愿意舍弃有关所用比例的编译时信息。2/2 (2认同)