为了账号安全,请及时绑定邮箱和手机立即绑定

强制类型差异

强制类型差异

MM们 2019-07-26 15:14:05
强制类型差异在Scala中,我可以在编译时强制类型相等。例如:case class Foo[A,B]( a: A, b: B )( implicit ev: A =:= B )scala> Foo( 1, 2 )res3: Foo[Int,Int] = Foo(1,2)scala> Foo( 1, "2" )<console>:10: error: Cannot prove that Int =:= java.lang.String.是否有办法强制执行A型和B型应该不同的做法?
查看完整描述

3 回答

?
PIPIONE

TA贡献1829条经验 获得超9个赞

借用让-菲利普的想法,这是有效的:

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]}




查看完整回答
反对 回复 2019-07-27
?
qq_花开花谢_0

TA贡献1835条经验 获得超6个赞

我有一个更简单的解决方案,它也利用了模糊性,

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) = t

REPL样本会话,

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")
                            ^


查看完整回答
反对 回复 2019-07-27
?
慕的地10843

TA贡献1785条经验 获得超8个赞

我喜欢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 }




查看完整回答
反对 回复 2019-07-27
  • 3 回答
  • 0 关注
  • 226 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信