如果是模棱两可的类型,如何确定类型?

How to figure out the type if it is ambiguous type?

我是 Haskell 初学者,我在理解这一点上遇到了一些困难。我在这样的文件中定义了 2 个函数:

expr n x = (x ^ n)/(fact n)


fact n
  | n == 0 = 1
  | otherwise = n * fact (n - 1)

但是当我尝试 运行 说 expr 3 2 时,我不断收到这样的错误:

*Main> expr 3 2

<interactive>:31:1: error:
    • Ambiguous type variable ‘a0’ arising from a use of ‘print’
      prevents the constraint ‘(Show a0)’ from being solved.
      Probable fix: use a type annotation to specify what ‘a0’ 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 19 instances involving out-of-scope types
        (use -fprint-potential-instances to see them all)
    • In a stmt of an interactive GHCi command: print it

我检查了各种表达式的类型,我看到了这个:

*Main> :t (/)
(/) :: Fractional a => a -> a -> a
*Main> :t (^)
(^) :: (Integral b, Num a) => a -> b -> a
*Main> :t 3 ^ 6
3 ^ 6 :: Num a => a
*Main> :t fact
fact :: (Eq p, Num p) => p -> p
*Main> :t fact 3
fact 3 :: (Eq p, Num p) => p
*Main> :t 9 / 6
9 / 6 :: Fractional a => a

但是我不知道应该为我的函数提供什么类型来避免这种情况。我尝试为我的事实函数提供这样的类型,因为 (/) 需要 Fractional:

fact :: (Fractional a) => Int -> a

但是如果我尝试提供我的函数类型,该文件甚至不会加载到 ghci 中。如果有帮助,我可以 post 我尝试加载类型文件时收到的错误消息。

如果我尝试将类型添加到计算表达式时使用的显示,那也不起作用:

*Main> expr 3 2 :: String

<interactive>:37:1: error:
    • No instance for (Fractional String) arising from a use of ‘expr’
    • In the expression: expr 3 2 :: String
      In an equation for ‘it’: it = expr 3 2 :: String

<interactive>:37:6: error:
    • No instance for (Num String) arising from the literal ‘3’
    • In the first argument of ‘expr’, namely ‘3’
      In the expression: expr 3 2 :: String
      In an equation for ‘it’: it = expr 3 2 :: String
*Main> expr 3 2 :: Integer

<interactive>:38:1: error:
    • No instance for (Fractional Integer) arising from a use of ‘expr’
    • In the expression: expr 3 2 :: Integer
      In an equation for ‘it’: it = expr 3 2 :: Integer

如评论中所述,运算符 / 就像 + - * 一样,强制其两个操作数具有相同的类型(这也是结果的类型)。运算符 ^ 是一个例外,其结果必须与其左操作数具有相同的类型。请注意,对于浮点类型,您还可以使用 Fortran 中的 ** 求幂运算符。

在此 tutorial 中对规则进行了更详细的解释。

不幸的是,这意味着混合模式算术在 Haskell 中比在一些著名的命令式语言中更难获得。如果需要,可以使用转换函数fromIntegral :: (Integral a, Num b) => a -> b。它允许编译器插入一些适当的转换。

对于目前的情况,如果需要宽类型签名,可以这样实现,例如:

{-#  LANGUAGE  ScopedTypeVariables  #-}
{-#  LANGUAGE  ExplicitForAll       #-}

fact :: Integral nt => nt -> nt
fact n = if (n <= 0)  then  1  else  n * fact (n - 1)

expr :: forall nt ft.  (Integral nt, Fractional ft) => nt -> ft -> ft 
expr n x = (x ^ n) / ((fromIntegral (fact n)) :: ft)

测试 ghci:

$ ghci
GHCi, version 8.6.5: http://www.haskell.org/ghc/  :? for help
 λ> 
 λ> :load q65254539.hs
...
Ok, one module loaded.
 λ> 
 λ> expr 2 3
 4.5
 λ> expr 2 pi
 4.934802200544679
 λ> 
 λ> expr 3 (4::Double)
 10.666666666666666
 λ>