【问题标题】:Scala: type inference of generic and it's type argumentScala:泛型的类型推断及其类型参数
【发布时间】:2014-09-17 10:56:55
【问题描述】:

假设我有任意单参数泛型类的实例(我将在演示中使用List,但这可以是任何其他泛型)。

我想编写可以接受实例 (c) 并能够理解什么泛型类 (A) 和什么类型参数 (B) 生成类 (C) 的泛型函​​数那个实例。

我想出了这样的东西(函数的主体并不真正相关,但证明C符合A[B]):

def foo[C <: A[B], A[_], B](c: C) {
  val x: A[B] = c
}

... 如果您像这样调用它,它会编译:

foo[List[Int], List, Int](List.empty[Int])

...但是如果我省略显式类型参数并依赖推理,编译会失败并出现错误:

foo(List.empty[Int])

我得到的错误是:

    Error:Error:line (125)inferred kinds of the type arguments (List[Int],List[Int],Nothing) do not conform to the expected kinds of the type parameters (type C,type A,type B).
List[Int]'s type parameters do not match type A's expected parameters:
class List has one type parameter, but type A has one
  foo(List.empty[Int])
  ^
    Error:Error:line (125)type mismatch;
 found   : List[Int]
 required: C
  foo(List.empty[Int])
                ^

正如您所见,在这种情况下,Scala 的类型推断无法正确推断类型(似乎猜测第二个参数是 List[Int] 而不是 List,第三个参数是 Nothing 而不是 Int)。

我假设我想出的foo 的类型边界不够精确/正确,所以我的问题是如何实现它,以便 Scala 可以推断参数?

注意:如果有帮助,可以假设所有潜在的泛型 (As) 继承/符合某些共同祖先。例如,A 可以是继承自 Seq 的任何集合。

注意:此问题中描述的示例是合成的,是我要解决的更大问题的提炼部分。

【问题讨论】:

    标签: scala generics variance


    【解决方案1】:

    这是当前 Scala 对类型构造函数的类型推断的已知限制。将形参 c 的类型定义为 C 仅收集 C(并间接到 A)的类型约束,但不收集 B。换句话说,List[Int] &lt;: C =&gt; { List[Int] &lt;: C &lt;: Any, C &lt;: A[_] &lt;: Any }

    有一个非常简单的翻译可以指导此类情况的类型推断。你的情况是:

    def foo[C[_] <: A[_], A[_], B](c: A[B]) { val x: A[B] = c }
    

    语义相同,只是类型签名略有不同。

    【讨论】:

    • 感谢您的回答,但实际上并没有解决问题。我从C 类型的参数开始(我知道这是A[B] 的结果),并希望将其分解为构建它的类型(即B 和A)。正如@4lex1v 在您的示例中指出的那样,C 根本没有真正使用,我需要能够在函数内使用所有 3 种类型(即主体应该知道参数的类型为 C 并符合 @ 987654328@)
    • 啊,是的。在那种情况下,您是否在遵循 ticket SI-2712 中提到的 Unpack 隐式黑客攻击?
    【解决方案2】:

    除了 hubertp 答案之外,您还可以通过删除过时的(在您的示例中)类型变量 C 来修复您的功能,例如:

    def foo[A[_], B](c: A[B]) {
      val x: A[B] = c
    }
    

    在这种情况下,scalac 将推断A[_]ListBInt

    更新(根据评论)。

    如果您需要证明CA[B] 的子类型,则使用隐式:

    def foo[A[_], B, C](c: C)(implicit ev: C <:< A[B]) = {
      val x: A[B] = c
    }
    

    然后它不会编译这个:

    scala> foo[List, String, List[Int]](List.empty[Int])
    <console>:9: error: Cannot prove that List[Int] <:< List[String].
                  foo[List, String, List[Int]](List.empty[Int])
    

    【讨论】:

    • 查看我对 hubertp 答案的评论
    • @EugenyLoy 检查我的更新,这是你需要的吗?但是 scalac 不会将 C 分解为 A[_] 和 B,你可以从 typelevel scala fork 写给那些人,也许他们会实现它。
    • 是的,这是最接近我需要的。顺便说一句,=:=&lt;:&lt; 更适合我的目的,但我有这个想法,这并不重要。
    猜你喜欢
    • 2015-09-11
    • 1970-01-01
    • 2023-03-08
    • 1970-01-01
    • 2014-02-04
    • 2012-11-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多