Haskell类型类(Float并不意味着Float?)

Haskell类型类(Float并不意味着Float?),haskell,Haskell,给我一个错误: toFloat :: (Floating a) => String -> a toFloat s = read s :: Float main = print (toFloat "1") 我确信我遗漏了一些基本的东西,但看起来我的toFloat应该总是返回一个Float,而这个Float应该意味着Float。你说的toFloat可以返回属于Floating类型类的任何类型,但是你把它限制为Float,这是错误的。你的函数在a中是多态的,所以你不能返回一个浮动的实例

给我一个错误:

toFloat :: (Floating a) => String -> a
toFloat s = read s :: Float

main = print (toFloat "1")

我确信我遗漏了一些基本的东西,但看起来我的toFloat应该总是返回一个Float,而这个Float应该意味着Float。

你说的
toFloat
可以返回属于
Floating
类型类的任何类型,但是你把它限制为
Float
,这是错误的。你的函数在
a
中是多态的,所以你不能返回一个
浮动的
实例,它应该能够处理所有实例

否则,你可以通过

Could not deduce (a ~ Float)
from the context (Floating a)
在ghci中

toFloat :: (Read a,Floating a) => String -> a
toFloat s = read s
由于它返回属于typeclass
Floating
的类型,您应该能够在应用函数后通过提供显式类型签名将其转换为任何类型(属于
Floating
)。 另一方面,请记住当显式返回
Float
时的情况。现在,您不能只说我希望从这个函数中获得
Double
,因为如果没有显式转换,这是不可能发生的

另一种理解你的假设有多么可怕的方法是考虑函数<代码> Read < /Cult>

*Main> :t toFloat "12.1"
toFloat "12.1" :: (Floating a, Read a) => a
*Main> :t (toFloat "12.1" :: Float)
(toFloat "12.1" :: Float) :: Float
*Main> :t (toFloat "12.1" :: Double)
(toFloat "12.1" :: Double) :: Double
现在根据您的说法,您可以返回say
Int
,因为
Int
有一个
Read
的实例。现在你可以明白如果你做了这样的事情会发生什么

read :: Read a => String -> a

类型签名保证结果将是调用者想要的
浮动
类的任何实例。实现说“知道什么?不要承诺它可以是任何类型-让我们把它变成一个
浮点数

然后编译器出现并说:“哇!你没有返回你承诺会返回的类型。”只是它非常努力地使你的类型签名和你的实现匹配。它对自己说:“如果这受到某种限制,使得
a
始终与
Float
相同,那么这将是正确的。”它真的想找到一种方法使代码正确。编写这样一个约束的方法是使用类型相等运算符
~
(A~Float)
的约束意味着“
A
Float
的类型相同”。因此,编译器检查您在类型签名中提供的上下文,但它找不到该约束。而且它无法使您的类型签名和实现协同工作、放弃并报告错误


不幸的是,它报告的错误有点不透明,因为它在使代码正常工作方面付出了很多努力。哪怕是最微小的改变,再加上一点约束,都会使它变得正确。所以它报告说这个约束不存在。但是它没有报告它为什么要寻找这个约束,如果你以前没有见过它,那么整个事情就有点不清楚了。

这很简单:

read "12" +  (1.2 :: Double)

这个问题或类似的问题经常被问到。(这不是抱怨,我只是指出,你并不是唯一一个对此感到困惑的人。)

在OO语言中,您可以说“这个函数返回实现X的东西”。然后,函数可以返回它想要返回的任何东西,只要它确实实现了X

哈斯克尔不是那样工作的。如果您说“此函数返回实现X的内容”,那么该函数必须能够生成实现X的任何可能类型

关键区别在于:在OO语言中,函数决定返回什么类型(在指定的约束内)。在Haskell中,调用方决定返回什么类型(同样,在规定的约束范围内)

一旦你理解了这一关键区别,剩下的就不言而喻了


同样,很多人似乎误解了这一部分。我们可能应该在教程和其他内容中更多地提到它,因为它似乎是一个VFAQ…

感谢您的快速响应。这对我来说完全澄清了这个问题。不知何故,函数的调用者可能会要求我的typeclass的一个特定实例,但它并没有为我点击。
-- The simplest.                                                                                                                                               
toFloat :: String -> Float
toFloat = read

-- More generalized.                                                                                                                                           
toFloat' :: (Floating a, Read a) => String -> a
toFloat' = read