假设我们有一个功能:
def bar[F[_], A](x: F[A], y: F[A]) = null
以下所有行为都很明确:
bar(List(1, 2, 3), List(1)) // compile ok
bar(List(1), Some(1)) // doesn't compile
但是,
bar(1, List(1)) // compile ok
bar(1, 1) // compile ok
为什么?
P.S。来自FSiS Part 1 - Type Constructors, Functors, and Kind Projector
的示例答案 0 :(得分:4)
我认为您正在遇到类型推理系统的限制。为了解释这一点,让我们看看当我们重新定义这一点以获得更有用的输出时会发生什么:
class Bar[F[_], A](x: F[A], y: F[A]) {}
res0: Bar[List,Int] = Bar@69f1a286
new Bar(List(1,2,3), List(1))
res1: Bar[Any,Int] = Bar@7b139eab
new Bar(List(1), 1)
res2: Bar[Any,Int] = Bar@456be73c
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))
在第一个示例中,我们有一个Bar[List, Int]
,这很有道理,我们传递了两个List[Int]
。
在第二个和第三个中,我们有一个Bar[Any, Int]
。在这里它变得奇怪。请记住,Any
是AnyVal
(Scala的父级,Java的原语)和AnyRef
的父级(Scala相当于Java&#) 39; s Object)(有关详细说明,请参阅Scala Documentation)。
Scala的类型推断已决定此Bar
的构造函数应接受Any
的{{1}}和F
的{{1}} }。由于Int
确实是A
和Any
的父级,这很好。 List
确实已参数化为Int
,因此很好。有点奇怪的是,Scala可以说List
也是[Int]
类型。我对这部分没有很好的解释。
对于最后一个,我真的很困惑,我不得不怀疑这是不是一个错误。出于某种原因,即使Int
和Any[Int]
都是List
的子项,并且都使用Some
参数化,但它并不允许它。我担心我不熟悉编译器推理方法的复杂性,但是对于它的价值,明确指出这些参数是有效的:
Any
对我而言,这表明类型推理系统无法正确推断出类型,或推断出不正确的推断类型。
答案 1 :(得分:4)
我认为以下提供了线索(尽管仍然存在一些谜团):
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
因此函数bar
和baz
可以找到任何容器类型(Comparable
表示String,AbstractFunction0
表示案例类等)以匹配预期的{ {1}}。
猜测:在F[_]
的情况下,我怀疑我们正在打一个特殊的容器&#34;类型为底层字节码中的(盒装)基元类型。如果这种特殊类型只能作为&#34; Int
&#34;打印回Scala,但实际上我们可以将其视为&#34; Any
&#34;那么这可以解释我们看到的结果。作为与原语的特殊处理有关的佐证,请注意它对于非原始的简单类型是失败的,例如上面的(非案例)类Any[_]
。
答案 2 :(得分:4)
Any
类型与种类系统相结合似乎有些事情发生了。
Any
不接受类型参数:
val i:Any[Int] = 1
给出错误,因此它应该是简单的。
Any
可用于需要较高种类的位置,如您在示例中所示bar[Any, Nothing](1,1)
如果我们在较高的kinded位置使用Any
,这个类型参数会神奇地变成一个简单的类型,并且完全忽略这个前一个更高的kinded类型的类型参数。
当bar
的第一个类型参数为Any
时,我们可以将任何类型作为第二个参数,它将始终编译:
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)
类型推断似乎存在问题,导致某些示例在没有类型注释的情况下失败。
我通过反复试验发现了这种行为,我不知道它是错误还是功能;-)
答案 3 :(得分:3)
这是有效的,因为编译器会为您推断出某些类型。以下是添加到bar
中的类型的内容:
bar[Any, Int](1, List(1)) // compile ok
bar[Any, Nothing](1, 1) // compile ok
这对bar(List(1), Some(1))
不起作用,因为编译器无法推断支持List
和Some
的类型。但是你可以像上面那样向下转发,bar[Any, Int](List(1), Some(1))
可以工作。