2010-09-28 11 views
9

Rozważmy następujący kod:niejawny rozdzielczości parametr dla wyższych typów kinded

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

To działa, ale muszę wpisać Niektórzy (42), a opcja [Int], inny niejawny obiekt OptionBar nie będzie rozwiązany (ponieważ oczekuje się paska [Część]). Czy istnieje sposób na uniknięcie jawnego wpisywania, tak aby uzyskać niejawny obiekt OptionBar w teście, nawet jeśli test jest podawany z parametrem Some lub None?

[Wyjaśnienie]

  • użyłem opcji tutaj tylko jako przykład, powinien także działać, jeśli mam Bar dla klasy abstrakcyjnej itp
  • Roztwór powinien także działać, gdy inne, niezwiązane bary w zakresie, powiedzmy implicit object listBar extends Bar[list]

[Aktualizacja]

wydaje się, że co baru parametru kontrawariantny robi t podstęp:

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

Oczywiście jest to poważne ograniczenie możliwości w Bar, więc nadal mam nadzieję na lepszą odpowiedź.

+1

Dlaczego kontrawariancja jest poważnym ograniczeniem? Bez użycia go, wtedy Bar jest niezmienny. Jeśli próbujesz użyć Bar jako klasy dla typów o wyższych typach, to kontrawariancja wydaje się pasować do mojego umysłu. To oczywiście, dopóki nie chcesz inaczej traktować podklas. Jednak w takim przypadku nadal masz inne sztuczki, takie jak ukryte rozdzielczości "priorytety" – jsuereth

+0

@ Jos: Weź pod uwagę coś takiego jak 'cecha Bar [Q [_]] {def zero [T]: Q [T]}', zwracając Brak i Zero w moich przykładach. Ale nie mogę mieć takiej metody w Bar, jeśli zdefiniuję Q jako przeciwieństwo. Jeśli wiesz, jak rozwiązać ten problem, proszę dać mi znać ... – Landei

+1

Ponadto, dla naturalnie sprzecznych klas typu, takich jak "Równe [T]", wyszukiwanie niejawne będzie faworyzować "Równe [Zwierzę]" nad "Równe [Pies] ': http://www.scala-lang.org/node/4626. Dziedziczenie i klasy typów są naprawdę trudne do połączenia razem. – retronym

Odpowiedz

7

To nie będzie działać we wszystkich przypadkach, ale jak wspomniano, można spróbować to:

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

Co ciekawe, to nie wywnioskować, choć wyraża to samo:

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

Aby dowiedzieć się więcej o <:< patrz:

+0

Działa to jednak, jeśli dodasz inną niejawną dla paska w tym zakresie, powiedz 'niejawny obiekt ListBar rozszerza pasek [lista]' otrzymujesz błąd "niejednoznaczne wartości". – Landei

+0

Podaję nagrodę za tę odpowiedź, ponieważ pokazuje miłą technikę i rozwiązuje początkowe pytanie, które nie zostało sformułowane wystarczająco precyzyjnie. Jednak nadal doceniam wskazówki, jak rozwiązać problem "ukrytych wartości ambiguos". – Landei

+0

Cóż, trzy lata i kilka wersji Scali później, nie wydaje się, że istnieje odpowiedź. (Stawiam czoła temu samemu wyzwaniu). – Tim

5

To dlatego Some(42) jest bardziej specyficzny typ niż Option[Int]. Jest to Some[Int]. Zobacz alternatywne kodowanie poniżej:

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

Naprawdę nie zalecam używania 'Option.apply', chyba że używam go do zakodowania zerowego czeku. Nawiasem mówiąc, przy użyciu skalazu napisałbym '42.some' lub' 42.pure [Option] ', oba typy jako' Option [Int] '. – retronym

+0

@retronym - Dlaczego nie zaleca się używania 'Option.apply'? –

+1

Używam go, gdy mam potencjalnie zerowalne odniesienie, które chce przekonwertować na 'Some' lub' None'. Mogę przeczytać 'Some (x): Option [A]' i powód, że będę miał 'Some' bez śledzenia pochodzenia' x'. Jeśli argument jest dosłowny, jak w tym przykładzie, jest w porządku. – retronym

Powiązane problemy