如何在应用类型默认规则的情况下在 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 使用的类型兼容,但在任何其他上下文中默认使用它可能会导致不兼容的不同类型。
当我在 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 使用的类型兼容,但在任何其他上下文中默认使用它可能会导致不兼容的不同类型。