2015-04-24 4 views
5

Po zapoznaniu się z kilkoma przykładami typów wyższych typu Scala w samouczku this zacząłem bawić się przy (a) implicits oraz (b) technikach pisania metod, które ogólnie obsługują podklasy cechy, którą definiuje jako wyższą. rodzaj rodzaju. Ja już mam wielką odpowiedź na (b) here który porusza mnie bliżej do rozwiązania poniższego problemu:Niejawna niejednoznaczność Scala nie zostanie rozwiązana bez denerwującego atrapa do oznaczenia typu.

Chcę utworzyć fabrykę do tworzenia pojedynczych, kontenery (zestawy, listy, tablice), które owijają element, który Przejdę do funkcji fabrycznej. [Nawet jeśli jest już biblioteka Scala , która to robi, robię to jako ćwiczenie edukacyjne. Ale, hej ... jeśli jest tylko jedna, proszę dać mi znać]

Wynik końcowy powinien pozwolić mi to zrobić!

scala> var j: Set[String] = wrapItemInContainer("foo") 
j: Set[String] = Set(foo) 

scala> var k: List[Int] = wrapItemInContainer(9) 
k: List[Int] = List(9) 

Rozwiązanie wymyśliłem jest pokazany poniżej. Muszę dostarczyć denerwującego atrapa końcowego argumentu, abym mógł pomóc kompilatorowi dowiedzieć się jakiego kontenera chcę. To działa, ale jestem zdziwiony faktem, że podpis typu zmiennej, której chcę przypisać wynik do (j, k..etc.) Nie daje kompilatorowi informacji wystarczającej do wykrycia, który niejawnie zdefiniował ContainerFactory musi być użyty.

Ten przylegający rozwiązanie działa:

trait ContainerFactory[M[_]] { def put[A](x: A): M[A] } 


implicit val factory = new ContainerFactory[List] { def put[A](x: A) = List(x) } // factory for List containers 
implicit val factory2 = new ContainerFactory[Set] { def put[A](x: A) = Set(x)} // factory for Set containers 

def wrapItemInContainer[ M[A]: ContainerFactory, A](item: A, helper: M[A]) : M[A] = { 
    val c = implicitly[ContainerFactory[M]]        
    c.put(item) 
} 

var j: List[Int] = wrapItemInContainer(9, List(0)) 

Ale naprawdę chcę coś się hałaśliwym drugi argument:

def wrapItemInContainer[ M[A]: ContainerFactory, A](item: A) : M[A] = { 
    val c = implicitly[ContainerFactory[M[A]]]        
    c.put(item) 
} 

var j: List[Int] = wrapItemInContainer(9) // this does not work. 

otrzymuję ten błąd:

<console>:17: error: ambiguous implicit values: 
both value factory of type => ContainerFactory[List] 
and value factory2 of type => ContainerFactory[Set] 
match expected type ContainerFactory[M] 
     var j: List[Int] = wrapItemInContainer(9) 

pomysłów lub wskazówki są bardzo cenne!

-Chris

+2

Daj znać: to się czasem nazywa "Spiczaste". Zwykle jest to część klasy typu Applicative, np. W [scalaz] (https://github.com/scalaz/scalaz/blob/466d4da2951666f736d554104a2b3b956e4e6885/core/src/main/scala/scalaz/Applicative.scala). – phg

Odpowiedz

6

Trzeba uczynić kowariantna fabryka w M. Zobacz https://groups.google.com/forum/#!topic/scala-language/dQEomVCH3CI i https://issues.scala-lang.org/browse/SI-7332.

To kompiluje:

import language.higherKinds 

trait Factory[+M[_], A] { def put(x: A): M[A] } 

implicit def factory1[A] = 
    new Factory[List, A] { def put(x: A) = List(x) } 
implicit def factory2[A] = 
    new Factory[Set, A] { def put(x: A) = Set(x) } 

def wrap[M[_], A](a: A)(
    implicit factory: Factory[M, A]): M[A] = 
    factory.put(a) 

val j: Set[String] = wrap("foo") 
val k: List[Int] = wrap(9) 

Wziąłem wolność skrócenie swoje nazwiska. Postępowałem również zgodnie z radą, którą otrzymałeś pod numerem how to generically handle Scala Higher-kinded types when coding factories for generating containers of items of type 'X', aby ustawić parametr A na fabrykę.