Неявное разрешение параметров для более высокородных типов

Рассмотрим следующий код:

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])  //???
    }
}

Это работает, но мне нужно ввести Some (42) как Option [Int], иначе неявный объект OptionBar не будет разрешен (потому что вместо этого ожидается Bar [Some]). Есть ли способ избежать явной типизации, чтобы я получал неявный объект OptionBar в тесте, даже если я загружаю тест с помощью Some или None?

[Разъяснение]

  • Я использовал здесь Option как пример, он также должен работать, если у меня есть Bar для абстрактного класса и т. Д.
  • Решение также должно работать, когда в области видимости находятся другие, несвязанные панели, например implicit object listBar extends Bar[list]

[Обновлять]

Похоже, что создание контравариантного параметра Бар помогает:

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

Но, конечно, это серьезное ограничение возможностей в Bar, поэтому я все еще надеюсь на лучший ответ.


person Landei    schedule 28.09.2010    source источник
comment
Почему контравариантность - серьезное ограничение? Без его использования Бар инвариантен. Если вы пытаетесь использовать Bar как тип-класс против высокодородных типов, мне кажется, что контравариантность подходит мне. Это, конечно, до тех пор, пока вы не захотите по-другому обращаться с подклассами. Однако в этом случае у вас все еще есть другие уловки, например, неявные приоритеты разрешения.   -  person jsuereth    schedule 30.09.2010
comment
@Josh: Рассмотрим что-то вроде trait Bar[Q[_]] { def zero[T]:Q[T] }, возвращая None и Nil в моих примерах. Но у меня не может быть такого метода в Bar, если я определю Q как контравариантный. Когда вы узнаете, как это решить, дайте мне знать ...   -  person Landei    schedule 30.09.2010
comment
Кроме того, для класса естественно контравариантного типа, такого как Equal[T], неявный поиск будет отдавать предпочтение Equal[Animal] над Equal[Dog]: scala -lang.org/node/4626. Классы наследования и типов действительно сложно соединить вместе.   -  person retronym    schedule 30.09.2010


Ответы (2)


Это не сработает во всех случаях, но, как уже говорилось, вы можете попробовать следующее:

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)) //???
  }
}

Интересно, что это не вывод, хотя выражает то же самое:

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

Чтобы узнать больше о <:<, см .:

person retronym    schedule 28.09.2010
comment
Это работает, однако, если вы добавите еще один неявный для Bar в этой области, скажем implicit object ListBar extends Bar[List], вы получите неоднозначную ошибку неявных значений. - person Landei; 29.09.2010
comment
Я даю высокую оценку этому ответу, потому что он показывает хорошую технику и решает исходный вопрос, который не был сформулирован достаточно точно. Однако я все равно был бы признателен за подсказку, как решить проблему неявных значений неоднозначности. - person Landei; 08.10.2010
comment
Что ж, три года спустя и несколько версий Scala, похоже, ответа не существует. (Я столкнулся с той же проблемой.) - person Tim; 16.03.2013

Это потому, что Some(42) - более конкретный тип, чем Option[Int]. Это Some[Int]. См. Альтернативное кодирование ниже:

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))
    }
}
person Daniel C. Sobral    schedule 28.09.2010
comment
Я действительно не защищаю использование Option.apply, если только не использую его для кодирования нулевой проверки. Между прочим, с помощью scalaz я бы написал 42.some или 42.pure[Option], оба типа которых были бы Option[Int]. - person retronym; 29.09.2010
comment
@retronym - Почему вы не выступаете за использование Option.apply? - person Rex Kerr; 29.09.2010
comment
Я использую его, когда у меня есть ссылка, потенциально допускающая значение NULL, которую я хочу преобразовать в Some или None. Я могу читать Some(x) : Option[A] и полагать, что у меня будет Some, без отслеживания происхождения x. Если аргумент является буквальным, как в этом примере, это нормально. - person retronym; 29.09.2010
comment
Я использовал вариант здесь просто в качестве примера. Я хочу избежать явной типизации, когда у меня есть Bars для абстрактных базовых классов и т. Д. - person Landei; 29.09.2010