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

lui*_*dro 14 debugging haskell

举个例子,假设我希望实现一个总结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
Run Code Online (Sandbox Code Playgroud)

问题是这不会编译:

Could not deduce (Show a) arising from a use of dosum'
from the context (Num a)
Run Code Online (Sandbox Code Playgroud)

我可以添加(Show a)dosum,然后删除它,当我完成调试(在现实生活中,我会希望有这样一种类型,不一定在Show,但我会用整数调试).如果涉及一些函数并且我不断添加删除Show a语句,这会很麻烦.

我想要一个功能 unsafeShow

unsafeShow :: a -> String
Run Code Online (Sandbox Code Playgroud)

如果aShow a,它可以工作,如果不是,可以自由崩溃.这可能吗?

ehi*_*ird 13

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

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

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

class (Num a) => Number a
instance (Num a) => Number a
Run Code Online (Sandbox Code Playgroud)

您可以使用它来代替Num签名,(Num a, Show a)在调试时将声明更改为.(Number然而,选择一个更有意义的名字会更好!)


Ben*_*ood 9

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

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

但是如果你不小心使用unsafeCoerce除了Integer崩溃,内存损坏之类的东西来调用代码,那么任何事情都可能发生 - 你只是完全抛弃了你的安全网.

一般来说,唯一的"安全"用途unsafeCoerce已经知道的类型之间是相同的,但是类型检查器没有(或者其他一些专门的用例,请参阅文档).即便如此,除非您的评论解释为什么它是唯一的选择,否则任何读取您的代码的人都会非常不满意.