Проверка утверждения о том, что что-то не должно компилироваться
Когда я работаю с библиотеками, которые поддерживают программирование на уровне типов, я часто пишу комментарии, подобные следующим (изпример представленныйПол Снайвли на Странной Петле 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
заHList
не работает, потому что Shapeless с удовольствием найдетFilterAux
экземпляр в последнем случае. Другими словами, следующее будет компилироваться, даже если вы, вероятно, ожидаете, что это не так:
implicitly[FilterAux[Char :: Char :: HNil, Char, Char :: HNil]]
В этом случае то, что я видел, былоЖук- или, по крайней мере, что-то ошибочное - и этос тех пор был исправлен.
В более общем смысле, мы можем представить, что хотим проверить вид инварианта, который подразумевался в моих ожиданиях о том, какFilterAux
должен работать с чем-то вроде модульного теста - как бы странно это ни звучало, если говорить о тестировании кода на уровне типа, подобного этому, со всеми недавними дебатами об относительной значимости типовпротив тесты.
Проблема в том, что я не знаю какой-либо среды тестирования (для любой платформы), которая позволяет программисту утверждать, что что-то не должнокомпилировать.
Один подход, который я могу представить дляFilterAux
дело будет использовать старыйтрюк неявного аргумента с нулевым значением по умолчанию:
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, но я согласен на все.