问题
当我使用支持类型级编程的库时,我经常会发现自己在写如下注释(来自Paul Snively 在 Strange Loop 2012 中提出的示例):
// But these invalid sequences don't compile:
// isValid(_3 :: _1 :: _5 :: _8 :: _8 :: _2 :: _8 :: _6 :: _5 :: HNil)
// isValid(_3 :: _4 :: _5 :: _8 :: _8 :: _2 :: _8 :: _6 :: HNil)
/**
* If we wanted to confirm that the list uniquely contains `Foo` or any
* subtype of `Foo`, we could first use `unifySubtypes` to upcast any
* subtypes of `Foo` in the list to `Foo`.
*
* The following would not compile, for example:
*/
//stuff.unifySubtypes[Foo].unique[Foo]
这是一种非常粗略的方式来表明关于这些方法的行为的一些事实,我们可以想象想要使这些断言更正式——用于单元或回归测试等。
为了给出一个具体的例子来说明为什么这可能在像 Shapeless 这样的库的上下文中很有用,几天前我写了以下内容作为对这个问题的快速第一次尝试:
import shapeless._
implicit class Uniqueable[L <: HList](l: L) {
def unique[A](implicit ev: FilterAux[L, A, A :: HNil]) = ev(l).head
}
目的是编译:
('a' :: 'b :: HNil).unique[Char]
虽然这不会:
('a' :: 'b' :: HNil).unique[Char]
我惊讶地发现这种类型级unique
for 的实现HList
不起作用,因为 Shapeless 很乐意FilterAux
在后一种情况下找到一个实例。换句话说,即使您可能不希望它编译,以下内容也会编译:
implicitly[FilterAux[Char :: Char :: HNil, Char, Char :: HNil]]
在这种情况下,我看到的是一个错误——或者至少是一些类似错误的东西——并且它已经被修复了。
更一般地说,我们可以想象想要检查隐含在我的期望中的那种不变量,它FilterAux
应该如何处理单元测试之类的东西——就像谈论测试这样的类型级代码听起来很奇怪,所有最近关于类型与测试的相对优点的辩论。
我的问题
问题是我不知道任何类型的测试框架(适用于任何平台)允许程序员断言某些东西不能编译。
我可以想象的一种方法是FilterAux
使用旧的implicit-argument-with-null-default技巧:
def assertNoInstanceOf[T](implicit instance: T = null) = assert(instance == null)
这将让您在单元测试中编写以下内容:
assertNoInstanceOf[FilterAux[Char :: Char :: HNil, Char, Char :: HNil]]
不过,以下内容会更加方便和富有表现力:
assertDoesntCompile(('a' :: 'b' :: HNil).unique[Char])
我要这个。我的问题是,是否有人知道任何支持远程类似东西的测试库或框架——对于 Scala 来说是理想的,但我会满足于任何东西。