为什么Haskell要求数字为printf消除歧义而不是show?

Ric*_*ruz 6 printf haskell types hindley-milner type-signature

为什么printf "%d\n" 3暧昧而不是show 3?是否printf可以重写模块以提供自动消除歧义?据推测,show必须在较低层次上完成printf......或者是否存在一些重要的区别printf,show这需要消除数字的歧义?

如果printf 可以重写以自动处理数字而不明确消除歧义,那么正在show做什么?如何在show:: Int消除歧义的情况下将数字转换为字符串printf

这是show(没有消除歧义)的正确操作以及printf(消除歧义)的正确操作:

$ cat printStrLnShow3
import Text.Printf
main = putStrLn (show 3)
$ runghc printStrLnShow3
3
$ cat printfWithInt3
import Text.Printf
main = printf "%d\n" (3 :: Int)
$ runghc printfWithInt3
3
Run Code Online (Sandbox Code Playgroud)

这里是不明确的变量时,错误printf没有歧义的数量:

$ cat printfWithAmbiguous3
import Text.Printf
main = printf "%d\n" 3
$ runghc printfWithAmbiguous3

printfWithAmbiguous3:2:8:
    No instance for (PrintfArg a0) arising from a use of `printf'
    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 [safe] PrintfArg Char -- Defined in `Text.Printf'
      instance [safe] PrintfArg Double -- Defined in `Text.Printf'
      instance [safe] PrintfArg Float -- Defined in `Text.Printf'
      ...plus 12 others
    In the expression: printf "%d" 3
    In an equation for `main': main = printf "%d" 3

printfWithAmbiguous3:2:22:
    No instance for (Num a0) arising from the literal `3'
    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 Num Double -- Defined in `GHC.Float'
      instance Num Float -- Defined in `GHC.Float'
      instance Integral a => Num (GHC.Real.Ratio a)
        -- Defined in `GHC.Real'
      ...plus 11 others
    In the second argument of `printf', namely `3'
    In the expression: printf "%d" 3
    In an equation for `main': main = printf "%d" 3
Run Code Online (Sandbox Code Playgroud)

Dan*_*ner 12

这是默认规则的一个怪癖,它明确表示只有在上下文中有一组固定的类时,才可以将类型类多态值默认为单态的:

在发现模糊类型的情况下,如果出现以下情况,则模糊类型变量v是可以违约的:

  • v仅出现在表单的约束中C v,其中C是一个类,和
  • 这些类中至少有一个是数字类(即,Num或其子类Num),和
  • 所有这些类都在Prelude标准库中定义(图6.2-6.3显示了数字类,图6.1显示了在Prelude.中定义的类.)

每个默认变量都由默认列表中的第一个类型替换,该类型是所有模糊变量类的实例.如果没有找到这样的类型,则是静态错误.

(报告的第4.3.4节.)这里令人不安的是第3点,因为PrintfArg a对类型的限制3 :: (Num a, PrintfArg a) => a提到了PrintfArg不在的类Prelude.

GHC提供了ExtendedDefaultRules放松这些规则的实用工具,如手册所述:

找到所有未解决的约束.然后:

  • 查找具有类型变量的形式的(C a)那些a,并将这些约束划分为共享公共类型变量的组a.
  • 仅保留至少一个类是交互式类的组(在下面定义).
  • 现在,对于每个剩余的组G,依次尝试默认类型列表中的每个类型ty ; 如果setting a= ty将允许完全解决G中的约束.如果是这样,默认aty.
  • 单元类型()和列表类型[]将添加到执行类型默认时尝试的标准类型列表的开头.

注意任何多参数约束(D a b)(D [a] Int)不参与过程(无论是帮助还是阻碍); 但是一旦违约过程完成,它们当然必须是可溶的.

事实上,打开这个pragma会使你的文件工作:

{-# LANGUAGE ExtendedDefaultRules #-}
import Text.Printf
main = printf "%d\n" 3
Run Code Online (Sandbox Code Playgroud)

  • 很好的澄清.简而言之,Prelude中的代码具有特权.ExtendedDefaultRules的一个作用是为我的外部代码授予额外的权限. (2认同)