What does dot colon colon (.::) mean in Scala? - scala

Below code adds element to res list. My question is how scala internally translates .:: symbols?
Code snippet:
var res = List[(Int, Int)]()
res .::= (1, 2)
res .::= (3, 4)
res
output:
res56: List[(Int, Int)] = List((1,2),(3,4))

There are a few things going on in that snippet. Before diving into it let's talk about the difference between var and val. Namely, that a variable declared using the val keyword is immutable, i.e. its value cannot be changed:
scala> val x = 1
x: Int = 1
scala> x = 2
<console>:13: error: reassignment to val
x = 2
^
On the other hand, var keyword is used to declare a mutable variable, i.e. its value can be changed:
scala> var y = "bar"
y: String = bar
scala> y = "foo"
y: String = foo
What if we wanted to compute a new value of y by appending to its current value?
scala> y = y + "bar"
y: String = foobar
Sure that works, but it turns out there's a shorthand for doing that:
scala> y += "bar"
scala> y
res10: String = foobar
By the way, in Scala, + is just a name of a method, so y + "bar" is the same as y.+("bar"). Ugly, but valid. Similarly, y.+=("bar") is also a valid replacement of y += "bar".
Great, let's remember that for later. Next, as others have already pointed out, :: is just a method for prepending elements to a list (from Java it can be invoked as someList.$colon$colon(someElement)). The important thing to note is that the :: method returns a new list:
scala> var letters = List("b", "c")
letters: List[String] = List(b, c)
scala> letters.::("a")
res1: List[String] = List(a, b, c)
scala> letters
res2: List[String] = List(b, c)
What if we wanted to set letters to the list which contains the letter "a"?
scala> letters = letters.::("a")
letters: List[String] = List(a, b, c)
Notice that this looks awfully similar to the previous example with strings. Does the shorthand work here too?
scala> letters ::= "a"
scala> letters
res6: List[String] = List(a, b, c)
Yes, it does. letters.::=("a") works as well.
Now, let's break down the original snippet:
Step 1
Create a variable named res and assign it an empty, immutable list. This empty list is intended to contain pairs of integers (Int, Int).
var res = List[(Int, Int)]()
Here's an alternative way of doing the same thing:
var res = List.empty[(Int, Int)]
(which, in my opinion, is a bit easier to read)
Step 2
Prepend a new element (1, 2) to list res and reassign the resulting list back to res.
res .::= (1, 2)
Or, without spaces:
res.::=(1, 2)
Looks familiar? We could've also written it out as:
res = res.::(1, 2)
Step 3
Prepend (3, 4) following the logic in step 2
Step 4
Print out current value of res, which should be: List((3,4), (1,2))
Side note
Confusingly, the compiler is lenient enough to allow us to specify only a single set of parentheses when calling ::, though we really ought to have two sets: one for the method invocation and another one for indicating a pair of integers. So, there happens to be yet another valid
way of writing the same thing res.::=((1, 2)).
More generally:
scala> def first(p:(Int, Int)):Int = p._1
first: (p: (Int, Int))Int
scala> first(6,7)
res0: Int = 6
scala> first((6,7))
res1: Int = 6
scala> first(6 -> 7) //lolz! another one using implicit conversion
res2: Int = 6
The implicit conversion is ever-present since it's defined in Predef.ArrowAssoc
mind = blown
I also recommend taking a look at What are all the instances of syntactic sugar in Scala?

Just method invocation
. (dot) used for method invocation on instance of a class.
:: is a method defined on the List
:: is a method declared in the List class which creates instance of scala.collection.immutable.:: class.
Notice that :: is a method in List class and also :: is a final class in the package scala.collection.immutable
Scala Standard library
Here is the implementation of the :: function in the List class
#SerialVersionUID(-6084104484083858598L) // value computed by serialver for 2.11.2, annotation added in 2.11.4
sealed abstract class List[+A] extends AbstractSeq[A]
with LinearSeq[A]
with Product
with GenericTraversableTemplate[A, List]
with LinearSeqOptimized[A, List[A]]
with Serializable {
override def companion: GenericCompanion[List] = List
import scala.collection.{Iterable, Traversable, Seq, IndexedSeq}
def isEmpty: Boolean
def head: A
def tail: List[A]
// New methods in List
/** Adds an element at the beginning of this list.
* #param x the element to prepend.
* #return a list which contains `x` as first element and
* which continues with this list.
*
* #usecase def ::(x: A): List[A]
* #inheritdoc
*
* Example:
* {{{1 :: List(2, 3) = List(2, 3).::(1) = List(1, 2, 3)}}}
*/
def ::[B >: A] (x: B): List[B] =
new scala.collection.immutable.::(x, this)
.....
}
Here is how scala.collection.immutable.:: is defined.
#SerialVersionUID(509929039250432923L) // value computed by serialver for 2.11.2, annotation added in 2.11.4
final case class ::[B](override val head: B, private[scala] var tl: List[B]) extends List[B] {
override def tail : List[B] = tl
override def isEmpty: Boolean = false
}

The output of
var res = List[(Int, Int)]()
res .::= (1, 2)
res .::= (3, 4)
res
should be
List((3,4), (1,2))
because the colon colon method :: adds an element to the front of a list.
The dot . is totally optional in this case - this is just for specifically stating, that you are calling method :: on the list object res. This means that your code is equivalent to this one:
var res = List[(Int, Int)]()
res ::= (1, 2)
res ::= (3, 4)
res
Internally colon colon :: is implemented like this:
/** Adds an element at the beginning of this list.
* #param x the element to prepend.
* #return a list which contains `x` as first element and
* which continues with this list.
*
* #usecase def ::(x: A): List[A]
* #inheritdoc
*
* Example:
* {{{1 :: List(2, 3) = List(2, 3).::(1) = List(1, 2, 3)}}}
*/
def ::[B >: A] (x: B): List[B] =
new scala.collection.immutable.::(x, this)
A new list is created (because of the immutability) with the argument as a first element and the current list content as the rest

Related

Return a sequence with dynamic type in scala

I'm trying to define a function, which should return a range from 1 to n of type T. Here, T is given as user-input and an implicit def method is available to convert int to T. I.e., what I would like to have, but doesn't work, is:
def createSeq[T](n: Int): Seq[T] =
1 to n map _.asInstanceOf[T]
For example, I can call createSeq[Int](3), which works like a charm. However, when I call createSeq[String](3), it still returns Vector(1,2,3), which contains integers and not strings.
Let's define createSeq, asking for an implicit conversion Int => T from Int to T, as:
def createSeq[T](n: Int)(implicit toT: Int => T): Seq[T] =
1.to(n).map(toT)
Then you can do:
scala> createSeq[Int](3)
res: Seq[Int] = Vector(1, 2, 3)
Let's add an implicit conversion Int => String:
implicit def toStr(n: Int): String = n.toString
Then you can do:
scala> createSeq[String](3)
res: Seq[String] = Vector(1, 2, 3)
Note: this last Vector(1, 2, 3) is of type Seq[String], it is actually Vector("1", "2", "3") but " are omitted.

Extractors in for vs extractors in assignment

I have this doubt with extractors.
If I can do this:
val a :: b = List(1, 2, 3)
why I cannot do this:
val c = for ( a :: b <- List(1, 2, 3) } yield a
The translation of a for-yield expression in Scala (without using guards) is the map function. If you try and use map instead, it'll seem a little clear as to the quirk with the code:
List(1, 2, 3).map((x: Int) => ???)
When you map over a List[+A], you project each value, one at a time. You don't have the entire list at your disposable inside the higher order function.
On the contrary, when using pattern matching on the list itself, the compiler will translate your first example into (after some cleanup):
def main(args: Array[String]): Unit = {
private[this] val x$1: (Int, List[Int]) = List(1, 2, 3) match {
case (head: Int, tl: List[Int])scala.collection.immutable.::[Int]((a # _), (b # _)) => Tuple2[Int, List[Int]](a, b)
};
val a: Int = x$1._1;
val b: List[Int] = x$1._2;
()
Which is a pattern match on the List[Int], matching the case of a head and tail. It's simply syntatic sugar for regular pattern matching, same as for-yield is syntatic sugar for a map. They simply do different things.
After Yuval answer, I've understood why it doesn't work.
Equivalent syntax if interested in 1st value:
val first = for (a <- List(1, 2, 3).headOption) yield a

Scala partition a set

I was looking at how to split a set in two based on the contents of a third set. Accidentally I stumbled upon this solution:
val s = Set(1,2,3)
val s2 = Set(4,5,6)
val s3 = s ++ s2
s3.partition(s)
res0: (scala.collection.immutable.Set[Int],scala.collection.immutable.Set[Int]) = (Set(1, 2, 3),Set(5, 6, 4))
The signature of partition is as follows:
def partition(p: A => Boolean): (Repr, Repr)
Can someone explain to me how providing a set instead of a function works?
Thanks in advance
A set s: Set[A] is a function A => Boolean: for any value a of A you return whether s contains a or not.
scala> val f: Int => Boolean = Set(1,2,3)
f: Int => Boolean = Set(1, 2, 3)
scala> f(1)
res0: Boolean = true
scala> f(4)
res1: Boolean = false
If you look a the documentation for .apply, you'll see
def apply(elem: A): Boolean
Tests if some element is contained in this set.
This method is equivalent to contains. It allows sets to be
interpreted as predicates.

Passing a individual arguments AND a Seq to a var-arg function

I know it's possible to pass individual arguments to a vararg function and it's possible to pass a seq using :_* but is it possible to pass both?
for example:
scala> object X { def y(s: String*) = println(s) }
defined module X
scala> X.y("a", "b", "c")
WrappedArray(a, b, c)
scala> X.y(Seq("a", "b", "c"):_*)
List(a, b, c)
scala> X.y("a", Seq("b", "c"):_*)
<console>:9: error: no `: _*' annotation allowed here
(such annotations are only allowed in arguments to *-parameters)
X.y("a", Seq("b", "c"):_*)
^
Edit: In Scala 2.10 (in case that matters)
Hacky but this should work well:
X.y(Seq("a") ++ Seq("b", "c"):_*)
If you look around in the Scala standard library you'll find this sort of pattern in places:
def doIt(arg: Thing)
def doIt(arg1: Thing, arg2: Thing, moreArgs: Thing*)
You can see this, e.g., in Set.+(...). It allows you to have any number of arguments without ambiguity in the overloads.
Addendum
Proof of concept:
scala> class DI { def doIt(i: Int) = 1; def doIt(i1: Int, i2: Int, iMore: Int*) = 2 + iMore.length }
defined class DI
scala> val di1 = new DI
di1: DI = DI#16ac0be1
scala> di1.doIt(0)
res1: Int = 1
scala> di1.doIt(1, 2)
res2: Int = 2
scala> di1.doIt(1, 2, 3)
res3: Int = 3
scala> di1.doIt(1, 2, List(3, 4, 5): _*)
res4: Int = 5

How to read the class of a Scala object extending Any but not AnyRef?

I have an heterogeneous List like the following one:
val l = List(1, "One", true)
and I need to filter its objects by extracting only the ones belonging to a given Class. For this purpose I wrote a very simple method like this:
def filterByClass[A](l: List[_], c: Class[A]) =
l filter (_.asInstanceOf[AnyRef].getClass() == c)
Note that I am obliged to add the explicit conversion to AnyRef in order to avoid this compilation problem:
error: type mismatch;
found : _$1 where type _$1
required: ?{val getClass(): ?}
Note that implicit conversions are not applicable because they are ambiguous:
both method any2stringadd in object Predef of type (x: Any)scala.runtime.StringAdd
and method any2ArrowAssoc in object Predef of type [A](x: A)ArrowAssoc[A]
are possible conversion functions from _$1 to ?{val getClass(): ?}
l filter (_.getClass() == c)
However in this way the invocation of:
filterByClass(l, classOf[String])
returns as expected:
List(One)
but of course the same doesn't work, for example, with Int since they extends Any but not AnyRef, so by invoking:
filterByClass(l, classOf[Int])
the result is just the empty List.
Is there a way to make my filterByClass method working even with Int, Boolean and all the other classes extending Any?
The collect method already does what you want. For example to collect all Ints in a collection you could write
xs collect { case x: Int => x }
This of course only works when you hardcode the type but as primitives are handled differently from reference types it is actually better to do so. You can make your life easier with some type classes:
case class Collect[A](collect: PartialFunction[Any,A])
object Collect {
implicit val collectInt: Collect[Int] = Collect[Int]({case x: Int => x})
// repeat for other primitives
// for types that extend AnyRef
implicit def collectAnyRef[A <: AnyRef](implicit mf: ClassManifest[A]) =
Collect[A]({ case x if mf.erasure.isInstance(x) => x.asInstanceOf[A] })
}
def collectInstance[A : Collect](xs: List[_ >: A]) =
xs.collect(implicitly[Collect[A]].collect)
Then you can use it without even passing a Class[A] instance:
scala> collectInstance[Int](l)
res5: List[Int] = List(1)
scala> collectInstance[String](l)
res6: List[String] = List(One)
Using isInstanceOf:
scala> val l = List(1, "One", 2)
l: List[Any] = List(1, One, 2)
scala> l . filter(_.isInstanceOf[String])
res1: List[Any] = List(One)
scala> l . filter(_.isInstanceOf[Int])
res2: List[Any] = List(1, 2)
edit:
As the OP requested, here's another version that moves the check in a method. I Couldn't find a way to use isInstanceOf and so I changed the implementation to use a ClassManifest:
def filterByClass[A](l: List[_])(implicit mf: ClassManifest[A]) =
l.filter(mf.erasure.isInstance(_))
Some usage scenarios:
scala> filterByClass[String](l)
res5: List[Any] = List(One)
scala> filterByClass[java.lang.Integer](l)
res6: List[Any] = List(1, 2)
scala> filterByClass[Int](l)
res7: List[Any] = List()
As can be seen above, this solution doesn't work with Scala's Int type.
The class of an element in a List[Any] is never classOf[Int], so this is behaving as expected. Your assumptions apparently leave this unexpected, but it's hard to give you a better way because the right way is "don't do that."
What do you think can be said about the classes of the members of a heterogenous list? Maybe this is illustrative. I'm curious how you think java does it better.
scala> def f[T: Manifest](xs: List[T]) = println(manifest[T] + ", " + manifest[T].erasure)
f: [T](xs: List[T])(implicit evidence$1: Manifest[T])Unit
scala> f(List(1))
Int, int
scala> f(List(1, true))
AnyVal, class java.lang.Object
scala> f(List(1, "One", true))
Any, class java.lang.Object
This worked for me. Is this what you want?
scala> val l = List(1, "One", true)
l: List[Any] = List(1, One, true)
scala> l filter { case x: String => true; case _ => false }
res0: List[Any] = List(One)
scala> l filter { case x: Int => true; case _ => false }
res1: List[Any] = List(1)
scala> l filter { case x: Boolean => true; case _ => false }
res2: List[Any] = List(true)
Despite my solution could be less elegant than this one I find mine quicker and easier. I just defined a method like this:
private def normalizeClass(c: Class[_]): Class[_] =
if (classOf[AnyRef].isAssignableFrom((c))) c
else if (c == classOf[Int]) classOf[java.lang.Integer]
// Add all other primitive types
else classOf[java.lang.Boolean]
So by using it in my former filterByClass method as it follows:
def filterByClass[A](l: List[_], c: Class[A]) =
l filter (normalizeClass(c).isInstance(_))
the invocation of:
filterByClass(List(1, "One", false), classOf[Int])
just returns
List(1)
as expected.
At the end, this problem reduces to find a map between a primitive and the corresponding boxed type.
Maybe a help can arrive from scala.reflect.Invocation (not included in the final version of 2.8.0), the getAnyValClass function in particular (here slightly edited)
def getAnyValClass(x: Any): java.lang.Class[_] = x match {
case _: Byte => classOf[Byte]
case _: Short => classOf[Short]
case _: Int => classOf[Int]
case _: Long => classOf[Long]
case _: Float => classOf[Float]
case _: Double => classOf[Double]
case _: Char => classOf[Char]
case _: Boolean => classOf[Boolean]
case _: Unit => classOf[Unit]
case x#_ => x.asInstanceOf[AnyRef].getClass
}
With this function the filter is as easy as
def filterByClass[T: Manifest](l:List[Any]) = {
l filter (getAnyValClass(_) == manifest[T].erasure)
}
and the invocation is:
filterByClass[Int](List(1,"one",true))