tags:

views:

276

answers:

2

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.

+5  A: 

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:

retronym
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
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
+3  A: 

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
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
@retronym - Why do you not advocate using `Option.apply`?
Rex Kerr
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
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