具有类型变量的类型模式的用例和示例 [英] Use case and examples for type pattern with type variable

查看:50
本文介绍了具有类型变量的类型模式的用例和示例的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我发现在进行类型模式匹配时阅读了 Scala 支持绑定类型变量的规范:

I found out reading the spec that scala supports binding type variables when doing a type pattern match:

Map(1 -> "one", 2 -> "two") match {
  case l: Map[k, v] =>
    // binds k to Int and v to String
    // k and v are types as shown here:
    val i: Iterator[Tuple2[k, v]] = l.iterator
    println(i.mkString(", "))
}

我可以用它做些什么奇特的事情或实用的事情吗?还是绑定类型变量仅用于类型文档目的?

Are there any fancy things or practical things I can do with this? Or binding type variables is only useful for type documentation purpose?

突然想到Scala有时需要类型注解,比如定义函数,于是尝试:

It occurred to me that Scala sometimes needs type annotations, such as defining function, so I tried:

def prepender(obj: Any) = obj match {
  case xs: List[a] => (x: a) => x :: xs
  case opt: Some[a] => (x: a) => x :: Nil
}

但是返回函数的类型很奇怪:

But then the type of the return function is weird:

prepender: (obj: Any)a with a => List[Any] forSome { type a; type a }

scala> val p = prepender(List(1,2))
p: a with a => List[Any] forSome { type a; type a } = <function1>

scala> p(1)
<console>:10: error: type mismatch;
 found   : Int(1)
 required: a(in value res7) with (some other)a(in value res7) where 
   type (some other)a(in value res7), type a(in value res7)

推荐答案

我希望这不会太长,但我严重怀疑,这就是为什么我要先尝试提供一个快速的答案:当你命名(抽象)某物,主要用例是稍后引用它".好吧,那现在没有帮助,是吗?

I hope this won't get too long, but I seriously doubt it, that's why I'm gonna try to provide a quick answer first: "When you name (abstract) something, the main use case is referring to it later". Well that wasn't helpful now, was it?

考虑这个简单的 Scala 函数:

Consider this simple Scala function:

val sum = (a: Int, b: Int) => a + b

编译器不需要知道a 是一个ab 是一个b.它只需要知道 ab 属于 Int 类型并且 a 出现在 之前>b(在这种情况下这无关紧要,因为加法是可交换的,但无论如何编译器都会关心!).Scala 提供了(不要误会我也喜欢它)编译器友好的占位符语法,它可以作为这种假设"的证明.

The compiler does not need to know that a is an a and b is a b. All it needs to know that a as well as b are of type Int and that a comes before b (which wouldn't matter in this case since addition is commutative, but the compiler cares anyway!). Scala offers a (don't get me wrong I also love it) compiler friendly placeholder syntax, which acts as a proof of this "hypothesis".

val sum: (Int, Int) => Int = _ + _ // where the 1st _ differs from the 2nd _

现在看看这个:

case x: SomeTypeParameterizedWith[AnotherType] // AnotherType is erased anyway
case x: SomeParameterizedType[_] // Existential type
case x: SomeParameterizedType[kind] // Existential type which you can reference

当您不关心类型参数时,请使用占位符语法.当您这样做时(无论出于何种原因),您应该使用小写字母命名类型参数,以便编译器知道您希望将其视为标识符.

When you don't care about the type argument use the placeholder syntax. When you do (for whatever reason) care you should name the type argument with a lower case so the compiler knows you want to treat it as an identifier.

回到你的问题.

存在类型的主要用途是处理 Java 的通配符类型.这取自 Scala 编程 - 存在类型,并由您稍作修改真的.

The primary use for existential types is working around Java's wildcard types. This is taken from Programming in Scala - Existential Types and was slightly modified by yours truly.

// This is a Java class with wildcards
public class Wild {
  public java.util.Collection<?> contents() {
    java.util.Collection<String> stuff = new Vector<String>();
    stuff.add("a");
    stuff.add("b");
    stuff.add("see");
    return stuff;
  }
}

// This is the problem
import scala.collection.mutable.Set
val iter = (new Wild).contents.iterator
val set = Set.empty[???] // what type goes here?
while (iter.hasMore)
  set += iter.next()

// This is the solution
def javaSet2ScalaSet[T](jset: java.util.Collection[T]): Set[T] = {
  val sset = Set.empty[T] // now T can be named!
  val iter = jset.iterator
  while (iter.hasNext)
    sset += iter.next()
  sset
}

好的,刚刚发生了什么?简单的泛型,没有魔法?!如果您每天都在处理泛型,这对您来说看起来很正常,但您会忘记,将类型参数引入范围的超级概念仅适用于类和方法.如果你在一个类或一个方法之外,只是在某个不知名的地方(比如 REPL)的随机范围内怎么办?或者如果你在一个类或一个方法中,但类型参数没有被引入到它们的作用域中怎么办?这就是您的问题和这个答案发挥作用的地方.

Ok, so what just happened? Simple generics, no magic there?! If you are dealing with generics on a day to day basis this looks normal to you, but you are forgetting, that the ultra super concept of introducing type arguments into scope works only on classes and methods. What if you are outside of a class or a method, just in some random scope in the middle of nowhere (like REPL)? Or what if you are in a class or a method but the type arguments have not been introduced into their scopes? This is where your question and this answer come in play.

val set = new Wild().contents match {
  case jset: java.util.Collection[kind] => {
    val sset = Set.empty[kind]
    val iter = jset.iterator
    while (iter.hasNext)
      sset += iter.next()
    sset
  }
}

标识符 kind 是必需的,以便编译器可以验证您指的是同一件事.

The identifier kind is required so the compiler can verify that you are referring to the same thing.

请注意,您不能只将字符串添加到 set 中,因为 set 的类型是 Set[_].

Note, that you can't just add strings into the set since the type of the set is Set[_].

这篇关于具有类型变量的类型模式的用例和示例的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆