scala - 具有类型变量的类型模式的用例和示例

标签 scala types pattern-matching

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

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(", "))
}

我可以用这个做任何花哨或实用的事情吗?或者绑定(bind)类型变量仅对类型文档有用?

我突然想到 Scala 有时需要类型注解,比如定义函数,所以我尝试了:
def prepender(obj: Any) = obj match {
  case xs: List[a] => (x: a) => x :: xs
  case opt: Some[a] => (x: a) => x :: Nil
}

但是返回函数的类型很奇怪:
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)

最佳答案

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

考虑这个简单的 Scala 函数:

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

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

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

回到你的问题。

存在类型的主要用途是处理 Java 的通配符类型。
这取自 Programming in Scala - Existential Types并且真的被你稍微修改了。
// 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)的某个随机范围内怎么办?或者,如果您在一个类或一个方法中,但类型参数没有被引入它们的作用域怎么办?这就是你的问题和这个答案发挥作用的地方。
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是必需的,因此编译器可以验证您指的是同一事物。

请注意,您不能只在 set 中添加字符串。由于 set 的类型是 Set[_] .

关于scala - 具有类型变量的类型模式的用例和示例,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7313948/

相关文章:

jpa - 如何使用 JPA 映射 Duration 类型

scala - 从相同特征派生的案例类的模式匹配

python - 结构模式匹配默认情况下如何访问匹配值?

erlang - 模式匹配的基本问题

scala - 如何避免由于 akka actor 中的锁定超时而丢失消息?

function - 为什么我的Scala函数返回类型Unit而不是最后一行?

scala - 是否有任何功能语言编译器/运行时可以优化链式迭代?

C# DBNull 和可空类型 - 最干净的转换形式

android - 在没有 Java 的情况下创建 Android 应用程序

types - 有没有办法约束仿函数的参数签名,以便参数可以为结构提供未指定的相等类型?