类型,元组,隐式优先级和重载方法

时间:2011-10-15 20:19:07

标签: scala

尝试根据第二个参数Any对Throwable的类型消除歧义,但没有成功。编译下面的代码会生成以下错误消息:

Main.scala:85: error: ambiguous reference to overloaded definition,
both method apply in class Call of type (body: =>(String, Throwable, Array[Any]))(implicit m:   Main.Call.Dummy3)Unit
and  method apply in class Call of type (body: => (String, Array[Any]))(implicit m: Main.Call.Dummy1)Unit
match argument types ((String, Throwable, String))
    agent.call {
          ^
one error found

以下是代码:

object Main {
  object Call {
    implicit def t1(t: Tuple2[String, Any]): Tuple2[String, Array[Any]] = {
      (t._1, Array(t._2))
    }
    implicit def t1t(t: Tuple2[String, Throwable]): Tuple2[String, Throwable] = {
      (t._1, t._2)
    }
    implicit def t2(t: Tuple3[String, Any, Any]): Tuple2[String, Array[Any]] = {
      (t._1, Array(t._2, t._3))
    }
    implicit def t2t(t: Tuple3[String, Throwable, Any]): Tuple3[String, Throwable, Array[Any]] = {
      (t._1, t._2, Array(t._3))
    }

    class Dummy1
    object Dummy1 {
      implicit def dummyImplicit: Dummy1 = {
println("Dummy1.dummyImplicit")
        new Dummy1
      }
    }
    class Dummy2
    object Dummy2 {
      implicit def dummyImplicit: Dummy2 = {
println("Dummy2.dummyImplicit")
        new Dummy2
      }
    }
    class Dummy3
    object Dummy3 {
      implicit def dummyImplicit: Dummy3 = {
println("Dummy3.dummyImplicit")
        new Dummy3
      }
    }
  }
  import Call._

  class Call {

    def apply(body: => Tuple2[String, Array[Any]])
        (implicit m: Dummy1): Unit = {
      println("message and array of parameters")
    }
    def apply(body: => Tuple2[String, Throwable])
        (implicit m: Dummy2): Unit = {
      println("message and throwable")
    }
    def apply(body: => Tuple3[String, Throwable, Array[Any]])
        (implicit m: Dummy3): Unit = {
      println("message, throwable and array of parameters")
    }
  }

  class Agent {
    val _call = new Call
    def call: Call = _call
  }

  def main(args: Array[String]): Unit = {
    val msg = "XXX"
    val agent = new Agent
    agent.call {
      (msg, "one")
    }
    agent.call {
      (msg, new Exception)
    }
    agent.call {
      (msg, "one", "two")
    }
    agent.call {
      (msg, new Exception, "one")
    }
  }
}

我尝试将“t2”置于较低优先级,如下所示:

trait LowPriority {
    implicit def t2(t: Tuple3[String, Any, Any]): Tuple2[String, Array[Any]] = {
      (t._1, Array(t._2, t._3))
    }
}
object Call extends LowPriority {
    ....
 }

并从“Call”对象中删除“t2”,但收到相同的错误消息。

我希望消除歧义在编译时而不是在运行时进行。 感谢。

1 个答案:

答案 0 :(得分:2)

Miles Sabin为我提供了以下解决方案:

    object Main {

      object Call {
        trait LowPriorityDistinguishThrowable {
          trait Wrap1[A, B] {
            val body : (A, B)
            def apply(call: Call) : Unit
          }
          trait Wrap2[A, B, Any] {
            val body : (A, B, Any)
            def apply(call: Call) : Unit
          }

          implicit def wrap11[T](body0 : => (String, T)) =
            new Wrap1[String, T] {
              lazy val body = body0
              def apply(call: Call) {
                println("(message and not throwable): " +body)
              }
            }

          implicit def wrap21[T](body0 : => (String, T, Any)) =
            new Wrap2[String, T, Any] {
              lazy val body = body0
              def apply(call: Call) {
                println("(message and not throwable): " +body)
              }
            }
        }

        object DistinguishThrowable extends LowPriorityDistinguishThrowable {
          implicit def wrap12(body0 : => (String, Throwable)) =
            new Wrap1[String, Throwable] {
              lazy val body = body0
              def apply(call: Call) {
                println("(message and throwable): " +body)
              }
            }

          implicit def wrap22(body0 : => (String, Throwable, Any)) =
            new Wrap2[String, Throwable, Any] {
              lazy val body = body0
              def apply(call: Call) {
                println("(message and throwable): " +body)
              }
            }
        }
      }

      class Call(val enabled: Boolean) {
        import Call._
        import DistinguishThrowable._

        def apply[T](body: Wrap1[String, T]): Unit = {
          if (enabled) body(this)
        }
        def apply[T](body: Wrap2[String, T, Any]): Unit = {
          if (enabled) body(this)
        }
      }

      def main(args : Array[String]): Unit = {
        val call = new Call(true)

        call {
          ("foo", new Exception)
        }
        call {
          ("foo", "bar")
        }

        call {
          ("foo", new Exception, "one")
        }
        call {
          ("foo", "bar", "one")
        }
      }
    }