2012-12-21 15 views
12

Questa domanda riguarda un limite del sistema di risoluzione implicito di Scala che ho incontrato poche volte quando si utilizza Scalaz e che per me non ha molto senso. Ho distillato il problema in una versione senza Scalaz di seguito, ma sono felice di fornire ulteriori informazioni sulla motivazione, se necessario.Distinzione tra tipo alias e tipo lambda

Supponiamo che io sono un paio di classi di tipo quella testimonianza qualcosa su un tipo di costruttore:

import scala.language.higherKinds 

trait Foo[F[_]] 
trait Bar[F[_], A] 

Ora anche supporre che, se ho un esempio Foo per alcuni F, so di avere anche un'istanza Foo per Bar[F, _]:

implicit def barFoo[F[_]: Foo] = new Foo[({type L[X] = Bar[F, X]})#L] {} 

ho anche le istanze per List e il lato destro del Either:

implicit object listFoo extends Foo[List] 
implicit def eitherFoo[A] = new Foo[({type L[X] = Either[A, X]})#L] {} 

Ora è abbastanza chiaro che dovrei essere in grado di scrivere il seguente:

type BarList[X] = Bar[List, X] 

implicitly[Foo[BarList]] 

o, equivalentemente:

implicitly[Foo[({type L[X] = Bar[List, X]})#L]] 

E, in effetti, sia il lavoro esattamente come previsto.

quindi cerco il seguente:

type StringOr[X] = Either[String, X] 
type BarStringOr[X] = Bar[StringOr, X] 

E poi:

scala> implicitly[Foo[BarStringOr]] 
res2: Foo[BarStringOr] = [email protected] 

Anche in questo caso, nessuna sorpresa qui. Ma poi cerco:

implicitly[Foo[({type L[X] = Bar[StringOr, X]})#L]] 

E ottengo il seguente:

<console>:15: error: could not find implicit value for parameter e: Foo[[X]Bar[[X]scala.util.Either[String,X],X]] 
       implicitly[Foo[({type L[X] = Bar[StringOr, X]})#L]] 
         ^

Si noti che non ho alcun problema dedurre la necessaria Foo istanza per StringOr, o chiamando barFoo esplicitamente per ottenere l'istanza desiderata:

scala> implicitly[Foo[StringOr]] 
res4: Foo[StringOr] = [email protected] 

scala> barFoo[StringOr] 
res5: Foo[[X]Bar[StringOr,X]] = [email protected]179fbfea 

Ho difficoltà a identificare quale importante distinzione ci potrebbe essere tra lo List e StringOr casi che consentono al tipo lambda di funzionare per il primo ma non il secondo.

Ho provato questo su Scala 2.10.0-RC5 e 2.9.2. L'aggiunta della covarianza in tutto non aiuta.

Mi manca qualcosa di ovvio? Qualcuno può indicarmi qualcosa nelle specifiche che mi aiuterebbe a dare un senso a questo, o alle discussioni precedenti su argomenti simili?

+0

Non sono sicuro su questo argomento, ma sembra che abbia qualcosa a che fare con https://issues.scala-lang.org/browse/SI-2712 – aemxdp

+0

@andriyp: Grazie, ma non lo sono certo che vedo come ciò è correlato. –

+0

Travis ha anche aperto un biglietto su questo a https://issues.scala-lang.org/browse/SI-6895 –

risposta

4

OK, non sono sicuro al 100%, ma penso che possiamo fare alcuni progressi riducendo questo al caso più semplice possibile che non riesce. Gli impliciti non sono il problema qui, né gli alias di tipo.Questo è sufficiente a fallire:

trait Foo[F[_]] 
trait Bar[F[_], A] 

def barFoo[F[_]: Foo] = new Foo[({type L[X] = Bar[F, X]})#L] {} 

val res1: Foo[({type L[X] = Either[String, X]})#L] = null 

val works = barFoo[({type L[X] = Either[String, X]})#L](res1) 
val fails = barFoo(res1) 

Il problema issems essere inabality di Scala di dedurre l'argomento tipo per barFoo come [X]Either[java.lang.String,X]. Questo sembra dovuto (o almeno relativo a) a scalac's refusal to infer a partially applied type constructor.

(In una nota correlata, questo è un esempio di uno di quei tipi che Scala consideres inaccettabilmente complesso).