如何在应用类型默认规则的情况下在 ghci 中打印多态函数(或值)的类型?

How to print type of polymorphic function (or value) in ghci with type defaulting rules applied?

当我在 GHCi 中输入 :t 命令时,我看到多态类型:

ghci> :t 42
42 :: Num t => t
ghci> :t div
div :: Integral a => a -> a -> a

但是在我实际评估这些函数之后,我看到了类型默认规则的结果。根据 Haskell 报告 and/or ghc 实施,在 ghci 中是否有一些命令或能力观察类型默认规则应用后类型将如何更改?

您可以通过打开单态限制然后将其绑定到一个新名称来做到这一点:

Prelude> :set -XMonomorphismRestriction 
Prelude> let n = 42
Prelude> :t n
n :: Integer
Prelude> let p = (^)
Prelude> :t p
p :: Integer -> Integer -> Integer
Prelude> let e = (**)
Prelude> :t e
e :: Double -> Double -> Double
Prelude> let d = div
Prelude> :t d
d :: Integer -> Integer -> Integer

如果您不喜欢总是定义一个新变量,您可以使用

来解决这个问题
Prelude> :def monotype (\e -> return $ ":set -XMonomorphismRestriction\nlet defaulted = "++e++"\n:t defaulted")

(您可能希望将其放入您的 .ghci 文件中以始终提供可用的命令)然后

Prelude> :monotype (^)
defaulted :: Integer -> Integer -> Integer

当然,启用单态限制的隐藏全局副作用非常丑陋,但是哦......

这不是一个完美的解决方案,但它可能是第一步。

> import Data.Typeable
> let withType x = (x, typeOf x)
> withType []
([],[()])
> withType 56
(56,Integer)

请注意,由于类型 a 已更改为 (a,TypeRep),GHCi 将不会使用其所有默认魔法。不过,还是可以展示一部分。

GHCi 的 :set +t 选项也很有趣,但似乎在 GHCi 默认设置之前打印多态类型。

从 GHC 8.4.1 开始,可以使用 :type +d(或简称 :t +d)选项来打印表达式的类型,如果可能的话默认类型变量。

ghci> :t 42
42 :: Num p => p
ghci> :t +d 42
42 :: Integer
ghci> :t div
div :: Integral a => a -> a -> a
ghci> :t +d div
div :: Integer -> Integer -> Integer

ghci 不可能为您提供与 GHC 类似的默认行为,这正是单态限制在 ghci 中(现在)默认关闭的原因。

如@Shersh 的回答所示,您现在可以询问 GHCi 它将给定表达式默认为什么。

Prelude> :t 2^100 `div` 2
2^100 `div` 2 :: Integral a => a

Prelude> :t +d 2^100 `div` 2
2^100 `div` 2 :: Integer

Prelude> 2^100 `div` 2
633825300114114700748351602688

但这不一定反映 GHC 对相同表达式的处理方式,因为 GHC 在完整模块的上下文中编译表达式。 GHC 可以考虑表达式的所有 uses,而 GHCi 只能访问表达式的组成部分。 GHC 仅在考虑所有这些额外的上下文后默认 remain 不明确的内容,因此不能保证使用您在 GHCi 中使用 :t +d 看到的表达式的类型.

例如:

n = 2^100 `div` 2

xs = "ABCD"

main = print $ xs !! n

这会打印出 'A',这显然不是该(4 元素)列表的第 633825300114114700748351602688 个元素。因为表达式 2^100 `div` 2 使用 作为 !! 的参数(非本地,通过 n 绑定),并且 (!!) :: [a] -> Int -> a类型选择为 Int,而不是没有此上下文 (Integer) 时默认选择的类型。将该表达式计算为 Int 有不同的结果(0,由于溢出)。

这意味着当您因 GHC 中的类型错误而绞尽脑汁并在 GHCi 中使用 :t +d 来尝试获取更多信息时,您需要意识到您仍然可能看不到与 GHC 实际使用的类型相同。保证多态类型与 GHC 使用的类型兼容,但在任何其他上下文中默认使用它可能会导致不兼容的不同类型。