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

为什么GHC的类型检查器接受这种UndeterminedInstance技巧?

  •  1
  • 141592653  · 技术社区  · 11 月前

    GHC接受以下代码。

    {-# LANGUAGE FlexibleInstances #-}
    {-# LANGUAGE UndecidableInstances  #-}
    
    class Blib b where
      blib :: b
    
    class Blob b where
      blob :: b
    
    instance Blib b => Blob b where
      blob = blib
    
    instance Blob b => Blib b where
      blib = blob
    
    bad :: ()
    bad = blib () 4 'a' -- I can add any number of arguments of any type, it still compiles
    
    main ::IO ()
    main = return bad
    
    

    更多好奇心:

    • 当我运行可执行文件时,它以某种方式“正常”工作
    • 当我在GHCi中加载它并键入时 bad 它悬挂着GHCi。
    • 如果我删除以下声明 blib blob 如下所示,代码不再挂起GHCi:
    instance Blib b => Blob b where
      -- blob = blib
    
    instance Blob b => Blib b where
      -- blib = blob
    

    发生什么事了?

    1 回复  |  直到 11 月前
        1
  •  4
  •   Carl    11 月前

    这与…无关 UndecidableInstances 真的。扩展所做的唯一一件事就是允许你的实例声明编译:

    instance Blib b => Blob b where
    

    当您对实例施加约束时,受约束的类型必须“小于”实例头部。例如:

    instance Foo a => Foo [a]
    

    该约束仅涵盖 a ,它是实例头部的一个子部分 [a] 这总是被允许的。但是,您的定义对与实例头相同的类型有约束。这是禁止的 无法确定的实例 .

    但你观察到的所有其他行为都与此无关。你会得到完全相同的结果,比如:

    blib :: b
    blib = blob
    
    blob :: b
    blob = blib
    
    bad :: ()
    bad = blib () 4 'a'
    
    main ::IO ()
    main = return bad
    

    blib 接受任意数量的参数,因为它实际上是任何类型(种类)的 Type ). 在这种情况下,推理(并将数字文字默认为 Integer 因为没有指定它)决定了它的类型为 () -> Integer -> Char -> () .声明的类型 b 与此统一,因此没有类型错误。

    编译后的程序运行良好,因为它从不求值 bad 编译后的程序运行IO操作 main ,并忽略IO产生的值。由于运行IO从不计算 坏的 整件事都变成了无行动。

    相比之下,ghci永远循环,因为评估 坏的 打印它会导致无限的相互递归。

    作为参考,ghci使用输入的裸露表达式做了几件事。值得记住的是,它按以下顺序尝试这些事情:

    1. 如果表达式的推断类型与 IO () ,它运行IO操作,不打印任何内容。
    2. 如果表达式的推断类型与 Show a => IO a ,它运行IO操作并打印结果。
    3. 如果表达式的推断类型与 IO a ,它运行IO操作,不打印任何内容。
    4. 它将输入视为 print <expr> 对于您输入的任何表达式,并按照上述情况1进行操作。