代码之家  ›  专栏  ›  技术社区  ›  Joe

第6章,练习7,Haskell from First Principles

  •  0
  • Joe  · 技术社区  · 5 年前

    对于以下代码:

    module Main where
    
    data EitherOr a b = Hello a | Goodbye b deriving Show
    
    instance (Eq a, Eq b) => Eq (EitherOr a b) where
      (==) (Hello x) (Hello x') = x == x'
      (==) (Goodbye x) (Goodbye x') = x == x'
      (==) _ _ = False
    
    main :: IO ()
    main = do
      print (Hello 2 == Hello 2)
      -- print (Hello 3 == Hello 2)
      -- print (Goodbye 3 == Goodbye 3)
      -- print (Goodbye 4 == Goodbye 3)
      -- print (Hello 3 == Goodbye 3)
    

    在runhaskell下执行,即在ghc下执行,得到以下错误:

        • Ambiguous type variable ‘b0’ arising from a use of ‘==’
          prevents the constraint ‘(Eq b0)’ from being solved.
          Probable fix: use a type annotation to specify what ‘b0’ should be.
          These potential instances exist:
            instance Eq Ordering -- Defined in ‘GHC.Classes’
            instance Eq Integer
              -- Defined in ‘integer-gmp-1.0.2.0:GHC.Integer.Type’
            instance (Eq a, Eq b) => Eq (EitherOr a b)
              -- Defined at /tmp/runghcXXXX61964-0.hs:5:10. <-- This is because I am using org-mode source blocks
            ...plus 23 others
            ...plus 11 instances involving out-of-scope types
            (use -fprint-potential-instances to see them all)
        • In the first argument of ‘print’, namely ‘(Hello 2 == Hello 2)’
          In a stmt of a 'do' block: print (Hello 2 == Hello 2)
          In the expression: do print (Hello 2 == Hello 2)
       |
    12 |   print (Hello 2 == Hello 2)
       |          ^^^^^^^^^^^^^^^^^^
    

    我想我可以给编译器一个类型提示

    print ((Hello (2 :: Int)) == (Hello (2 :: Int)))
    

    或者类似的,但这似乎还不够。我看到a和b是多态的,但我认为在main中使用==可能足以帮助编译器推断类型。

    接下来,我在ghci中加载了数据类型和类型类实例,并对这些类型进行了一些探索,发现

    λ> :t Hello (2 :: Int)
    Hello (2 :: Int) :: EitherOr Int b
    

    如预期。再次在ghci中,我做了更多的探索,并看到正在使用默认类型

    >:t(你好2==你好2)

    <interactive>:1:2: warning: [-Wtype-defaults]
        • Defaulting the following constraints to type ‘Integer’
            (Eq a0) arising from a use of ‘==’ at <interactive>:1:2-19
            (Num a0) arising from the literal ‘2’ at <interactive>:1:8
        • In the expression: (Hello 2 == Hello 2)
    
    <interactive>:1:2: warning: [-Wtype-defaults]
        • Defaulting the following constraint to type ‘()’
            Eq b0 arising from a use of ‘==’
        • In the expression: (Hello 2 == Hello 2)
    (Hello 2 == Hello 2) :: Bool
    

    当然,这是我想要的。

    然后我实际在ghci中执行代码,得到正确的答案

    λ> Hello 2 == Hello 2
    
    <interactive>:27:1: warning: [-Wtype-defaults]
        • Defaulting the following constraints to type ‘Integer’
            (Eq a0) arising from a use of ‘==’ at <interactive>:27:1-18
            (Num a0) arising from the literal ‘2’ at <interactive>:27:7
        • In the expression: Hello 2 == Hello 2
          In an equation for ‘it’: it = Hello 2 == Hello 2
    
    <interactive>:27:1: warning: [-Wtype-defaults]
        • Defaulting the following constraint to type ‘()’
            Eq b0 arising from a use of ‘==’
        • In the expression: Hello 2 == Hello 2
          In an equation for ‘it’: it = Hello 2 == Hello 2
    
    <interactive>:27:1: warning: [-Wtype-defaults]
        • Defaulting the following constraints to type ‘Integer’
            (Eq a0) arising from a use of ‘==’ at <interactive>:27:1-18
            (Num a0) arising from the literal ‘2’ at <interactive>:27:7
        • In the expression: Hello 2 == Hello 2
          In an equation for ‘it’: it = Hello 2 == Hello 2
    
    <interactive>:27:1: warning: [-Wtype-defaults]
        • Defaulting the following constraint to type ‘()’
            Eq b0 arising from a use of ‘==’
        • In the expression: Hello 2 == Hello 2
          In an equation for ‘it’: it = Hello 2 == Hello 2
    True
    

    但是在runhaskell下执行的相同代码,即在ghc编译下,由于我首先给出的错误而失败。我需要在这里学到什么?

    1 回复  |  直到 5 年前
        1
  •  3
  •   Thomas M. DuBuisson    5 年前

    ghci中的类型默认规则与编译程序(如使用ghc)时使用的规则不同。当类型不明确时,应提供显式签名,例如:

    print (Hello 2 == (Hello 2 :: EitherOr Integer ())
    

    实际上,这并不需要太频繁,因为类型是由程序的其他部分隐含的。像上面这样的玩具和教育片段在上下文方面没有太多的内容可以为类型检查器添加信息。