Мне понравилась простота и эффективность первого решения Майлза Сабина, но он был немного недоволен тем, что ошибка, которую мы получаем, не очень помогает:
Примером со следующим определением:
def f[T]( implicit e: T =!= String ) {}
Попытка выполнить f[String]
не удастся скомпилировать с:
<console>:10: error: ambiguous implicit values:
both method neqAmbig1 in object =!= of type [A]=> =!=[A,A]
and method neqAmbig2 in object =!= of type [A]=> =!=[A,A]
match expected type =!=[String,String]
f[String]
^
Я бы предпочел, чтобы компилятор сказал мне что-то вроде "T не отличается от String"получается, что это довольно просто, если добавить еще один уровень имплицитов таким образом, чтобы мы превратили ошибку неоднозначность в ошибку неявная не найдена .С этого момента мы можем использовать аннотацию implicitNotFound
для создания пользовательского сообщения об ошибке:
@annotation.implicitNotFound(msg = "Cannot prove that ${A} =!= ${B}.")
trait =!=[A,B]
object =!= {
class Impl[A, B]
object Impl {
implicit def neq[A, B] : A Impl B = null
implicit def neqAmbig1[A] : A Impl A = null
implicit def neqAmbig2[A] : A Impl A = null
}
implicit def foo[A,B]( implicit e: A Impl B ): A =!= B = null
}
Теперь давайте попробуем позвонить f[String]
:
scala> f[String]
<console>:10: error: Cannot prove that String =!= String.
f[String]
^
Это лучше.Спасибо компилятору.
В качестве последнего трюка для тех, кто любит синтаксический сахар, связанный с контекстом, можно определить этот псевдоним (на основе типа лямбда-выражений):
type IsNot[A] = { type λ[B] = A =!= B }
Тогда мы можем определить f
вот так:
def f[T:IsNot[String]#λ] {}
То, что легче читать, очень субъективно.В любом случае это определенно короче, чем написание полного списка неявных параметров.
ОБНОВЛЕНИЕ : для полноты здесь эквивалентный код для выражения, что A
не является подтипом B
:
@annotation.implicitNotFound(msg = "Cannot prove that ${A} <:!< ${B}.")
trait <:!<[A,B]
object <:!< {
class Impl[A, B]
object Impl {
implicit def nsub[A, B] : A Impl B = null
implicit def nsubAmbig1[A, B>:A] : A Impl B = null
implicit def nsubAmbig2[A, B>:A] : A Impl B = null
}
implicit def foo[A,B]( implicit e: A Impl B ): A <:!< B = null
}
type IsNotSub[B] = { type λ[A] = A <:!< B }
И для выражения того, что A
не конвертируется в B
:
@annotation.implicitNotFound(msg = "Cannot prove that ${A} <%!< ${B}.")
trait <%!<[A,B]
object <%!< {
class Impl[A, B]
object Impl {
implicit def nconv[A, B] : A Impl B = null
implicit def nconvAmbig1[A<%B, B] : A Impl B = null
implicit def nconvAmbig2[A<%B, B] : A Impl B = null
}
implicit def foo[A,B]( implicit e: A Impl B ): A <%!< B = null
}
type IsNotView[B] = { type λ[A] = A <%!< B }