Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the purpose of type ascriptions in Scala?

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> 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
like image 693
davetron5000 Avatar asked Jan 18 '10 15:01

davetron5000


2 Answers

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 String is also an Object. In your example you declared you want the expression s to be treated as an Object, not a String. Since there is no constraints preventing that and the desired type is one of the types s is a, it works.

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 s at ss declaration, or you could have declared ss's type to be 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:

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:

Expr1 ::= ...
        | PostfixExpr Ascription

Ascription ::= ‘:’ InfixType
             | ‘:’ Annotation {Annotation}
             | ‘:’ ‘_’ ‘*’
like image 121
Daniel C. Sobral Avatar answered Oct 31 '22 04:10

Daniel C. Sobral


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.

like image 28
Kevin Wright Avatar answered Oct 31 '22 05:10

Kevin Wright