2015-09-30 10 views
14

Diciamo che abbiamo una funzione:Perché è possibile passare a Int dove è previsto un parametro F [_] valido?

def bar[F[_], A](x: F[A], y: F[A]) = null 

Tutto il seguente comportamento sono chiari:

bar(List(1, 2, 3), List(1)) // compile ok 
bar(List(1), Some(1)) // doesn't compile 

Ma,

bar(1, List(1)) // compile ok 
bar(1, 1) // compile ok 

Perché?

P.S. esempio da FSiS Part 1 - Type Constructors, Functors, and Kind Projector

+4

Sia '' Any' e Nothing' sono tipo polimorfo-veda per es [SI-9248] (https://issues.scala-lang.org/browse/SI-9248). –

+2

btw 'bar (1, List (1))' non compila in '2.9', ma in' 2.10'. – dmitry

risposta

4

Penso che la seguente dà un indizio (anche se c'è ancora qualche mistero coinvolti):

def baz[F[_], A](x: F[A]): F[A] = x 

scala> baz("str") 
res5: Comparable[String] = str 

scala> baz(1) 
res6: Any = 1 

scala> class Foo 
defined class Foo 

scala> baz(new Foo) 
<console>:15: error: no type parameters for method baz: (x: F[A])F[A] exist so that it can be applied to arguments (Foo) 
--- because --- 
argument expression's type is not compatible with formal parameter type; 
found : Foo 
required: ?F 
       baz(new Foo) 
      ^
<console>:15: error: type mismatch; 
found : Foo 
required: F[A] 
       baz(new Foo) 
       ^

scala> case class Foo2 
warning: there were 1 deprecation warning(s); re-run with -deprecation for details 
defined class Foo2 

scala> baz(Foo2) 
res10: scala.runtime.AbstractFunction0[Foo2] = Foo2 

Quindi le funzioni bar e baz stanno trovando qualsiasi tipo di contenitore che possono (Comparable for String, AbstractFunction0 per un caso classe, ecc.) per corrispondere al previsto F[_].

Speculazione: nel caso di Int, sospetto che stiamo colpendo un tipo di "contenitore" speciale per i tipi primitivi (in box) nel codice byte sottostante. Se questo tipo speciale può essere stampato solo su Scala come "Any", ma è davvero un tipo speciale che possiamo pensare come "Any[_]", allora questo potrebbe spiegare i risultati che vediamo. Come conferma che ciò ha a che fare con la gestione speciale delle primitive, si noti che non riesce per tipi semplici non primitivi, come la classe (non-caso) Foo precedente.

4

Penso che tu stia correndo una limitazione del sistema di inferenza di tipo. Per far luce su questo, diamo un'occhiata a cosa succede quando ridefiniamo questo un po 'per ottenere un output più utile:

class Bar[F[_], A](x: F[A], y: F[A]) {} 
res0: Bar[List,Int] = [email protected] 
new Bar(List(1,2,3), List(1)) 
res1: Bar[Any,Int] = [email protected] 
new Bar(List(1), 1) 
res2: Bar[Any,Int] = [email protected] 
new Bar(List(1), Some(1)) 
<console>:12: error: inferred kinds of the type arguments (Product with java.io.Serializable,Int) do not conform to the expected kinds of the type parameters (type F,type A) in class Bar. 
Product with java.io.Serializable's type parameters do not match type F's expected parameters: 
<refinement of Product with java.io.Serializable> has no type parameters, but type F has one 
     new Bar(List(1), Some(1)) 
    ^
<console>:12: error: type mismatch; 
found : List[Int] 
required: F[A] 
     new Bar(List(1), Some(1)) 
       ^
<console>:12: error: type mismatch; 
found : Some[Int] 
required: F[A] 
     new Bar(List(1), Some(1)) 

Nel primo esempio, abbiamo un Bar[List, Int], che ha perfettamente senso, siamo passati in due List[Int].

Nel secondo e terzo, abbiamo un Bar[Any, Int]. Ecco dove diventa strano. Ricordare che Any è l'elemento principale di entrambi gli AnyVal (il genitore degli equivalenti di Scala delle primitive di Java) e AnyRef (l'equivalente di Scala dell'oggetto Java) (vedere Scala Documentation per ulteriori spiegazioni).

di Scala inferenza di tipo ha deciso che questo costruttore Bar s' dovrebbe accettare Any per F e Int per A. Come Any è in effetti un genitore di List e Int, va bene. Il è infatti parametrizzato come [Int], quindi va bene. La cosa strana è che Scala va bene dicendo che lo Int è anche di tipo Any[Int]. Non ho una buona spiegazione per quella parte.

Con l'ultimo, è dove sono sinceramente confuso, e devo chiedermi se questo è un bug. Per qualche motivo, anche se sia sia Some sono figli di Any ed entrambi sono parametrizzati con Int, non lo consente. Ho paura che non sono esperto nelle complessità di metodi di inferenza del compilatore, ma per quello che vale, specificare in modo esplicito i parametri funziona:

new Bar[Any,Int](List(1), Some(1)) 
res14: Bar[Any,Int] = [email protected] 

Per me, che suggerisce il sistema di inferenza di tipo solo puo' t dedurre correttamente i tipi, o si tratta di tipi che non sono corretti.

3

Questo funziona perché il compilatore ne deduce alcuni tipi. Ecco come si presenta con i tipi aggiunti in bar:

bar[Any, Int](1, List(1)) // compile ok 
    bar[Any, Nothing](1, 1) // compile ok 

Questo non funziona per bar(List(1), Some(1)) perché il compilatore non può dedurre un tipo di sostegno sia List e Some. Puoi comunque eseguire il downcast come sopra, bar[Any, Int](List(1), Some(1)) funziona.

4

Sembra che ci sia qualcosa di cablato in corso con il tipo Any in combinazione con il sistema gentile.

Any non accetta un parametro di tipo: val i:Any[Int] = 1 restituisce un errore, quindi dovrebbe essere di tipo semplice.

Any può essere utilizzato in una posizione in cui si prevede un tipo kinded più alto, come hai mostrato nel tuo esempio bar[Any, Nothing](1,1)

Se usiamo Any in una posizione kinded alto è questo parametro type si trasforma magicamente in un semplice tipo e la il parametro type di questo precedente tipo di tipo più alto viene completamente ignorato.

Quando il primo parametro tipo di bar è Any che possiamo, ma qualsiasi tipo come secondo parametro e sarà sempre la compilazione:

bar[Any,String](List(1),List(2)) 
bar[Any, Boolean](1,2) 
bar[Any, Int](List(), true) 
case class A() 
bar[Any, A](List, A) 

Sembra esserci un problema con il tipo di inferenza che porta alcuni esempi per fallire senza annotazioni di tipo.

ho scoperto su questo comportamento per tentativi ed errori, non so se si tratta di bug o una caratteristica ;-)

Problemi correlati