【发布时间】:2017-08-08 00:04:55
【问题描述】:
假设我有一个类型
data F a = A a | B
我这样实现函数f :: F a -> F a -> F a:
f (A x) B = A x
f B (A _) = B
f (A _) (A x) = A x
但是没有 f B B 这样的东西在逻辑上是不可能的,所以我想要:
f B B = GENERATE_HASKELL_COMPILE_ERROR
这当然是行不通的。省略定义或使用f B B = undefined 不是解决方案,因为它会生成运行时异常。我想得到一个编译时类型错误。
编译器有所有的信息,它应该能够推断出我犯了一个逻辑错误。如果说我声明了let z = f (f B (A 1)) B,那应该是一个立即的编译时错误,而不是一些可以隐藏在我的代码中多年的运行时异常。
我找到了一些关于合约的信息,但我不知道如何在这里使用它们,我很好奇 Haskell 中是否有任何标准方法可以做到这一点。
编辑:事实证明,我试图做的被称为依赖类型,它在 Haskell 中还没有完全支持。然而,使用索引类型和几个扩展可能会产生类型错误。 David Young 的解决方案似乎更直接,而 Jon Purdy 创造性地使用类型运算符。我接受第一个,但我都喜欢。
【问题讨论】:
-
值不会导致 Haskell 中的类型错误,因为值和类型不会交互。你所要求的被称为 dependent typing,这是 Haskell 所没有的(至少,不足以满足你的要求)。
-
感谢@ReinHenrichs 我确实找到了一些关于依赖类型的论文,但我不能说我理解得足够好,可以决定是否可以在我的案例中使用它。
-
当然可以,只是没有 Haskell。您可能对Type-Driven Development with Idris一书感兴趣,以实用地介绍依赖类型。
-
@ReinHenrichs 您有时可以从 Haskell 已经提供的依赖类型支持水平中获得惊人的里程数。可能刚好足以让这个示例正常工作(至少取决于实际问题的外观)。正如所写的那样,问题看起来可能涉及"phantom types as 'validation' tagging" 事物的轻微扩展(仅具有多个有效组合,而不仅仅是一个)。不过,它确实有点取决于如何使用这个函数(以及你可以接受的代码复杂程度)。
标签: haskell types compile-time