Scala 中类型归属的目的是什么?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2087250/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-22 01:51:31  来源:igfitidea点击:

What is the purpose of type ascriptions in Scala?

scalastatic-typingascription

提问by davetron5000

There's not much info in the spec on what type ascription is, and there certainly isn't anything in there about the purpose for it. Other than "making passing varargs work", what would I use type ascription for? Below is some scala REPL for the syntax and effects of using it.

规范中没有太多关于什么类型归属的信息,当然也没有任何关于它的目的的信息。除了“使传递可变参数工作”之外,我还会使用类型归属地做什么?下面是一些 scala REPL 的语法和使用效果。

scala> val s = "Dave"
s: java.lang.String = Dave

scala> val p = s:Object
p: java.lang.Object = Dave

scala> p.length
<console>:7: error: value length is not a member of java.lang.Object
       p.length
         ^
scala> p.getClass
res10: java.lang.Class[_ <: java.lang.Object] = class java.lang.String

scala> s.getClass
res11: java.lang.Class[_ <: java.lang.Object] = class java.lang.String

scala> p.asInstanceOf[String].length
res9: Int = 4

回答by Daniel C. Sobral

Type ascription is just telling the compiler what type you expect out of an expression, from all possible valid types.

类型归属只是告诉编译器你期望从所有可能的有效类型的表达式中得到什么类型。

A type is valid if it respects existing constraints, such as variance and type declarations, and it is either one of the types the expression it applies to "is a", or there's a conversion that applies in scope.

如果一个类型遵守现有的约束,例如变化和类型声明,并且它是它所应用的表达式“是一个”的类型之一,或者有一个适用于范围的转换,那么它就是有效的。

So, java.lang.String extends java.lang.Object, therefore any Stringis also an Object. In your example you declared you want the expression sto be treated as an Object, not a String. Since there is no constraints preventing that and the desired type is one of the types sis a, it works.

所以,java.lang.String extends java.lang.Object,因此 anyString也是Object。在您的示例中,您声明希望将表达式s视为Object,而不是String。由于没有限制阻止这种情况发生,并且所需的类型s是 a类型之一,因此它可以工作。

Now, why would you want that? Consider this:

现在,你为什么想要那个?考虑一下:

scala> val s = "Dave"
s: java.lang.String = Dave

scala> val p = s: Object
p: java.lang.Object = Dave

scala> val ss = scala.collection.mutable.Set(s)
ss: scala.collection.mutable.Set[java.lang.String] = Set(Dave)

scala> val ps = scala.collection.mutable.Set(p)
ps: scala.collection.mutable.Set[java.lang.Object] = Set(Dave)

scala> ss += Nil
<console>:7: error: type mismatch;
 found   : scala.collection.immutable.Nil.type (with underlying type object Nil)
 required: java.lang.String
       ss += Nil
             ^

scala> ps += Nil
res3: ps.type = Set(List(), Dave)

You could also have fixed this by type ascripting sat ssdeclaration, or you could have declared ss's type to be Set[AnyRef].

您也可以通过sss声明时使用类型脚本来修复此问题,或者您可以将ss的类型声明为Set[AnyRef]

However, type declarations achieve the same thing only as long as you are assigning a value to an identifier. Which one can always do, of course, if one doesn't care about littering the code with one-shot identifiers. For example, the following does not compile:

但是,类型声明只有在为标识符分配值时才能实现相同的目的。当然,如果一个人不关心用一次性标识符来乱扔代码,那么哪个总是可以做到的。例如,以下不能编译:

def prefixesOf(s: String) = s.foldLeft(Nil) { 
  case (head :: tail, char) => (head + char) :: head :: tail
  case (lst, char) => char.toString :: lst
}

But this does:

但这确实:

def prefixesOf(s: String) = s.foldLeft(Nil: List[String]) { 
  case (head :: tail, char) => (head + char) :: head :: tail
  case (lst, char) => char.toString :: lst
}

It would be silly to use an identifier here in place of Nil. And though I could just write List[String]()instead, that isn't always an option. Consider this, for instance:

在这里使用标识符代替Nil. 尽管我可以List[String]()改为写作,但这并不总是一种选择。考虑一下,例如:

def firstVowel(s: String) = s.foldLeft(None: Option[Char]) { 
  case (None, char) => if ("aeiou" contains char.toLower) Some(char) else None
  case (vowel, _) => vowel
}

For the reference, this is what Scala 2.7 spec (march 15, 2009 draft) has to say about type ascription:

作为参考,这是 Scala 2.7 规范(2009 年 3 月 15 日草案)关于类型归属的说明:

Expr1 ::= ...
        | PostfixExpr Ascription

Ascription ::= ‘:' InfixType
             | ‘:' Annotation {Annotation}
             | ‘:' ‘_' ‘*'

回答by Kevin Wright

One possibility is when network and serial protocol level stuff, then this:

一种可能性是当网络和串行协议级别的东西时,那么这个:

val x = 2 : Byte

is far cleaner than

val x = 2.asInstanceOf[Byte]

The second form is also a runtime conversion (not handled by the compiler) and could lead to some interesting over/underflow conditions.

第二种形式也是运行时转换(不由编译器处理),可能会导致一些有趣的上溢/下溢情况。

回答by Justin W

You may find this threadilluminating, if a bit convoluted to follow. The important thing to note is that you're adding constraint hints to the type checker - it gives you a little more control over what that compilation phase is doing.

你可能会发现这个线程很有启发性,如果有点令人费解的话。需要注意的重要一点是,您正在向类型检查器添加约束提示 - 它使您可以更好地控制编译阶段正在执行的操作。

回答by pkinsky

I use type ascription to paper over holes in Scala's type inference. For example, foldLeft over a collection of type A takes an initial element of type B and a function (B, A) => B that is used to fold the elements of the collection into the initial element. The actual value of type B is inferred from the type of the initial element. Since Nil extends List[Nothing], using it as an initial element causes problems:

我使用类型归属来弥补 Scala 类型推断中的漏洞。例如, foldLeft 在类型 A 的集合上采用类型 B 的初始元素和一个函数 (B, A) => B 用于将集合的元素折叠到初始元素中。类型 B 的实际值是从初始元素的类型推断出来的。由于 Nil 扩展了 List[Nothing],将其用作初始元素会导致问题:

scala> val x = List(1,2,3,4)
x: List[Int] = List(1, 2, 3, 4)

scala> x.foldLeft(Nil)( (acc,elem) => elem::acc)
<console>:9: error: type mismatch;
 found   : List[Int]
 required: scala.collection.immutable.Nil.type
              x.foldLeft(Nil)( (acc,elem) => elem::acc)
                                                 ^

scala> x.foldLeft(Nil:List[Int])( (acc,elem) => elem::acc )
res2: List[Int] = List(4, 3, 2, 1)

Alternatively, you could just use List.empty[Int] instead of Nil:List[Int].

或者,您可以只使用 List.empty[Int] 而不是 Nil:List[Int]。

scala> x.foldLeft(List.empty[Int])( (acc,elem) => elem::acc )
res3: List[Int] = List(4, 3, 2, 1)

edit: List.empty[A] is implemented as

编辑:List.empty[A] 实现为

override def empty[A]: List[A] = Nil

(source)

(来源)

This is effectively a more verbose form of Nil:List[A]

这实际上是 Nil:List[A] 的更详细的形式

回答by Vikas Sharma

Type Inference: We can skip Explicitly giving Name of Type of Something in source code, called Type Inference.(Although required in some exceptional cases.)

类型推断:我们可以跳过源代码中明确给出某事物类型的名称,称为类型推断。(尽管在某些特殊情况下需要。)

Type Ascription: Being explicit about the type of something is called a Type Ascription. What Difference It can make?

类型描述:明确说明事物的类型称为类型描述。它可以带来什么不同?

ex: val x = 2 : Byte

例如:val x = 2:字节

also see: 1. We can explicitly give return type to our functions

另见: 1. 我们可以明确地为我们的函数提供返回类型

def t1 : Option[Option[String]] = Some(None)

> t1: Option[Option[String]]

Another way of declaring this could be:

另一种声明方式可能是:

def t2 = Some(None: Option[String])
> t2: Some[Option[String]]

Here we did not give Option[Option[String]]return type explicitly and Compiler inferred it as Some[Option[String]]. Why Some[Option[String]]is because we used type ascription in the definition.

这里我们没有Option[Option[String]]明确给出返回类型,编译器将其推断为Some[Option[String]]. 为什么Some[Option[String]]是因为我们在定义中使用了类型归属。

  1. Another way we can use the same definition is:

    def t3 = Some(None)

    > t3: Some[None.type]

  1. 我们可以使用相同定义的另一种方式是:

    def t3 = Some(None)

    > t3: Some[None.type]

This time We did not explicitly tell the compiler anything(neither this defi). And It inferred our definition as Some[None.type]

这次我们没有明确告诉编译器任何事情(也不是这个定义)。它推断我们的定义为 Some[None.type]