14

例如,假设我希望实现一个汇总Nums 列表的函数。在编写代码的中途,我希望使用以下命令对其进行调试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然后删除它(在现实生活中,我希望有一个不一定是 in 的类型Show,但我会使用整数进行调试)。如果涉及到一些函数并且我不断添加删除Show a语句,这可能会变得很麻烦。

我想要一个功能unsafeShow

unsafeShow :: a -> String

如果是,则有效,如果a不是Show a,则可以自由崩溃。这可能吗?

4

6 回答 6

13

不,这是不可能的。它会违反参数化;不允许多态函数根据调用它的特定类型表现出不同的行为。它还会打破开放世界的假设,因为Show为类型添加实例会改变程序的行为。

它可能是一个有用的调试辅助工具,因为某些东西被明确标记为不安全,但 GHC 不支持这样的功能,而且我认为它当前的实现不允许轻松添加一个功能。

如果您有许多具有相同类型类上下文的函数,并且有一个概念语义分组,那么一个可能的替代方案是拥有一个类似的类

class (Num a) => Number a
instance (Num a) => Number a

您可以使用它而不是在签名中,在调试时Num将声明更改为。(Num a, Show a)(不过,最好选择一个比 更有意义的名称Number!)

于 2012-04-18T14:40:43.270 回答
10

真正可怕的答案是unsafeCoerceUnsafe.Coerce模块中使用。听起来确实如此——它是绕过类型系统的通用工具,如果你弄错了,你不会得到类型错误或异常,你会得到分段错误。

在这种情况下,您可以unsafeCoerce使用一个您已经知道是Integerto的值,Integer以便类型系统也可以识别它是一个整数。然后你可以像往常一样显示它(确保给出明确的类型签名,这样就show知道它显示的是什么——它无法推断,因为unsafeCoerce可以返回任何类型!)

但是,如果你不小心调用了代码而unsafeCoerce不是Integer,崩溃,内存损坏,任何事情都可能发生——你只是完全丢掉了你的安全网。

一般来说,唯一“安全”的使用是在unsafeCoerce已经知道的类型之间是相等的,但类型检查器却不是(或其他一些专门的用例,请参阅文档)。即便如此,任何阅读您的代码的人都会对此深表不满,除非您的评论解释了为什么它是唯一的选择。

于 2012-04-18T15:28:05.933 回答
3

这是不可能的。(注1


1 : 一个例外是您可以通过a -> String函数转储 GHC 堆的堆结构。例如,您始终可以通过Vacuum将值转换为十六进制指针值。这不太可能是您想要的。此功能与 GHCi 调试器用于显示任意堆值的功能相同。

于 2012-04-18T14:44:16.137 回答
3

unsafeShow在纯 Haskell中实现您的功能是不可能的。GHC 可以提供一个,但目前还没有。

不过,您可以查看 GHCi 调试器。这允许您打印出没有Show实例的东西。(此外,它使您可以避免评估本来不会被评估的东西,这可能很有用。)

于 2012-04-18T14:45:40.660 回答
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 #-}
于 2012-04-18T16:16:33.073 回答
0

它是 GHC 版本 7.4.1?从它发布说明

Num 类不再具有 Eq 或 Show 超类。因此,许多其他类和函数获得了明确的 Eq 或 Show 约束,而不是依靠 Num 约束来提供它们。

您可以通过以下方式编写适用于 Haskell98/Haskell2010 和 GHC 的代码:

  • 每当你创建一个类型的 Num 实例时,也要创建 Show 和 Eq 实例,并且

  • 每当你给一个函数、实例或类一个 Num t 约束时,也要给它 Show t 和 Eq t 约束。

您的代码在以前版本的 GHC 中运行良好(我在 7.0.4 中尝试)。

于 2012-04-18T14:49:56.333 回答