错误:使用'func'产生的模糊类型变量't0'可以防止约束'(Show t0)'被解决

时间:2018-03-22 12:13:53

标签: haskell

我正在研究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"哈斯克尔。我会感激每一个帮助:)!

2 个答案:

答案 0 :(得分:6)

问题是函数floor返回Integral类型,而/函数(小数除法运算符)返回Fractional类型。

所以你需要a属于IntegralFractional的类型,我认为这种类型不存在(其他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会尝试所有可能的候选类型:

  1. 整数
  2. 此列表被硬编码为GHC(和Haskell语言规范)。它按顺序尝试它们,并且它将类型变量默认为第一个将编译的类型。

    在这种情况下,它会尝试Integer,看看它不起作用,然后尝试Double,然后看到它确实有效,并且调用func :: Double -> IO ()

    现在,你的第二个功能:

    func a = 
        if a > 0 then 
            func (floor (a/2))
        else 
            print (a)
    

    现在,因为您致电floor (a/2),现在推断aRealFrac个实例。因为您使用结果调用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的实例为RealFracIntegral Show

    将起作用。

    问题不在于没有实例存在 - 问题在于GHC并不知道选择哪一个。

    一般来说,这就是"不明确的类型......"错误意味着:没有实例存在,但GHC需要你的帮助来选择你想要实例化一个函数的类型,因为GHC不能从类型推断中推断出来。

    让代码工作的实际修复来自于您可能希望事情首先与Double一起使用。如果您为func提供了类型签名,那么错误将变为No instance for Integral Double,这是"实际"你的代码的基本问题。