具有类型变量的类型模式的用例和示例
我发现在进行类型模式匹配时,阅读了 scala 支持绑定类型变量的规范:
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(", "))
}
我可以用它做任何奇特的事情或实用的事情吗?或者绑定类型变量仅用于类型文档目的?
我想到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)
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?
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)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我希望这不会太长,但我严重怀疑这一点,这就是为什么我要首先尝试提供一个快速答案:“当你命名(抽象)某个东西时,主要用例是稍后引用它”。好吧,那现在没用了,不是吗?
考虑这个简单的 Scala 函数:
编译器不需要知道
a
是a
且b
是b
>。它只需要知道a
和b
都是Int
类型,并且a
位于之前>b
(在这种情况下这并不重要,因为加法是可交换的,但编译器无论如何都会关心!)。 Scala 提供了(不要误会我的意思,我也喜欢它)编译器友好的占位符语法,它充当了这个“假设”的证明。现在看一下:
当您不关心类型参数时,请使用占位符语法。当您(无论出于何种原因)关心时,您应该用小写字母命名类型参数,以便编译器知道您想要将其视为标识符。
回到你的问题。
存在类型的主要用途是处理 Java 的通配符类型。
这取自 Programming in Scala - Existential Types 并由您稍加修改真的。
好吧,那么刚才发生了什么?简单的泛型,没有魔法吗?!如果您每天都在处理泛型,这对您来说看起来很正常,但您忘记了,将类型参数引入作用域的超级概念仅适用于类和方法。如果您在类或方法之外,只是在某个不知名的地方的随机范围内(例如 REPL)怎么办?或者,如果您位于类或方法中,但类型参数尚未引入其作用域,该怎么办?这就是你的问题和这个答案发挥作用的地方。
标识符
kind
是必需的,以便编译器可以验证您引用的是同一事物。请注意,您不能只将字符串添加到
set
中,因为set
的类型是Set[_]
。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?
Consider this simple Scala function:
The compiler does not need to know that
a
is ana
andb
is ab
. All it needs to know thata
as well asb
are of typeInt
and thata
comes beforeb
(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".Now take a look at this:
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.
Back to your question.
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.
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.
The identifier
kind
is required so the compiler can verify that you are referring to the same thing.Note, that you can't just add strings into the
set
since the type of theset
isSet[_]
.