我正在研究Haskell。而我正试图制作一个while循环,每次运行它都会产生价值' a'之后用地板缩小2倍。
这很好用:
func a =
if a > 0 then
func (a/2)
else
print (a)
现在添加楼层功能。
func a =
if a > 0 then
func (floor (a/2))
else
print (a)
现在正在运行
func 5
有一个错误:
Ambiguous type variable ‘t0’ arising from a use of ‘func’
prevents the constraint ‘(Show t0)’ from being solved.
Probable fix: use a type annotation to specify what ‘t0’ should be.
These potential instances exist:
instance Show Ordering -- Defined in ‘GHC.Show’
instance Show Integer -- Defined in ‘GHC.Show’
instance Show a => Show (Maybe a) -- Defined in ‘GHC.Show’
...plus 22 others
...plus 12 instances involving out-of-scope types
(use -fprint-potential-instances to see them all)
• In the expression: func 1
In an equation for ‘it’: it = func 1
我有两个问题:
问题1:为什么会出现此错误?
问题2:我该如何解决?
我正在使用" repl.it"哈斯克尔。我会感激每一个帮助:)!
答案 0 :(得分:6)
问题是函数floor
返回Integral
类型,而/
函数(小数除法运算符)返回Fractional
类型。
所以你需要a
属于Integral
和Fractional
的类型,我认为这种类型不存在(其他Haskellers:随意纠正我)。
解决此问题的一种方法是让Integral
返回的floor
免费成为任何类型的数字:
func a =
if a > 0 then
func $ fromIntegral (floor (a/2))
else
print (a)
无论哪种方式,最好指定函数的类型。如果您在使用fromIntegral
:
func :: Double -> IO ()
答案 1 :(得分:2)
@Marcelo Zabani的回答是正确的,使代码工作,但错误信息本身实际上是由于不同的原因:defaulting rules。
如果GHC推断,让我们看一下你的原始函数的类型:
func a =
if a > 0 then
func (a/2)
else
print (a)
您使用/2
(需要Fractional
)和print
(需要Show
),这意味着GHC会推断出func
的类型:
func :: (Fractional a, Show a) => a -> IO ()
这是一个完美的类型。但是,当您想实际调用 func
时,您必须选择特定类型来调用它。你不能打电话给(Fractional a, Show a) => a -> IO ()
;你必须致电Double -> IO ()
,或Float -> IO ()
,等等。那么,GHC如何知道你想要哪一个"如果您func 5
?
这是默认开始的地方。默认情况下,Haskell会尝试所有可能的候选类型:
此列表被硬编码为GHC(和Haskell语言规范)。它按顺序尝试它们,并且它将类型变量默认为第一个将编译的类型。
在这种情况下,它会尝试Integer
,看看它不起作用,然后尝试Double
,然后看到它确实有效,并且调用func :: Double -> IO ()
。
现在,你的第二个功能:
func a =
if a > 0 then
func (floor (a/2))
else
print (a)
现在,因为您致电floor (a/2)
,现在推断a
有RealFrac
个实例。因为您使用结果调用func
,现在推断a
具有Integral
实例。因此,推断的类型签名是:
func :: (RealFrac a, Integral a, Show a) => a -> IO ()
这也是一种完美的类型。这就是为什么定义函数很好,并且没有发生错误。
但是,当你致电func 5
时会发生什么?
GHC再次尝试排除所有违约候选人。整数失败,Double失败,因此......默认无法解决歧义,因此您会收到编译时错误。
请注意,错误是不是,因为没有类型是所有三个类型类的实例。这在Haskell中永远不会导致错误,因为类型类是全局的。这会产生一些奇怪的情况,即在模块中为未被使用的类型声明的实例将导致代码编译或不编译。因此,如果在Haskell中不允许或发出给定类型类的 no 实例,则失败的值。
所以,即使存在所有Integral,RealFrac和Show的实例,你仍会出现错误。想象一下,有一个类型Foo
具有所有三个必要的实例。 即使在这种情况下,仍然会出现错误,因为Foo
不在GHC尝试的默认类型列表中。
问题是GHC需要帮助来确定你想要什么类型,因为它的默认列表已经用尽。
所以,你可以这样做:
func (5 :: Foo)
如果Foo
的实例为RealFrac
,Integral
,和 Show
,将起作用。
问题不在于没有实例存在 - 问题在于GHC并不知道选择哪一个。
一般来说,这就是"不明确的类型......"错误意味着:不没有实例存在,但GHC需要你的帮助来选择你想要实例化一个函数的类型,因为GHC不能从类型推断中推断出来。
让代码工作的实际修复来自于您可能希望事情首先与Double
一起使用。如果您为func
提供了类型签名,那么错误将变为No instance for Integral Double
,这是"实际"你的代码的基本问题。