Haskell 类型错误: 推断类型不够多态

3

我目前在研究Haskell,但有一点让我困惑,就是我无法弄清楚以下代码为什么可以正常工作......

square :: (Num a) => a -> a 
square x = x * x 
dx = 0.0000001
deriv1 :: (Fractional a) => (a -> a) -> (a -> a)
deriv1 g = (\x -> ((g (x + 2) - (g x)) / 0.0000001 ))   
main = printf "res==%g %g\n" (square 5.12::Double) ((deriv1 square) 2::Float)

但这并不意味着......
square :: (Num a) => a -> a 
square x = x * x 
dx = 0.0000001
deriv1 :: (Fractional a) => (a -> a) -> (a -> a)
deriv1 g = (\x -> ((g (x + 2) - (g x)) / dx ))          
main = printf "res==%g %g\n" (square 5.12::Double) ((deriv1 square) 2::Float)

请注意,这次在derv1函数中我使用了dx。我对Haskell不熟悉,所以任何关于类型的深入讨论都可能过于复杂而无法理解。因此,必须给出类似于命令式语言的答案,否则在我的Haskell学习早期几乎肯定会迷失方向。

我得到的错误消息如下:

Inferred type is less polymorphic than expected
  Quantified type variable `a' is mentioned in the environment:
    dx :: a (bound at sicp-1.40.hs:12:0)
When trying to generalise the type inferred for `deriv1'
  Signature type:     forall a. (Fractional a) => (a -> a) -> a -> a
  Type to generalise: (a -> a) -> a -> a
In the type signature for `deriv1'
When generalising the type(s) for `deriv1'
3个回答

6
由于单态限制,您会遇到错误。由于您没有给dx指定类型签名,在这种情况下,它被推断为Double。您可以提供一个显式的多态签名,例如:
dx :: Fractional a => a
dx = 0.0000001

或者您可以在源文件顶部包含以下行以禁用单态限制

{-# LANGUAGE NoMonomorphismRestriction #-}

4

避免陷入单态限制的最佳方法可能是将dx设为本地变量:

deriv1 :: (Fractional a) => (a->a) -> a->a
deriv1 g = (\x -> ((g (x + dx) - (g x)) / dx ))
   where dx = 0.0000001

请注意,我也将dx中的2进行了更改,因为在你的定义中有些不正确。(这不是编程方面的错误,而是数学上的错误。)
顺便说一句,你还可以简单地写成这样。
deriv1 g x = (g (x + dx) - g x) / dx

Haskell会自动将其翻译为lambda表达式。


2
由于单态限制,dx 的类型默认为 Double。因此,在 deriv1 中除以 dx 时,Haskell 推断出另一个操作数和结果也必须具有 Double 类型。但由于你的类型签名说的是 a,所以你会得到错误提示。
你可以通过显式声明 dx 的类型为 Fractional a => a 或者禁用单态限制来解决这个问题。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接