是否可以在Haskell中强制类到类?

时间:2012-04-18 14:34:28

标签: debugging haskell

作为一个例子,假设我希望实现一个总结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

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

6 个答案:

答案 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一个您已经知道的IntegerInteger的值,这样类型系统也可以识别它也是一个整数。然后你可以像往常一样展示它(确保提供一个明确的类型签名,因此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中尝试)。