6

为什么是printf "%d\n" 3模棱两可而不是show 3?可以printf重写模块以提供自动消歧吗?大概类似的事情show必须在printf...的较低级别完成,或者和之间是否存在一些关键的区别printfshow这需要消除数字的歧义?

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

这是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

这是不消除数字歧义时歧义变量错误: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
4

1 回答 1

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 提供了ExtendedDefaultRulespragma 来放宽这些规则,如手册中所述

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

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

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

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

{-# LANGUAGE ExtendedDefaultRules #-}
import Text.Printf
main = printf "%d\n" 3
于 2017-10-17T16:27:27.313 回答