What type signature or annotations are needed for this Haskell function?

时间:2016-02-03 03:59:49

标签: haskell

I'm new to Haskell and I'm having trouble understanding exactly which type annotations or which type signature is needed to get this to work. This test function itself is useless, but if I can understand how to get it to work, I think it'd help me to understand haskell types better.

test 0 = -1
test n = test (floor (n / 10))

This question seems to be related: Haskell: Why does RealFrac not imply Fractional?

But I can't figure out how to get my example to work. Here's the error I see when I run test 0:

<interactive>:470:1:
Could not deduce (Integral a10) arising from a use of ‘test’
from the context (Num a)
  bound by the inferred type of it :: Num a => a
  at <interactive>:470:1-6
The type variable ‘a10’ is ambiguous
Note: there are several potential instances:
  instance Integral Foreign.C.Types.CChar
    -- Defined in ‘Foreign.C.Types’
  instance Integral Foreign.C.Types.CInt
    -- Defined in ‘Foreign.C.Types’
  instance Integral Foreign.C.Types.CIntMax
    -- Defined in ‘Foreign.C.Types’
  ...plus 28 others
In the expression: test 0
In an equation for ‘it’: it = test 0

<interactive>:470:6:
Could not deduce (Num a10) arising from the literal ‘0’
from the context (Num a)
  bound by the inferred type of it :: Num a => a
  at <interactive>:470:1-6
The type variable ‘a10’ is ambiguous
Note: there are several potential instances:
  instance RealFloat a => Num (Data.Complex.Complex a)
    -- Defined in ‘Data.Complex’
  instance Data.Fixed.HasResolution a => Num (Data.Fixed.Fixed a)
    -- Defined in ‘Data.Fixed’
  instance forall (k :: BOX) (f :: k -> *) (a :: k).
           Num (f a) =>
           Num (Data.Monoid.Alt f a)
    -- Defined in ‘Data.Monoid’
  ...plus 42 others
In the first argument of ‘test’, namely ‘0’
In the expression: test 0
In an equation for ‘it’: it = test 0

2 个答案:

答案 0 :(得分:6)

您需要了解一些关于Haskell数字的基本知识:

  1. Haskell

  2. 中没有隐式数字类型转换
  3. 运算符重载数量有限,但仅适用于类型类的成员

  4. 有很多数字类型,每个类都支持不同的操作集

  5. 某些类型暗示其他类;例如,它们都暗示Num,因此Num上的运算符适用于所有数字

  6. 你的问题的本质是你有相互矛盾的类型约束,但是编译器不足以告诉你,你需要的约束是荒谬的,而不仅仅是模糊或缺失。

    之所以产生矛盾,是因为“真正的除法”运算符/位于Fractional类型类中。但是您也使用floor,其返回类型约束为Integral类。因此,为了理所当然,您需要一个同时为IntegralFractional的数字类型,这是没有意义的。

    有几种方法可以解决这个矛盾:

    1. 不要使用“真正的分裂”;使用div

    2. 中定义的Integral
    3. 使用fromIntegralIntegral参数转换为/合适的Fractional成员

    4. 当我使用数字代码并试图找出约束时,我经常采用这种方法:

      我使用我想要的一般排序的具体数字类型在顶级函数上打了一些类型注释;通常是Int或Double,取决于什么类型的计算。对于test函数,我会写test :: Int -> Int。在这一点上,GHC会给我一个简单的错误信息:

      No instance for (RealFrac Int) arising from a use of ‘floor’
      In the first argument of ‘test’, namely ‘(floor (n / 10))’
      In the expression: test (floor (n / 10))
      In an equation for ‘test’: test n = test (floor (n / 10))
      
      No instance for (Fractional Int) arising from a use of ‘/’
      In the first argument of ‘floor’, namely ‘(n / 10)’
      In the first argument of ‘test’, namely ‘(floor (n / 10))’
      In the expression: test (floor (n / 10))
      

      现在我知道/floor期待Fractional类型;我要么需要使用一些显式类型转换来满足它们,尝试不同的类型,或者找一些其他运算符!如果我尝试test :: Double -> Double,我会收到类似的错误:

      No instance for (Integral Double) arising from a use of ‘floor’
      In the first argument of ‘test’, namely ‘(floor (n / 10))’
      In the expression: test (floor (n / 10))
      In an equation for ‘test’: test n = test (floor (n / 10))
      

      此时,很明显单一类型不会满足这些约束。该算法看起来好像需要Integral,因此我会在Prelude页面搜索“除法”并查看其中的内容。

      幸运的是,Integral提供了两种不同的除法运算,quotdiv。查看描述,看起来div/的语义匹配,后跟floor,因此可以用来消除类型错误并简化函数!

      现在我可以将其保留为Int -> Int函数,或者我可以将其再次概括为Integral a => a -> a。取决于我是否会在其他情境中使用它,或者如果它是特定的一部分,那么很明显不需要更通用的类型。

答案 1 :(得分:4)

test的类型为(Integral a1, Num a, RealFrac a1) => a1 -> a。此类型不可用,因为没有IntegralRealFrac的可用类型。

为什么?您正在使用n / 10,该部门需要Fractional个参数,ntest的参数,因此test的参数必须为{{1} }}。它必须是Fractional,因为您随后将分部的结果提供给RealFracfloor接受floor参数。 OTOH您正在使用RealFractest (floor ...)会返回floor,因此Integral的参数也必须是test

如果您想要Integral类型,例如test,则需要将Integral a => a->a转换为积分,以便可以执行除法和n

floor