为什么传递Int,其中F [_]参数预期有效?

时间:2015-09-30 21:21:57

标签: scala higher-kinded-types

假设我们有一个功能:

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

的示例

4 个答案:

答案 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]。在这里它变得奇怪。请记住,AnyAnyVal(Scala的父级,Java的原语)和AnyRef的父级(Scala相当于Java&#) 39; s Object)(有关详细说明,请参阅Scala Documentation)。

Scala的类型推断已决定​​此Bar的构造函数应接受Any的{​​{1}}和F的{​​{1}} }。由于Int确实是AAny的父级,这很好。 List确实已参数化为Int,因此很好。有点奇怪的是,Scala可以说List也是[Int]类型。我对这部分没有很好的解释。

对于最后一个,我真的很困惑,我不得不怀疑这是不是一个错误。出于某种原因,即使IntAny[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

因此函数barbaz可以找到任何容器类型(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))不起作用,因为编译器无法推断支持ListSome的类型。但是你可以像上面那样向下转发,bar[Any, Int](List(1), Some(1))可以工作。