Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Types, Tuples, Implicit priority and Overloaded methods

Tags:

scala

Trying to disambiguate which method is called based upon the type of the second parameter, Any vs. Throwable, but without success. Compiling the code below generates the following error message:

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

Here's the code:

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

I tried making the "t2" lower priority as follows:

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 {
    ....
 }

and removing "t2" from the "Call" object, but got the same error message.

I would like the disambiguation to take place at compile-time and not at run-time. Thanks.

like image 587
user997206 Avatar asked Nov 05 '22 12:11

user997206


1 Answers

Miles Sabin provided me with the following solution:

    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")
        }
      }
    }
like image 89
user997206 Avatar answered Nov 15 '22 06:11

user997206