In scala if I have a declaration such as this:
val i: Float = 5
Is this the same like in this line:
val i = 5: Float
?
Recently I encountered an expression with such type annotation on the right side and I wonder what its usage is as I could not find it in the specs.
This is called type ascription and it's meant to disambiguate types when several options might be available.
A typical example is a fold:
val l = 1 to 5
val stringyfied = l.foldLeft(Nil)((list, elem) => elem.toString :: list)
What is the type of Nil in that case? If you try that example, the compile will bark with an error:
error: type mismatch;
found : List[String]
required: scala.collection.immutable.Nil.type
We can make the specific type known to the compiler by using type ascription:
val stringyfied = l.foldLeft(Nil:List[String])((list, elem) => elem.toString :: list)
// stringyfied: List[String] = List(5, 4, 3, 2, 1)
Another common use is to view a collection as a vararg as in:
def varargs(a:Int*) = a.sum
// common use:
varargs(1,2,3,4,5) //15
// now we have a collection of elements:
val intlist = 1 to 5
// we cannot call varagrs with a list
varargs(intlist)
> found : List[Int]
> required: Int
// then using type ascription:
varargs(intlist: _*)
To my knowledge, there's not much specific documentation about type ascription other than the specification of the algebra of expressions SLS#Chapter6
This syntax is used to assign types to expressions (while val i: Float = 5 assigns a type to a term). It can make a difference when you do it with a part of a bigger expression.
scala> implicit class WeirdFloat(f: Float) {
| def +(ff: Float) = new WeirdFloat(f + ff * 2)
| override def toString = s"$f"
| }
defined class WeirdFloat
scala> val i: WeirdFloat = 5 + 7
i: WeirdFloat = 12.0
scala> val i = (5: WeirdFloat) + 7
i: WeirdFloat = 19.0
I believe the only difference is that the type of i is inferred (by the compiler) from the value in the second example, whereas in the first you are being explicit about the type of i.
The stye guidelines encourage the use of type of inference wherever possible. However, it also mentions that the syntax whereby you follow the value with the type is the approach when you are using ascription - a compile time upcast.
scala> val s = "Jane"
s: String = Jane
scala> val p = s: Object
p: Object = Jane
Related
I mistakenly concatted a string with an Option[String] while coding in scala.
I expected as a strongly typed language, scala would not allow me to do such operation.
This is what I tried.
This works
scala> val a:String = "aaa"
val a: String = aaa
scala> val b:Option[String] = Some("bbbb")
val b: Option[String] = Some(bbbb)
scala> a + b
val res0: String = aaaSome(bbbb)
scala> val c:Option[String] = None
val c: Option[String] = None
scala> val d = a + c
val d: String = aaaNone
scala> val e = 1
val e: Int = 1
scala> a + e
val res2: String = aaa1
while this does not work
scala> val f:Option[String] = Some("ffff")
val f: Option[String] = Some(ffff)
scala> val g:Option[String] = None
val g: Option[String] = None
scala> f + g
^
error: type mismatch;
found : Option[String]
required: String
Why does scala allow such behavior? Dynamically typed languages like python will stop me from adding strings to int types, None types or any type other than strings. Curious if this design is intentional? If so why?
Scala contains an implicit class any2stringadd in it's Predef package. This class is responsible for these concatenation operations.
implicit final class any2stringadd[A](private val self: A) extends AnyVal {
def +(other: String): String = String.valueOf(self) + other
}
What it means is that, by default, scope contains a method + which can concatenate value of any type A with string by converting value of this type to string via String.valueOf(...).
I can't speak of design choices, I agree that this might be an unexpected behavior. The same applies to Scala's native == method. For example, this code compiles just ok: Some("a") == "b". This can lead to nasty bugs in filtering and other methods.
If you want to eliminate this behavior I suggest you take a look at https://typelevel.org/cats/ library, which introduces different typeclasses that can solve this problem.
For example, for string concatenation you can use Semigroup typeclass (which has tons of other useful use-cases as well):
import cats.Semigroup
Semigroup[String].combine("a", "b") // works, returns "ab"
Semigroup[String].combine("a", Some("b")) // won't work, compilation error
This looks tedious, but there is a syntactic sugar:
import cats.implicits._
"a" |+| "b" // works, returns "ab"
"a" |+| Some("b") // won't work, compilation error
// |+| here is the same as Semigroup[String].combine
The same thing applies to == method. Instead you can use Eq typeclass:
import cats.implicits._
"a" == Some("b") // works, no error, but could be unexpected
"a" === Some("b") // compilation error (Cats Eq)
"a" === "b" // works, as expected
"a" =!= "b" // same as != but type safe
I have a testing program in which we have in memory static arrays. I am using type aliases for brevity.
The following works in the REPL
type >[T] = Array[T]
val dat = >(>(1,2,3),>(2,3,4))
dat: Array[Array[Int]] = Array(Array(1, 2, 3), Array(2, 3, 4))
However changing the identifier to "A" from ">" does not work: the type is created but the same syntax for creating the array as used above fails:
scala> type A[T] = Array[T]
defined type alias A
scala> val dat = A(A(1,2,3),A(2,3,4))
<console>:7: error: not found: value A
val dat = A(A(1,2,3),A(2,3,4))
Also, NEITHER of the two above work within a Scala program AFAICT:
test("VectorProjection") {
type A[T] = Array[T]
// Next line shows RED for all the A's and also has compiler error: "not found: value A"
val dat = A(A(1., 2., 3.), A(1.5,2.,2.5), A(2.,3.8,5.6), A(2.5,3.0,3.5), A(3.1,3.7,4.3) )
val firsteigen = subtractProject(dat(0), dat(4))
}
Looking for:
1) For the REPL: An explanation of why the symbol ">" works but not
the identifier would be helpful.
2) For a real scala program/class:
An explanation of if it were possible to use any syntax similar to
the above
UPDATE Per suggestion by James Iry the following approach does work :
def A[T : ClassTag](ts: T*) = Array(ts:_*)
Here it is in action:
test("VectorProjection") {
def A[T : ClassTag](ts: T*) = Array(ts:_*)
val dat = A(
A(1., 2., 3.),
A(1.5,2.,2.5),
A(3.,6.,9.) )
val firstEigen = subtractProject(dat(0), dat(5))
println(s"firstEigen: ${firstEigen.mkString(",")}")
}
Another UPDATE Another answer hits closer to this OP:
Use type and val together:
type A = Array[Double]
val A = Array
Here it is in action:
test("VectorProjection") {
type A = Array[Double]
val A = Array
val dat = A(
A(1., 2., 3.),
A(1.5,2.,2.5),
A(3.,6.,9.) )
val firstEigen = subtractProject(dat(0), dat(5))
println(s"firstEigen: ${firstEigen.mkString(",")}")
}
I'm not able to replicate your success with '>'
scala> type >[T]=Array[T]
defined type alias $greater
scala> >(1,2,3)
<console>:8: error: not found: value >
>(1,2,3)
^
At least, not until I define it
scala> import scala.reflect._
import scala.reflect._
scala> def >[T : ClassTag](ts: T*) = Array(ts:_*)
$greater: [T](ts: T*)(implicit evidence$1: scala.reflect.ClassTag[T])Array[T]
scala> >(1,2,3)
res1: Array[Int] = Array(1, 2, 3)
Same thing works for A
scala> type A[T]=Array[T]
defined type alias A
scala> A(1,2,3)
<console>:11: error: not found: value A
A(1,2,3)
^
scala> def A[T : ClassTag](ts: T*) = Array(ts:_*)
A: [T](ts: T*)(implicit evidence$1: scala.reflect.ClassTag[T])Array[T]
scala> A(1,2,3)
res2: Array[Int] = Array(1, 2, 3)
For an explanation: type X = Y just creates a synonym for the type X. It doesn't bring in synonyms for everything else that might be associated with the type like companion objects, constructor methods, etc.
If you create a value alias it will work:
type A[T] = Array[T]
val A = Array
val dat = A(A(1,2,3),A(2,3,4)) //dat: Array[Array[Int]] = Array(Array(1, 2, 3), Array(2, 3, 4))
Line 2 creates a value alias, so you can create values with A type alias. It will in turn be able to call A.apply(1,2,3).
Use this to show what the repl knows:
scala> $intp.definedTerms
res0: List[$intp.global.TermName] = List($intp)
scala> $intp.definedTypes
res1: List[$intp.global.TypeName] = List($greater)
E.g., you might have:
scala> object X
defined object X
scala> trait X
defined trait X
warning: previously defined object X is not a companion to trait X.
Companions must be defined together; you may wish to use :paste mode for this.
scala> type X = String
defined type alias X
But it doesn't warn on aliasing.
Inspired by this, I was wondering if we can have type-safe string interpolations in Scala (maybe using macros)?
For example, I want to have something like this
def a[A] = ???
val greetFormat = f"Hi! My name is ${a[String]}. I am ${a[Int]} years old"
greetFormat.format("Rick", 27) // compiles
//greetFormat.format("Rick", false) // does not compile
//greetFormat.format(27, "Rick") // does not compile
//greetFormat.format("Rick", 27, false) // does not compile
//greetFormat.format("Rick") // does not compile or is curried?
The f string interpolator is already implemented with a macro.
This can be demonstrated inside of the REPL:
scala> val b = "not a number"
b: String = not a number
scala> f"$b%02d"
<console>:9: error: type mismatch;
found : String
required: Int
f"$b%02d"
^
Just wrap it in a function.
def greet(name: String, age: Int) = s"Hi! My name is $name. I am $age years old"
You can supply implicits to the f-interpolator:
scala> case class A(i: Int)
defined class A
scala> implicit def atoi(a: A): Int = a.i
warning: there were 1 feature warning(s); re-run with -feature for details
atoi: (a: A)Int
scala> f"${A(42)}%02d"
res5: String = 42
See also Travis Brown's examples and solution for using regex group names in extractions. It took me about a minute to steal that great idea.
"a123bc" match {
case res # xr"(?<c>a)(?<n>\d+)(?<s>bc)" => assert {
res.c == 'a' && res.n == 123 && res.s == "bc"
}
}
For the record, on the composition side, I would like:
val a = A(Rick, 42)
val greeter = f"Hi! My name is $_. I am ${_}%d years old"
greeter(a, a)
But it was deemed too much for the poor underscore. You'll have to write the function as in the other answer.
Your form, in which your macro sees "${a[Int]}" and writes a function with an Int param, doesn't look hard to implement.
Other features of the f-interpolator include other static error checking:
scala> f"$b%.02d"
<console>:19: error: precision not allowed
f"$b%.02d"
^
and support for Formattable:
scala> val ff = new Formattable { def formatTo(fmtr: Formatter, flags: Int, width: Int, precision: Int) = fmtr.format("%s","hello, world") }
ff: java.util.Formattable = $anon$1#d2e6b0b
scala> f"$ff"
res6: String = hello, world
A quick macro might emit (i: Int) => f"${ new Formattable {...} }".
When trying to define a generic method def f[T] (x:T) = x + 1 Scala gives below error
<console>:8: error: type mismatch;
found : Int(1)
required: String
def f[T] (x:T) = x + 1
^
The question is why Scala is assuming that it should be a String?
What if we want a function that can do +1 to Int, Char and String. I know I can do something like below, but it would work only on Int and Char.
def f[T <% Int](x:T) = x + 1
So what's the reason of this error and how to handle it in generic way.
The question is why Scala is assuming that it should be a String?
I can't guarantee this analysis, but it appears that Scala is applying the Predef.any2stringadd() implicit conversion to x, in an attempt to turn it into something that supports the + operator.
Here's a variant that will compile, and which demonstrates that implicit conversion:
scala> def f(x:Any) = { x + "1" }
f: (x: Any)String
scala> f("foo")
res3: String = foo1
scala> f(123)
res0: String = 1231
scala> f(classOf[String])
res2: String = class java.lang.String1
What if we want a function that can do +1 to Int, Char and String.
What does it mean to add 1 to any of these values?
If you simply want to invoke the + operator, then you need to use the match operator to select different behaviors depending on the actual type. This is because, while the name + is used for both, there's no common behavior between strings and numbers.
On the other hand, perhaps you want to deal with numbers that can be provided as either strings or numeric values (in which case, why Char?). To make that work, you need an implicit function that converts Any to a number.
scala> implicit def any2int(x:Any) : Int = { x.toString.toInt }
warning: there were 1 feature warning(s); re-run with -feature for details
any2int: (x: Any)Int
scala> def f(x:Any) : Int = { x + 1 }
f: (x: Any)Int
scala> f(123)
res0: Int = 124
scala> f("123")
res1: Int = 124
The return type of the function will always be Int as I assume so it should be that the compiler choose Int but why String.
I'm learning Scala as it fits my needs well but I am finding it hard to structure code elegantly. I'm in a situation where I have a List x and want to create two Lists: one containing all the elements of SomeClass and one containing all the elements that aren't of SomeClass.
val a = x collect {case y:SomeClass => y}
val b = x filterNot {_.isInstanceOf[SomeClass]}
Right now my code looks like that. However, it's not very efficient as it iterates x twice and the code somehow seems a bit hackish. Is there a better (more elegant) way of doing things?
It can be assumed that SomeClass has no subclasses.
EDITED
While using plain partition is possible, it loses the type information retained by collect in the question.
One could define a variant of the partition method that accepts a function returning a value of one of two types using Either:
import collection.mutable.ListBuffer
def partition[X,A,B](xs: List[X])(f: X=>Either[A,B]): (List[A],List[B]) = {
val as = new ListBuffer[A]
val bs = new ListBuffer[B]
for (x <- xs) {
f(x) match {
case Left(a) => as += a
case Right(b) => bs += b
}
}
(as.toList, bs.toList)
}
Then the types are retained:
scala> partition(List(1,"two", 3)) {
case i: Int => Left(i)
case x => Right(x)
}
res5: (List[Int], List[Any]) = (List(1, 3),List(two))
Of course the solution could be improved using builders and all the improved collection stuff :) .
For completeness my old answer using plain partition:
val (a,b) = x partition { _.isInstanceOf[SomeClass] }
For example:
scala> val x = List(1,2, "three")
x: List[Any] = List(1, 2, three)
scala> val (a,b) = x partition { _.isInstanceOf[Int] }
a: List[Any] = List(1, 2)
b: List[Any] = List(three)
Just wanted to expand on mkneissl's answer with a "more generic" version that should work on many different collections in the library:
scala> import collection._
import collection._
scala> import generic.CanBuildFrom
import generic.CanBuildFrom
scala> def partition[X,A,B,CC[X] <: Traversable[X], To, To2](xs : CC[X])(f : X => Either[A,B])(
| implicit cbf1 : CanBuildFrom[CC[X],A,To], cbf2 : CanBuildFrom[CC[X],B,To2]) : (To, To2) = {
| val left = cbf1()
| val right = cbf2()
| xs.foreach(f(_).fold(left +=, right +=))
| (left.result(), right.result())
| }
partition: [X,A,B,CC[X] <: Traversable[X],To,To2](xs: CC[X])(f: (X) => Either[A,B])(implicit cbf1: scala.collection.generic.CanBuildFrom[CC[X],A,To],implicit cbf2: scala.collection.generic.CanBuildFrom[CC[X],B,To2])(To, To2)
scala> partition(List(1,"two", 3)) {
| case i: Int => Left(i)
| case x => Right(x)
| }
res5: (List[Int], List[Any]) = (List(1, 3),List(two))
scala> partition(Vector(1,"two", 3)) {
| case i: Int => Left(i)
| case x => Right(x)
| }
res6: (scala.collection.immutable.Vector[Int], scala.collection.immutable.Vector[Any]) = (Vector(1, 3),Vector(two))
Just one note: The partition method is similar, but we need to capture a few types:
X -> The original type for items in the collection.
A -> The type of items in the left partition
B -> The type of items in the right partition
CC -> The "specific" type of the collection (Vector, List, Seq etc.) This must be higher-kinded. We could probably work around some type-inference issues (see Adrian's response here: http://suereth.blogspot.com/2010/06/preserving-types-and-differing-subclass.html ), but I was feeling lazy ;)
To -> The complete type of collection on the left hand side
To2 -> The complete type of the collection on the right hand side
Finally, the funny "CanBuildFrom" implicit paramters are what allow us to construct specific types, like List or Vector, generically. They are built into to all the core library collections.
Ironically, the entire reason for the CanBuildFrom magic is to handle BitSets correctly. Because I require CC to be higher kinded, we get this fun error message when using partition:
scala> partition(BitSet(1,2, 3)) {
| case i if i % 2 == 0 => Left(i)
| case i if i % 2 == 1 => Right("ODD")
| }
<console>:11: error: type mismatch;
found : scala.collection.BitSet
required: ?CC[ ?X ]
Note that implicit conversions are not applicable because they are ambiguous:
both method any2ArrowAssoc in object Predef of type [A](x: A)ArrowAssoc[A]
and method any2Ensuring in object Predef of type [A](x: A)Ensuring[A]
are possible conversion functions from scala.collection.BitSet to ?CC[ ?X ]
partition(BitSet(1,2, 3)) {
I'm leaving this open for someone to fix if needed! I'll see if I can give you a solution that works with BitSet after some more play.
Use list.partition:
scala> val l = List(1, 2, 3)
l: List[Int] = List(1, 2, 3)
scala> val (even, odd) = l partition { _ % 2 == 0 }
even: List[Int] = List(2)
odd: List[Int] = List(1, 3)
EDIT
For partitioning by type, use this method:
def partitionByType[X, A <: X](list: List[X], typ: Class[A]):
Pair[List[A], List[X]] = {
val as = new ListBuffer[A]
val notAs = new ListBuffer[X]
list foreach {x =>
if (typ.isAssignableFrom(x.asInstanceOf[AnyRef].getClass)) {
as += typ cast x
} else {
notAs += x
}
}
(as.toList, notAs.toList)
}
Usage:
scala> val (a, b) = partitionByType(List(1, 2, "three"), classOf[java.lang.Integer])
a: List[java.lang.Integer] = List(1, 2)
b: List[Any] = List(three)
If the list only contains subclasses of AnyRef, becaus of the method getClass. You can do this:
scala> case class Person(name: String)
defined class Person
scala> case class Pet(name: String)
defined class Pet
scala> val l: List[AnyRef] = List(Person("Walt"), Pet("Donald"), Person("Disney"), Pet("Mickey"))
l: List[AnyRef] = List(Person(Walt), Pet(Donald), Person(Disney), Pet(Mickey))
scala> val groupedByClass = l.groupBy(e => e.getClass)
groupedByClass: scala.collection.immutable.Map[java.lang.Class[_],List[AnyRef]] = Map((class Person,List(Person(Walt), Person(Disney))), (class Pet,List(Pet(Donald), Pet(Mickey))))
scala> groupedByClass(classOf[Pet])(0).asInstanceOf[Pet]
res19: Pet = Pet(Donald)
Starting in Scala 2.13, most collections are now provided with a partitionMap method which partitions elements based on a function returning either Right or Left.
That allows us to pattern match a given type (here Person) that we transform as a Right in order to place it in the right List of the resulting partition tuple. And other types can be transformed as Lefts to be partitioned in the left part:
// case class Person(name: String)
// case class Pet(name: String)
val (pets, persons) =
List(Person("Walt"), Pet("Donald"), Person("Disney")).partitionMap {
case person: Person => Right(person)
case pet: Pet => Left(pet)
}
// persons: List[Person] = List(Person(Walt), Person(Disney))
// pets: List[Pet] = List(Pet(Donald))