Haskell 我如何比较我的数据类型,它可能是有理数或浮点数

Haskell 我如何比较我的数据类型,它可能是有理数或浮点数,haskell,types,functional-programming,Haskell,Types,Functional Programming,鉴于我的数据类型为: data Number = Float Float | Rational Integer Integer 为什么这个代码不起作用 createComparator :: (a -> a -> Bool) -> Number -> Number -> Bool createComparator comparator (Float f1) (Float f2) = comparator f1 f2 createComparator compara

鉴于我的数据类型为:

data Number = Float Float | Rational Integer Integer
为什么这个代码不起作用

createComparator :: (a -> a -> Bool) -> Number -> Number -> Bool
createComparator comparator (Float f1) (Float f2) = comparator f1 f2
createComparator comparator (Float f) (Rational n d) = comparator f $ (fromIntegral n) / (fromIntegral d)
createComparator comparator (Rational n d) (Float f) = comparator ((fromIntegral n) / (fromIntegral d)) f
-- createComparator comparator (Rational n1 d1) (Rational n2 d2) = ... -- TODO
因此,它基本上需要一个比较器(>)或(1/3==True)

或者,0.2>1/3==False

但是,我得到了这个错误

Couldn't match expected type ‘a’ with actual type ‘Float’
  ‘a’ is a rigid type variable bound by
      the type signature for
        createComparator :: (a -> a -> Bool) -> Number -> Number -> Bool
      at Ramadoka/Parser/Number.hs:55:23
Relevant bindings include
  comparator :: a -> a -> Bool
    (bound at Ramadoka/Parser/Number.hs:56:20)
  createComparator :: (a -> a -> Bool) -> Number -> Number -> Bool
    (bound at Ramadoka/Parser/Number.hs:56:3)
In the first argument of ‘comparator’, namely ‘f1’
In the expression: comparator f1 f2
我可以通过以下方式使其工作:

(|>|) :: Number -> Number -> Bool
(Float f1) |>| (Float f2) = f1 > f2
(Float f) |>| (Rational n d) = f > (fromIntegral n) / (fromIntegral d)
(Rational n d) |>| (Float f) = (fromIntegral n) / (fromIntegral d) > f
r1@(Rational _ _) |>| r2@(Rational _ _) = rationalCompare r1 r2 == GT

(|<|) :: Number -> Number -> Bool
(Float f1) |<| (Float f2) = f1 < f2
(Float f) |<| (Rational n d) = f < (fromIntegral n) / (fromIntegral d)
(Rational n d) |<| (Float f) = (fromIntegral n) / (fromIntegral d) < f
r1@(Rational _ _) |<| r2@(Rational _ _) = rationalCompare r1 r2 == LT
(|>|)::数字->数字->布尔
(浮动f1)|>|(浮动f2)=f1>f2
(Float f)|>|(有理数nd)=f>(from积分n)/(from积分d)
(有理数nd)|>|(浮点f)=(from积分n)/(from积分d)>f
r1@(有理数)->r2@(有理数)=有理数比较r1 r2==GT
(|编号->布尔

(Float f1)|警告:我手边没有Haskell编译器,因此无法检查我的工作

简单的答案是将
a
更改为
Float

让我们继续玩下去,看看会发生什么。你的类型签名告诉我的是,如果我为任何可能的
a
提供一个函数
a->Bool
,你的函数就会工作。即使我给你一些可笑的函数,比如
String->String->Bool
,你的函数也会工作。请注意,对于任何变形ic
a
在所有
中都有一个隐式的
,Haskell将其隐藏在类型签名的左侧

“对于您选择的任何
a
,您可以给我一个
a->a->Bool
,我会给您一个
Number->Number->Bool

这可能不是你想说的。你希望你的比较器是
Float->Float->Bool
类型,因为你正在把你自己的
Rational
转换成
Float

如果您想知道如何获得您最初想要的多态性,那么您需要的是

因此,在我们开始这段旅程之前,请在文件顶部启用rank-n类型

{-# LANGUAGE RankNTypes #-}
您可能想说一些更接近以下内容的话,这涉及到将
forall
移动到一组括号中

“如果您给我一个函数,使我可以对所选的任何类型
a
,比较两者并给您一个
Bool
,那么我可以给您一个
Number->Number->Bool

将你的
forall
移动到一层括号中就是所谓的秩-2类型。如果你嵌套另一层,你会得到秩-3,依此类推

这仍然不是您想要的,因为类型约束太强大了。
对于所有a.a->a->Bool
必须始终为true或false才能适用于所有可能的
a
。您可能想要的是一个类型类约束,以缩小您关心的
a

(forall a. Ord a => a -> a -> Bool) -> Number -> Number -> Bool
在您的特定情况下,即使这基本上是过度的,您也可以像前面提到的那样使用
Float
,因为这是
对于所有a.Ord a=>a->a->Bool
的最终特长


如果您有这个签名,我想您已经完成了(现在手头上没有Haskell编译器)。

打开
Rank2Types
并按以下方式编写函数类型:

createComparator :: (forall a. Ord a => a -> a -> Bool) -> Number -> Number -> Bool

在编写时,有一个特定的类型
a
,您正在为其传递一个比较器;而您希望传递一个函数,该函数在多态性上是许多类型的比较器(因此必须在其类型中提及
forall
).

是的,将类型签名设置为Float->Float->Bool可能会起作用,如果我比较我的Rational的Float版本,它可能也会起作用,例如:1/5为0.2。不过,这并不完全是我想要做的。不过,你的解决方案非常有效,谢谢。@Ramadoka,出于好奇,你打算做什么?直接比较
Rational
Float
而不需要任何转换?对不起,我的意思是,如果我在比较一个有理数和另一个有理数,我不希望它们首先转换为Float。例如:3/25>1/5=False。我真的不知道将整数转换为Float的行为,例如,如果我e a=1/3129292和b=99/3129292200,我认为假设a>b总是正确的并不安全。在这种情况下,我和@DanielWagner建议的多态性签名会让你明白这一点。不过,在比较a
Float
和a
Rational
之前,你仍然需要将它们转换为通用类型e比较。我认为你想得太多了。如果你在比较(Float f1)(Float f2)=比较f1 f2;比较..
的地方编写
实例Ord Number,那么你就可以免费获得所需的
函数(从
Ord
中的默认定义中获得)。不需要更高阶的函数(更不用说更高阶的类型了)。
(forall a. a -> a -> Bool) -> Number -> Number -> Bool
(forall a. Ord a => a -> a -> Bool) -> Number -> Number -> Bool
createComparator :: (forall a. Ord a => a -> a -> Bool) -> Number -> Number -> Bool