9

Consider the following code:

object foo {

    trait Bar[Q[_]]

    implicit object OptionBar extends Bar[Option]

    def test[T, C[_]](c: C[T])(implicit bar: Bar[C]) = ()

    def main(args: Array[String]) {
      test(Some(42): Option[Int])  //???
    }
}

This works, but I need to type the Some(42) as Option[Int], else the implicit object OptionBar won't be resolved (because a Bar[Some] is expected instead). Is there a way to avoid the explicit typing, so that I get the implicit OptionBar object in test even if I feed test with a Some or None?

[Clarification]

  • I used Option here just as example, it should also work if I have a Bar for an abstract class etc.
  • The solution should also work when other, unrelated Bars are in scope, say implicit object listBar extends Bar[list]

[Update]

It seems that making Bar's parameter contravariant does the trick:

object foo {

  trait Bar[-Q[_]] //<---------------

  implicit object OptionBar extends Bar[Option]
  implicit object ListBar extends Bar[List]

  def test[T, C[_]](c: C[T])(implicit bar: Bar[C]) = ()

  def main(args:Array[String]) {
    test(Some(42))
  }
}

But of course this is a severe limitation of the possibilities in Bar, so I still hope for a better answer.

0__
  • 66,707
  • 21
  • 171
  • 266
Landei
  • 54,104
  • 13
  • 100
  • 195
  • 1
    Why is contravariance a severe limitation? Without using it, then Bar is invariant. If you're attempting to use Bar as a type-class against higher-kinded types that contravariance seems to fit in my mind. That is of course until you want to treat subclasses differently. However in that case, you still have other tricks, like implicit resolution "priorities" – jsuereth Sep 30 '10 at 00:48
  • @Josh: Consider something like `trait Bar[Q[_]] { def zero[T]:Q[T] }`, returing None and Nil in my examples. But I can't have such a method in Bar, if I define Q as contravariant. When you know how to solve this, please let me know... – Landei Sep 30 '10 at 07:19
  • 1
    Also, for a naturally contravariant type class, such as `Equal[T]`, implicit search will favour `Equal[Animal]` over `Equal[Dog]`: http://www.scala-lang.org/node/4626. Inheritance and type classes are really tricky to mesh together. – retronym Sep 30 '10 at 09:44

2 Answers2

7

It's not going to work in all cases, but as stated, you can try this:

object foo {
  trait Bar[Q[_]]

  implicit object OptionBar extends Bar[Option]

  def test[T, C[_], D](c: D)(implicit bar: Bar[C], ev: D <:< C[T]) = ()

  def main(args: Array[String]) {
    test(Some(42)) //???
  }
}

Interestingly, this doesn't infer, although it expresses the same thing:

def test[T, C[_], D <: C[T]](c: D)(implicit bar: Bar[C]) = ()

To learn more about <:<, see:

Community
  • 1
  • 1
retronym
  • 54,768
  • 12
  • 155
  • 168
  • This works, however if you add another implicit for Bar in that scope, say `implicit object ListBar extends Bar[List]` you get an "ambiguous implicit values" error. – Landei Sep 28 '10 at 21:51
  • I give the bounty to this answer, because it shows a nice technique and solves the initial question, which wasn't formulated precisely enough. However I'd still appreciate to get hint how to solve the "ambiguos implicit values" problem. – Landei Oct 08 '10 at 07:10
  • Well, three years and several versions of Scala later, it doesn't seem an answer exists. (I'm facing the same challenge.) – Tim Mar 15 '13 at 23:12
5

That's because Some(42) is a more specific type than Option[Int]. It is a Some[Int]. See alternative coding below:

object foo {

    trait Bar[Q[_]]

    implicit object OptionBar extends Bar[Option]

    def test[T, C[_]](c: C[T])(implicit bar: Bar[C]) = ()

    def main(args: Array[String]) {
      test(Option(42))
    }
}
Daniel C. Sobral
  • 295,120
  • 86
  • 501
  • 681
  • 1
    I don't really advocate using `Option.apply` unless using it to encode a null check. Incidentally, with scalaz, I would write `42.some` or `42.pure[Option]`, both of which type as `Option[Int]`. – retronym Sep 28 '10 at 20:24
  • @retronym - Why do you not advocate using `Option.apply`? – Rex Kerr Sep 28 '10 at 20:42
  • 1
    I use it when I have a potentially nullable reference that want to convert to a `Some` or a `None`. I can read `Some(x) : Option[A]` and reason that I'll have a `Some` without tracing the origin of `x`. If the argument is a literal, as in this example, it's okay though. – retronym Sep 28 '10 at 21:03
  • I used Option here just as an example. I want to avoid the explicit typing when I have "Bars" for abstract base classes etc. – Landei Sep 28 '10 at 21:55