virtualeyes
virtualeyes

Reputation: 11237

implicit param and overloading gotcha

trait DAOContract[T <: Entity] {
  // default create
  def create(t: T): Option[Int]
}

trait UserContract extends DAOContract[User] {
  // provide alternate create method for transactional blocks
  def create(u: User)(implicit ss: Session): Either[String, Int]
  ...
}

// DAO provides create implementation with embedded session
class UserDAO(implicit val db: Connection) 
  extends DAO[User] with UserContract {

  import db.Driver.Implicit._
  import org.scalaquery.ql._
  ...
}

in a controller

dao.create(model) // boom, no implicit session

I hope I am missing something here: why is the scala compiler unable to differentiate between the 2 create method signatures above?

Basically makes it impossible for me to overload DAO operations without coming up with a different method naming convention for operations that are transactional (i.e. return Either) or standalone.

Or, I'm just going about things in the wrong way, entirely possible...

Upvotes: 2

Views: 200

Answers (2)

Petr
Petr

Reputation: 63359

There are reasons why Scala prohibits that:

  1. Imagine that your create methods would be:

    def create(t: User): Function[Int,Something]
    def create(u: User)(implicit ss: Session): Either[String, Int]
    

    Then calling create(x)(y) would be applicable to both of them.

  2. Even though the problem in (1.) could be perhaps resolved by if Scala compiler carefully examined of the types of the functions, it'd be very error prone. In such setup it would be very easy for programmers to make mistakes. Forcing different names for the functions ensures that programmers always know what they call.


If change your methods to have the same return type and if you're willing to take the risky way, you could try something like this:

trait Broken {
  final def foo(x: Int)(implicit session: String = null): Option[Int] =
    if (session == null) foo1(x)
    else foo2(x, session);

  def foo1(x: Int): Option[Int]
  def foo2(x: Int, session: String): Option[Int]
}
class Example extends Broken {
  def foo2(x: Int, y: String) = Some(2)
  def foo1(x: Int) = Some(1)
}

object Test extends App {
  def withImplicit(e: Example) = {
    implicit val imp = "hey"
    e.foo(1) // (imp)
  }
  def withoutImplicit(e: Example) = e.foo(1)

  println(withImplicit(new Example()));
  println(withoutImplicit(new Example()));
}

When an implicit value is available, the corresponding method is called. Otherwise, the method without the "session" argument is called. But I strongly discourage this approach. Just a slight mistake will lead to calling the wrong variant and this will be very hard to debug.

Upvotes: 1

Ivan Meredith
Ivan Meredith

Reputation: 2222

Your create methods have different sigs. One returns an Either, and the other one returns an Option. Plus the implicit. You probably need to use implicitly and return an Option[Int] from both

Upvotes: 0

Related Questions