2015-09-30 20 views
14

Sagen wir, wir haben eine Funktion:Warum wird Int übergeben, wo ein F [_] Parameter erwartet wird?

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

das All folgende Verhalten sind klar:

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

Aber

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

Warum?

P.S. Beispiel von FSiS Part 1 - Type Constructors, Functors, and Kind Projector

+4

Sowohl 'Any' und' Nothing' ist Art polymorphe siehe z.B. [SI-9248] (https://issues.scala-lang.org/browse/SI-9248). –

+2

btw 'bar (1, Liste (1))' compiliert nicht in '2.9', sondern in' 2.10'. – dmitry

Antwort

4

denke ich folgendes einen Anhaltspunkt gibt (obwohl es nach wie vor einige Rätsel beteiligt ist):

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 

So sind die Funktionen bar und baz sind zu finden, jede Art Behälter können sie (Comparable für String, AbstractFunction0 für einen Fall, Klasse usw.), um den erwarteten F[_] zu entsprechen.

Spekulation: im Fall von Int, ich vermute, wir treffen einen speziellen "Container" -Typ für was sind (boxed) primitive Typen in den zugrunde liegenden Bytecode. Wenn dieser spezielle Typ nur als "Any" zurück in Scala gedruckt werden kann, aber wirklich ein spezieller Typ ist, den wir uns als "Any[_]" vorstellen können, dann könnte das die Ergebnisse erklären, die wir sehen. Als Bestätigung, dass dies mit der speziellen Behandlung von Primitiven zu tun hat, beachte, dass es für nicht primitive einfache Typen fehlschlägt, wie die (nicht-fallende) Klasse Foo oben.

4

Ich denke, dass Sie in eine Beschränkung der Art Rückschluss-System laufen. Um etwas Licht in diese, mal schauen, was passiert, wenn wir diese ein wenig neu definieren eine nützlichere Ausgabe zu erhalten:

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

Im ersten Beispiel haben wir eine Bar[List, Int], die durchaus Sinn macht, passierten wir in zwei List[Int].

In der zweiten und dritten haben wir eine Bar[Any, Int]. Hier wird es komisch. Denken Sie daran, dass Any das übergeordnete Element von beiden AnyVal (die Eltern von Scala Äquivalenten von Java-Primitiven) und AnyRef (das Scala-Äquivalent von Java-Objekt) (siehe Scala Documentation für weitere Erklärung).

Scala Typinferenz hat entschieden, dass diese Bar ‚s Konstruktor Any für F annehmen sollte, und Int für A. Als Any ist in der Tat ein Elternteil von List und Int, das ist in Ordnung. Die List ist in der Tat als [Int] parametriert, so dass es in Ordnung ist. Was komisch ist, ist, dass Scala okay ist zu sagen, dass der Int auch vom Typ Any[Int] ist. Ich habe keine gute Erklärung für diesen Teil.

Mit dem letzten, das ist, wo ich ehrlich verwirrt bin, und ich muss mich fragen, ob das ein Fehler ist. Aus irgendeinem Grund, obwohl beide List und Some Kinder von Any sind, und beide mit Int parametrisiert sind, erlaubt es es nicht. Ich fürchte, ich bin nicht gut in die Feinheiten des Compilers Inferenzmethoden versiert, aber für das, was es wert ist, explizit die Parameter angeben, funktioniert:

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

Für mich, dass die Typinferenz System schlägt nur kann‘ t richtig die Typen ableiten, oder es sind Inferenztypen, die nicht korrekt sind.

3

Dies funktioniert, weil der Compiler einige Typen für Sie leitet. Hier ist, was das sieht aus wie mit den Typen ergänzt in bar:

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

Diese nicht für bar(List(1), Some(1)) funktioniert, da der Compiler nicht eine Art sowohl zu unterstützen List und Some ableiten kann. Sie können dies jedoch wie oben beschrieben, bar[Any, Int](List(1), Some(1)) funktioniert.

4

Es scheint, dass etwas mit dem Typ Any in Verbindung mit dem freundlichen System verdrahtet ist.

Any nimmt keinen Typparameter: val i:Any[Int] = 1 gibt einen Fehler, so sollte es von einfacher Art sein.

Any in einer Position eingesetzt werden, wo ein höherer kinded Typ zu erwarten ist, wie Sie in Ihrem Beispiel bar[Any, Nothing](1,1) zeigten

Wenn wir Any in einer höheren kinded Position dieses Typ Parameter auf magische Weise in eine einfache Art verwandelt sich verwenden und die Der Typparameter dieses früheren höherwertigen Typs wird vollständig ignoriert.

Wenn die ersten Typ-Parameter von bar sind Any wir können aber jeder Typ als zweiten Parameter und es wird immer kompilieren:

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) 

Es scheint ein Problem mit der Typinferenz zu sein, das einige Beispiele führt zu scheitern ohne Typ Anmerkungen.

Ich habe über dieses Verhalten durch Versuch und Irrtum herausgefunden, ich weiß nicht, ob es Fehler oder ein Feature ist ;-)