복붙노트

[SCALA] 유형의 차이를 적용

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 형을 집행 할 수있는 방법이 있습니까?

해결법

  1. ==============================

    1.이 작동 장 - 필립의 아이디어 떨어져 Riffing :

    이 작동 장 - 필립의 아이디어 떨어져 Riffing :

    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("", "")
    

    다음과 같이 아마,이 단순화 것 항상 어쨌든 피할 수있다 "속이고"에 대한 검사하기 때문에 (예를 들어, 푸 (1, 1) (널) 또는 = 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]
    }
    
  2. ==============================

    2.나는 또한 모호성을 활용하는 간단한 해결책을 가지고

    나는 또한 모호성을 활용하는 간단한 해결책을 가지고

    trait =!=[A, B]
    
    implicit def neq[A, B] : A =!= B = null
    
    // This pair excludes the A =:= B case
    implicit def neqAmbig1[A] : A =!= A = null
    implicit 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 => Nothing
    implicit def neg[T, U](t : T)(implicit ev : T =!= U) : ¬[U] = null
    
    def notString[T <% ¬[String]](t : T) = t
    

    샘플 REPL 세션,

    scala> val ns1 = notString(1)
    ns1: Int = 1
    
    scala> val ns2 = notString(1.0)
    ns2: Double = 1.0
    
    scala> 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")
                                ^
    
  3. ==============================

    3.나는 마일 사빈의 첫 번째 솔루션의 단순성과 효율성을 좋아하지만, 우리가 얻을 오류가 매우 도움이되지는 사실에 약간 불만이었다 :

    나는 마일 사빈의 첫 번째 솔루션의 단순성과 효율성을 좋아하지만, 우리가 얻을 오류가 매우 도움이되지는 사실에 약간 불만이었다 :

    다음과 같은 정의와 예에 의하여 :

    def f[T]( implicit e: T =!= String ) {}
    

    [문자열] f를 수행하려고하면 컴파일에 실패합니다 :

    <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 것은 문자열 다르지 않다" 그것은 우리가 모호성 오류를 설정하는 방식으로 implicits의 또 다른 레벨을 추가 할 경우 그것은 아주 쉽게 밝혀 암시 찾을 수 없음 오류로. 그때부터 우리는 사용자 지정 오류 메시지를 방출하는 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 [문자열]을 호출 할 수 있습니다 :

    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]#λ] {}
    

    쉽게 읽을 여부는 매우 주관적이다. 어떤 경우에는 전체 암시 적 매개 변수 목록을 작성하는 것보다 확실히 짧습니다.

    업데이트 : 완전성 들면임을 표현 여기 해당 코드 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 }
    

    그리고하는을 표현하는 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 }
    
  4. ==============================

    4.Landei의 생각을 바탕으로, 다음은 작동하는 것 같다 :

    Landei의 생각을 바탕으로, 다음은 작동하는 것 같다 :

    case class Foo[A, B <: A, C <: A]( a: B, b: C)(implicit f: AnyVal <:< A)
    
    scala> Foo(1f, 1.0)
    res75: Foo[AnyVal,Float,Double] = Foo(1.0,1.0)
    
    scala> Foo("", 1.0)
    res76: Foo[Any,java.lang.String,Double] = Foo(,1.0)
    
    scala> Foo(1f, 1f)
    <console>:10: error: Cannot prove that AnyVal <:< Float.
           Foo(1f, 1f)
              ^
    
    scala> Foo("", "")
    <console>:10: error: Cannot prove that AnyVal <:< java.lang.String.
           Foo("", "")
              ^
    
    scala> Foo("", 1)
    res79: Foo[Any,java.lang.String,Int] = Foo(,1)
    
  5. ==============================

    5.여기에 또 다른 시도이다 :

    여기에 또 다른 시도이다 :

    class =!=[A, B] private () extends NotNull
    
    object =!= {
      implicit def notMeantToBeCalled1[A, B >: A, C >: B <: A]: =!=[B, A] = error("should not be called")
      implicit def notMeantToBeCalled2[A, B >: A, C >: B <: A]: =!=[B, A] = error("should not be called")
      implicit def unambigouslyDifferent[A, B](implicit same: A =:= B = null): =!=[A, B] =
        if (same != null) error("should not be called explicitly with the same type")
        else new =!=
    }
    
    case class Foo[A, B](a: A, b: B)(implicit ev: A =!= B)
    

    그런 다음 다시 :

    // compiles:
    Foo(1f, 1.0)
    Foo("", 1.0)
    Foo("", 1)
    Foo("Fish", Some("Fish"))
    
    // doesn't compile
    // Foo(1f, 1f)
    // Foo("", "")
    

    내 다른 제안에서와 마찬가지로, 여기에 목표는 A와 B가 동일한 경우 컴파일시 모호성을 소개하는 것입니다. 그렇지 않은 경우, A는 B와 동일하고, 암시 모호이고 여기서는 경우 두 implicits을 제공한다.

    문제가 여전히 명시 적으로 수동 =! =. notMeantToBeCalled1 또는 =! =. unambigouslyDifferent를 호출하여 암시 적 매개 변수를 제공 할 수 있음을 유의하십시오. 나는 컴파일시에이를 방지 할 수있는 방법을 생각하지 못했습니다. 그러나, 우리는 unambigouslyDifferent는 증거 매개 변수 자체는 A가 ​​B와 동일 여부를 나타내는하지만 잠깐 ... 우리는 정반대을 증명하려고하지 있습니까 요구하는 트릭, 런타임에 예외를 던질 수 있습니까? 같은 암시 적 파라미터가 null의 기본값이 왜 그래, 그입니다. 그리고 우리는 모든 법적 용도에 널 (null)이 될 것으로 예상 - 불쾌한 사용자가 예를 들어, 호출 할 때이 null이 될하지 않을 유일한 시간이다 푸 (1 층, 1 층) (=. = unambiguouslyDifferent [플로트는 플로트), 거기에 우리가 예외를 throw하여 부정 행위를 방지 할 수 있습니다.

  6. ==============================

    6.어떻게 이런 일에 대해, 다음?

    어떻게 이런 일에 대해, 다음?

    class Foo[A, B] private (a: A, b: B)
    
    object Foo {
      def apply[A, B <: A, C >: A <: B](a: A, b: B)(implicit nothing: Nothing) = nothing
      def apply[A, B >: A, C >: B <: A](a: A, b: B)(implicit nothing: Nothing, dummy: DummyImplicit) = nothing
      def apply[A, B](a: A, b: B): Foo[A, B] = new Foo(a, b)
    }
    

    그때:

    // compiles:
    Foo(1f, 1.0)
    Foo("", 1.0)
    Foo("", 1)
    Foo("Fish", Some("Fish"))
    
    // doesn't compile
    // Foo(1f, 1f)
    // Foo("", "")
    

    아이디어는 A는 그들이 동일한 B와 동일하고, 모호하지 때 때 해상도가 모호하게하는 것입니다. 더 모호한 방법을 호출 할 수 없습니다 것을 강조하기 위해, 나는 주위 않을 것입니다 (그들은 명시 적으로 삽입하려고하면 확실히 호출자에게 잘못 보일 것입니다) 유형 아무것도의 암시했다. 합니다 (DummyImplicit의 역할은 먼저 두 가지 방법으로 다른 특성을 제공하는 막이다.)

  7. ==============================

    7.이 답변, 답변이 무엇인지 내가 생각할 수있는 단지 시작되지 않습니다. 코드는 아래의 예 또는 [B], AreEqual [A] 암시 적으로 요청하는 경우 종류가 동일인지 여부에 따라서 어떤 하나를 반환하지 않습니다. 어떻게 실제로 내가 알아낼 수 없었다 수표를 만들기 위해 거기에서 이동합니다. 아마 전체 접근 방식은 아마도 누군가가 무언가를 할 수있다, 운명을 정한다. 알았지, 암시 적으로 [없음 [A는 B가] 항상 뭔가를 반환합니다, 하나는 그것을 사용할 수 없습니다. :-(

    이 답변, 답변이 무엇인지 내가 생각할 수있는 단지 시작되지 않습니다. 코드는 아래의 예 또는 [B], AreEqual [A] 암시 적으로 요청하는 경우 종류가 동일인지 여부에 따라서 어떤 하나를 반환하지 않습니다. 어떻게 실제로 내가 알아낼 수 없었다 수표를 만들기 위해 거기에서 이동합니다. 아마 전체 접근 방식은 아마도 누군가가 무언가를 할 수있다, 운명을 정한다. 알았지, 암시 적으로 [없음 [A는 B가] 항상 뭔가를 반환합니다, 하나는 그것을 사용할 수 없습니다. :-(

    class AreEqual[A, B]
    trait LowerPriorityImplicits {
      implicit def toNo[A : Manifest, B : Manifest]: No[A, B] = No[A, B]
    }
    object AreEqual extends LowerPriorityImplicits {
      implicit def toYes[A, B](implicit ev: A =:= B, m1: Manifest[A], m2: Manifest[B]): Yes[A, B] = Yes[A, B]
    }
    
    case class Yes[A : Manifest, B : Manifest]() extends AreEqual[A, B] {
      override def toString: String = "Yes(%s, %s)" format (manifest[A].toString, manifest[B].toString)
    }
    case class No[A : Manifest, B : Manifest]() extends AreEqual[A, B] {
      override def toString: String = "No(%s, %s)" format (manifest[A].toString, manifest[B].toString)
    }
    

    테스트:

    scala> implicitly[AreEqual[String, Option[String]]]
    res0: AreEqual[String,Option[String]] = No(java.lang.String, scala.Option[java.lang.String])
    
    scala> implicitly[AreEqual[String, String]]
    res1: AreEqual[String,String] = Yes(java.lang.String, java.lang.String)
    
  8. from https://stackoverflow.com/questions/6909053/enforce-type-difference by cc-by-sa and MIT license