-
扬帆大鱼
借用让-菲利普的想法,这是有效的:sealed class =!=[A,B]trait LowerPriorityImplicits {
implicit def equal[A]: =!=[A, A] = sys.error("should not be called")}object =!= extends LowerPriorityImplicits {
implicit def nequal[A,B](implicit same: A =:= B = null): =!=[A,B] =
if (same != null) sys.error("should not be called explicitly with same type")
else new =!=[A,B]} case class Foo[A,B](a: A, b: B)(implicit e: A =!= B)然后:// compiles:Foo(1f, 1.0)Foo("", 1.0)Foo("", 1)Foo("Fish", Some("Fish"))// doesn't compile// Foo(1f, 1f)// Foo("", "")我可能会简化如下,因为对“作弊”的检查总是可以避免的。Foo(1, 1)(null)或=!=.nequal(null)):sealed class =!=[A,B]trait LowerPriorityImplicits {
/** do not call explicitly! */
implicit def equal[A]: =!=[A, A] = sys.error("should not be called")}object =!= extends LowerPriorityImplicits {
/** do not call explicitly! */
implicit def nequal[A,B]: =!=[A,B] = new =!=[A,B]}
-
白板的微信
我有一个更简单的解决方案,它也利用了模糊性,trait =!=[A, B]implicit def neq[A, B] : A =!= B = null// This pair excludes the A =:= B caseimplicit def neqAmbig1[A] : A =!= A = nullimplicit def neqAmbig2[A] : A =!= A = null最初的用例,case class Foo[A,B](a : A, b : B)(implicit ev: A =!= B)new Foo(1, "1")new Foo("foo", Some("foo"))// These don't compile// new Foo(1, 1)// new Foo("foo", "foo")// new Foo(Some("foo"), Some("foo"))更新我们可以把这个和我的“神奇的打字技巧”(谢谢@JPP;-)如下:type ¬[T] = T => Nothingimplicit def neg[T, U](t : T)(implicit ev : T =!= U) : ¬[U] = nulldef notString[T <% ¬[String]](t : T) = tREPL样本会话,scala> val ns1 = notString(1)ns1: Int = 1scala> val ns2 = notString(1.0)ns2: Double = 1.0scala> val ns3 = notString(Some("foo"))ns3: Some[java.lang.String] = Some(foo)scala> val ns4 = notString("foo")<console>:14: error: No implicit view available from
java.lang.String => (String) => Nothing.
val ns4 = notString2("foo")
^
-
慕娘9325324
我喜欢Miles Sabin的第一个解决方案的简单性和有效性,但对我们得到的错误没有多大帮助这一事实有点不满意:例如,定义如下: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和字符串没有什么不同”,结果发现,如果添加另一个层次的派生,那么我们就可以将歧义错误为隐未发现错误。从那时起,我们可以使用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]
^那好多了。谢谢编译器。对于那些喜欢上下文绑定语法糖的人来说,最后一个技巧是可以定义这个别名(基于lambdas类型):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 }