The problem
When I'm working with libraries that support type-level programming, I often find myself writing comments like the following (from an example presented by Paul Snively at 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)
Or this, from an example in the Shapeless repository:
/**
* 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]
This is a very rough way of indicating some fact about the behavior of these methods, and we could imagine wanting to make these assertions more formal—for unit or regression testing, etc.
To give a concrete example of why this might be useful in the context of a library like Shapeless, a few days ago I wrote the following as a quick first attempt at an answer to this question:
import shapeless._
implicit class Uniqueable[L <: HList](l: L) {
def unique[A](implicit ev: FilterAux[L, A, A :: HNil]) = ev(l).head
}
Where the intention is that this will compile:
('a' :: 'b :: HNil).unique[Char]
While this will not:
('a' :: 'b' :: HNil).unique[Char]
I was surprised to find that this implementation of a type-level unique
for HList
didn't work, because Shapeless would happily find a FilterAux
instance in the latter case. In other words, the following would compile, even though you'd probably expect it not to:
implicitly[FilterAux[Char :: Char :: HNil, Char, Char :: HNil]]
In this case, what I was seeing was a bug—or at least something bug-ish—and it has since been fixed.
More generally, we can imagine wanting to check the kind of invariant that was implicit in my expectations about how FilterAux
should work with something like a unit test—as weird as it may sound to be talking about testing type-level code like this, with all the recent debates about the relative merit of types vs. tests.
My question
The problem is that I don't know of any kind of testing framework (for any platform) that allows the programmer to assert that something must not compile.
One approach that I can imagine for the FilterAux
case would be to use the old implicit-argument-with-null-default trick:
def assertNoInstanceOf[T](implicit instance: T = null) = assert(instance == null)
Which would let you write the following in your unit test:
assertNoInstanceOf[FilterAux[Char :: Char :: HNil, Char, Char :: HNil]]
The following would be a heck of a lot more convenient and expressive, though:
assertDoesntCompile(('a' :: 'b' :: HNil).unique[Char])
I want this. My question is whether anyone knows of any testing library or framework that supports anything remotely like it—ideally for Scala, but I'll settle for anything.
See Question&Answers more detail:
os