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

main = print (toFloat "1")

给我错误:

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

我确定我缺少一些基本的东西,但似乎我的 toFloat 应该总是返回一个 Float 并且 Float 应该暗示 Floating。

4

4 回答 4

18

类型签名承诺结果将是Floating调用者想要的类的任何实例。实现说“知道吗?不要介意它可以是任何类型的承诺 - 让我们把它变成一个Float”。

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

不幸的是,它报告的错误有点不透明,因为它付出了多少努力来尝试使您的代码正常工作。只需最微小的更改,添加一点限制,就可以让它变得正确。因此它报告该约束不存在。但它没有报告它为什么要寻找那个约束,如果你以前没有看过它,这会让整个事情变得有点不清楚。

于 2012-12-30T06:26:05.090 回答
8

这个问题或与之非常相似的问题会定期被问到。(这不是抱怨,我只是指出您不是唯一对此感到困惑的人。)

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

Haskell 不是那样工作的。如果你说“这个函数返回了实现 X 的东西”,那么这个函数必须能够产生任何实现 X 的可能类型!

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

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

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

于 2012-12-30T12:38:56.657 回答
5

您是说toFloat可以返回属于Floatingtypeclass 的任何类型,但您将其限制为Float,这是错误的。你的函数是多态的,a所以你不能返回一个实例Floating,它应该能够与所有实例一起工作。

否则,您可以通过以下方式理解这一点

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

在 ghci

*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

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

另一种理解你的假设有多可怕的方法是考虑函数read

read :: Read a => String -> a

现在根据你的说法,你可以只返回 sayInt一切,因为Int有一个Read. 现在你可以理解如果你做类似的事情会发生什么

read "12" +  (1.2 :: Double)
于 2012-12-30T06:19:44.540 回答
1

这很简单:

-- The simplest.                                                                                                                                               
toFloat :: String -> Float
toFloat = read

-- More generalized.                                                                                                                                           
toFloat' :: (Floating a, Read a) => String -> a
toFloat' = read
于 2012-12-30T08:28:47.877 回答