2

注意:这里的完整源代码:https ://gist.github.com/anonymous/7085509

我有以下功能:

tournament n p pop = do
    winner <- (\w -> min (n - 1) (floor (log w / log (1-p)))) <$> gaRandom
    (flip S.index) winner <$> S.sort <$> seqChoose n pop

如果没有类型签名,编译器会告诉我tournament签名是:

tournament
  :: (Floating a, Ord a1, RealFrac a, Random a) =>
     Int -> a -> S.Seq a1 -> StateT GA Data.Functor.Identity.Identity a1

这对我来说看起来不错。但是当我使用它时:

t2 = do
    g <- newStdGen
    let a = evalState (tournament 5 0.9 (S.fromList [1..10])) (GA g)
    return ()

我得到错误:

GA.hs:85:37:
    No instance for (Fractional a0) arising from the literal `0.9'
    The type variable `a0' is ambiguous
    Possible fix: add a type signature that fixes these type variable(s)
    Note: there are several potential instances:
      instance Fractional Double -- Defined in `GHC.Float'
      instance Fractional Float -- Defined in `GHC.Float'
      instance Integral a => Fractional (GHC.Real.Ratio a)
        -- Defined in `GHC.Real'
      ...plus three others
    In the second argument of `tournament', namely `0.9'
    In the first argument of `evalState', namely
      `(tournament 5 0.9 (S.fromList [1 .. 10]))'
    In the expression:
      evalState (tournament 5 0.9 (S.fromList [1 .. 10])) (GA g)

这引出了我的第一个问题,为什么不RealFrac暗示Fractional?类型签名具有 RealFrac,但错误抱怨缺少 Fractional 的实例。

其次,我将类型签名复制并粘贴回代码中并添加Fractional a

tournament 
  :: (Floating a, Ord a1, RealFrac a, Fractional a, Random a) =>
     Int -> a -> S.Seq a1 -> State GA a1
tournament n p pop = do
    winner <- (\w -> min (n - 1) (floor (log w / log (1-p)))) <$> gaRandom
    (flip S.index) winner <$> S.sort <$> seqChoose n pop

现在我得到的错误是:

GA.hs:88:24:
    No instance for (Random a0) arising from a use of `tournament'
    The type variable `a0' is ambiguous
    Possible fix: add a type signature that fixes these type variable(s)
    Note: there are several potential instances:
      instance Random Bool -- Defined in `System.Random'
      instance Random Foreign.C.Types.CChar -- Defined in `System.Random'
      instance Random Foreign.C.Types.CDouble
        -- Defined in `System.Random'
      ...plus 33 others
    In the first argument of `evalState', namely
      `(tournament 5 0.9 (S.fromList [1 .. 10]))'
    In the expression:
      evalState (tournament 5 0.9 (S.fromList [1 .. 10])) (GA g)
    In an equation for `a':
        a = evalState (tournament 5 0.9 (S.fromList [1 .. 10])) (GA g)

这现在让我更加困惑,因为我没有类型变量a0这导致了我的第二个问题:显然我误解了一些东西,但是什么?

4

2 回答 2

7

简而言之,您需要为0.9like修复一个具体类型Double。您可以使用内联类型注释来做到这一点(0.9 :: Double)

长篇大论:数字文字在 Haskell 中有点奇怪。一般来说,Haskell 需要一种方法将语法(0, 0.0, 0e0)投影到语义(Int, Integer, Rational, Double)中,同时尽可能长时间地保持通用性(Num, Fractional, RealFrac)。让我们看看它是如何完成的。

如果您自己键入数字文字,您将获得泛型类型

>>> :t 1
1 :: Num a => a

>>> :t 1.0
1.0 :: Fractional a => a

>>> :t 1e0
1e0 :: Fractional a => a

这意味着我们需要先修复它的具体实现,a然后才能使用它。在实践中,这种类型变量a会随身携带

>>> :t [1,2,3]
[1,2,3] :: Num a => [a]
>>> :t [1e0,2,3]
[1e0,2,3] :: Fractional a => [a]

如果它有帮助,将语法视为这样翻译会很有用

1     ===   fromInteger  (1   :: Integer)   :: Num a        => a
1.0   ===   fromRational (1.0 :: Rational)  :: Fractional a => a

但是我们可以在不同的时间消除类型变量

>>> :t show 3
show 3 :: String

当我们从未声明过 3 时,Haskell 怎么知道它的类型?如果可能,它会默认设置。特别是,如果你打开-Wall你会看到这个

>>> show 1e3

<interactive>:63:6: Warning:
    Defaulting the following constraint(s) to type `Double'
      (Fractional a0)
        arising from the literal `1e3' at <interactive>:63:6-8
      (Show a0) arising from a use of `show' at <interactive>:63:1-4
    In the first argument of `show', namely `1e3'
    In the expression: show 1e3
    In an equation for `it': it = show 1e3

"1000.0"

这种默认行为由一个几乎从未使用过的 pragma 控制default,“默认情况下”是

default (Integer, Double)

哪个作为

Each defaultable variable is replaced by the first type in the default list 
that is an instance of all the ambiguous variable's classes. It is a static 
error if no such type is found.

所以,可能发生的事情是你被限制0.9在一些Double没有实例化的类上。在搜索过程中,Haskell 在找不到Fractional类后放弃了,它引入了新a0变量来表示这种迄今为止未被引用、未知类型的0.9.

如前所述,您可能需要一个内联注释Double来帮助推理器。可以添加到您的default列表中,但这是一个坏主意,因为人们很少使用该功能。

于 2013-10-21T15:42:30.613 回答
2

问题不在于类型类,而是 GHC 不知道将哪个实例用于 `(Fractional a, RealFrac a, Floating a, Random a)。如果您将其指定为

tournament 5 (0.9 :: Double) (S.fromList [1..10])

然后它应该可以工作(或者至少使用你的要点它对我有用)

于 2013-10-21T15:39:22.180 回答