第6章,练习7,Haskell从第一原理开始

Joe*_*Joe 3 haskell

对于以下代码:

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)
Run Code Online (Sandbox Code Playgroud)

在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)
   |          ^^^^^^^^^^^^^^^^^^
Run Code Online (Sandbox Code Playgroud)

我以为我可以通过给编译器一个类型提示

print ((Hello (2 :: Int)) == (Hello (2 :: Int)))
Run Code Online (Sandbox Code Playgroud)

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

接下来,我将数据类型和typeclass实例加载到ghci中,并进行了一些探索,发现例如

?> :t Hello (2 :: Int)
Hello (2 :: Int) :: EitherOr Int b
Run Code Online (Sandbox Code Playgroud)

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

?>:t(Hello 2 == Hello 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
Run Code Online (Sandbox Code Playgroud)

当然是我想要的。

然后我实际上在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
Run Code Online (Sandbox Code Playgroud)

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

Tho*_*son 5

GHCi中的类型默认规则与编译程序(例如使用GHC)时使用的规则不同。如果类型不明确,则应给出明确的签名,例如:

print (Hello 2 == (Hello 2 :: EitherOr Integer ())
Run Code Online (Sandbox Code Playgroud)

实际上,并不需要太多,因为程序的其他部分隐含了类型。上面的玩具和教育性摘要在上下文方面没有太多可以为类型检查器添加信息的方式。