作为一个例子,假设我希望实现一个总结Num
列表的函数。在编写代码的过程中,我希望使用Debug.Trace
调试它:
module T where
import Debug.Trace
dosum :: (Num a) => [a] -> a
dosum xs = dosum' 0 xs
where
dosum' n [] = n
dosum' n (x:xs) = trace (show n) $ dosum' (n+x) xs
问题是这不会编译:
Could not deduce (Show a) arising from a use of dosum'
from the context (Num a)
我可以将(Show a)
添加到dosum
,然后在我完成调试时删除它(在现实生活中,我希望有一个类型不一定在Show
,但是我将用整数调试)。如果涉及一些函数,我会继续添加删除Show a
语句,这会很麻烦。
我想要一个函数unsafeShow
unsafeShow :: a -> String
如果a
为Show a
则有效,如果不是,则可以自由崩溃。这可能吗?
答案 0 :(得分:13)
不,这是不可能的。它会违反parametricity;根据调用的特定类型,不允许多态函数表现不同。它也会打破开放世界的假设,因为为类型添加Show
实例会改变程序的行为。
它可能是一个有用的调试辅助工具,因为明确标记为不安全,但GHC不支持这样的功能,我不认为它的当前实现允许轻松添加一个。
一种可能的替代方案,如果你有许多具有相同类型类上下文的函数,并且有一个概念上的语义分组,那就是拥有类似
的类。class (Num a) => Number a
instance (Num a) => Number a
您可以在签名中使用而不是Num
,在调试时将声明更改为(Num a, Show a)
。 (然而,选择一个比Number
更有意义的名称会更好!)
答案 1 :(得分:9)
真正可怕的答案是使用unsafeCoerce
模块中的Unsafe.Coerce
。听起来就是这样 - 它是绕过类型系统的通用工具,如果你弄错了,就不会出现类型错误或异常,你会遇到分段错误。
在这种情况下,您可以unsafeCoerce
一个您已经知道的Integer
到Integer
的值,这样类型系统也可以识别它也是一个整数。然后你可以像往常一样展示它(确保提供一个明确的类型签名,因此show
知道它显示的内容 - 它无法推断,因为unsafeCoerce
可以返回任何类型!)
但是,如果您不小心使用unsafeCoerce
调用Integer
以外的代码,崩溃,内存损坏,可能会发生任何事情 - 您只是完全抛弃了您的安全网。
一般来说,unsafeCoerce
的唯一“安全”用途是你已经知道的类型之间相同,但是类型检查器没有(或者其他一些专门的用例,见the docs)。即便如此,除非你的评论解释了为什么它是唯一的选择,否则读取你的代码的人会非常不满意。
答案 2 :(得分:3)
这是不可能的。 (注意1)
1:一个例外是您可以通过a -> String
函数转储GHC堆的堆结构。你可以,例如始终通过vacuum将值转换为十六进制指针值。这不太可能是你想要的。此功能与GHCi调试器用于显示任意堆值的功能相同。
答案 3 :(得分:3)
在纯Haskell中实现unsafeShow
函数是不可能的。 GHC可以提供一个,但目前它没有。
但是,您可以查看GHCi调试器。这允许您打印没有Show
实例的东西。 (此外,它允许您避免评估无法评估的内容,这可能很有用。)
答案 4 :(得分:1)
通常我会注释掉类型签名,但是如果一个函数在树中很深,那会很烦人。您可以尝试使用重写规则将多态函数替换为已修改的变体。
-- original function, should add NOINLINE to make sure your rule gets a chance to fire.
{-# NOINLINE dosum #-}
dosum :: (Num a) => [a] -> a
-- a version with added debugging
dosumShow :: (Num a, Show a) => [a] -> a
{-# RULES "sub/dosum" forall x. dosum x = dosumShow x #-}
答案 5 :(得分:0)
这是GHC版本7.4.1?从它release notes:
Num类不再具有Eq或Show超类。因此,许多其他类和函数都获得了明确的Eq或Show约束,而不是依赖于Num约束来提供它们。
您可以通过以下方式制作适用于Haskell98 / Haskell2010和GHC的代码:
每当你创建一个类型的Num实例时,也会生成Show和Eq实例,并且
每当你给一个函数,实例或类一个Num t约束时,也给它显示t和Eq t约束。
您的代码在以前版本的GHC中运行良好(我在7.0.4中尝试)。