【发布时间】:2013-02-14 01:10:39
【问题描述】:
问题
当我使用支持类型级编程的库时,我经常发现自己在编写类似以下的 cmets(来自 Paul Snively at Strange Loop 2012 提供的 an example):
// 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)
或者这个,来自 Shapeless 存储库中的 an example:
/**
* 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 这样的库的上下文中很有用,几天前我写了以下内容作为对this question 的回答的第一次快速尝试:
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]
我惊讶地发现 HList 的类型级别 unique 的这种实现不起作用,因为在后一种情况下,Shapeless 很乐意找到 FilterAux 实例。换句话说,即使您可能不希望它编译,以下内容也会编译:
implicitly[FilterAux[Char :: Char :: HNil, Char, Char :: HNil]]
在这种情况下,我看到的是a bug——或者至少是一些错误的东西——它是has since been fixed。
更一般地说,我们可以想象想要检查隐含在我对FilterAux应该如何与单元测试之类的东西一起工作的期望中的那种不变量——尽管听起来很奇怪谈论像这样测试类型级代码,以及最近关于类型 vs. 测试的相对优点的所有辩论。
我的问题
问题是我不知道任何类型的测试框架(适用于任何平台)允许程序员断言某些东西不能编译。
对于FilterAux 案例,我可以想象的一种方法是使用旧的implicit-argument-with-null-default trick:
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 来说是理想的,但我会满足于任何东西。
【问题讨论】:
-
我假设您不想将您的上下文提供给解释器的实例并以这种方式进行测试?
-
@RexKerr:不是手动的,不是。我很乐意编写一个采用这种方法的框架,我认为这不会太难,但我更愿意发现其他人已经为我编写了它。
-
我听到一位“Scala Types”(播客)人员谈论他正在做的工作,以验证无效的源代码不会无意中被编译器接受。我想是尤维·马索里。如果您擅长搜索,它可能会出现在演出说明中...
标签: scala testing types shapeless type-level-computation