简体   繁体   中英

Curried apply in trait companion object

Two questions:

1) Why am I unable to have these two apply methods in the BaseTrait companion object when the apply functions are curried (note, it works when they are un-curried, with two arguments)?

2) How can I achieve this functionality: multiple apply methods that are curried?

trait BaseTrait[T, U] {
  def name: String
  def tryMe(record: T): Option[U]
}

object BaseTrait {
  // can't have both apply methods when curried
  def apply[T](s: String)(f: T => Option[Long]): LongTrait[T] =
    new LongTrait[T] {
      override val name: String = s
      override def tryMe(record: T): Option[Long] = f(record)
    }

  def apply[T](s: String)(f: T => Option[Boolean]): BooleanTrait[T] =
    new BooleanTrait[T] {
      override val name: String = s
      override def tryMe(record: T): Option[Boolean] = f(record)
    }
}

trait LongTrait[T] extends BaseTrait[T, Long] {
  override def tryMe(record: T): Option[Long]
}

trait BooleanTrait[T] extends BaseTrait[T, Boolean] {
  override def tryMe(record: T): Option[Boolean]
}

It compiles fine, but throws a runtime error:

scala> BaseTrait("test") { s: String => Option(s.toBoolean) }

<console>:13: error: ambiguous reference to overloaded definition,
both method apply in object BaseTrait of type [T](s: String)(f: T => 
Option[Boolean])BooleanTrait[T]
and  method apply in object BaseTrait of type [T](s: String)(f: T => 
Option[Long])LongTrait[T]
match argument types (String)
   BaseTrait("test") { s: String => Option(s.toBoolean) }

If you reverse the order of the current arguments, such that they differ in the first argument, then the apply method can be invoked successfully.

object BaseTrait {
    // can't have both apply methods when curried
    def apply[T](f: T => Option[Long])(s: String): LongTrait[T] =
      new LongTrait[T] {
        override val name: String = s
        override def tryMe(record: T): Option[Long] = f(record)
      }

    def apply[T](f: T => Option[Boolean])(s:String): BooleanTrait[T] =
      new BooleanTrait[T] {
        override val name: String = s
        override def tryMe(record: T): Option[Boolean] = f(record)
      }
  } 

BaseTrait { s:String => Option(s.toBoolean) }("test") 
res58: BooleanTrait[String] = ammonite.$sess.cmd57$BaseTrait$$anon$2@e39317d

As mentioned in the comment, this appears to be a corner case in the language . In the referenced issue, which was closed with a Wont-Fix status, the following simpler code also exhibits the same ambiguous reference issue.

object Foo {
  def bar(i: Int) = println(i)
  def bar(i: Int) (f: Float) = println(i*f)
}

According to Scala language creator Martin Odersky,

"I just wrote above that there is no attempt to do such a thing in the spec. I challenge you to give a set of complete and decidable rules to do this."

So it appears that due to the difficulty of implementing the necessary checks, it is not possible prevent these methods from compiling even though they cannot be subsequently referenced.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM