Why does "a".::(List()) report error ":: is not member of string"? - scala

See this:
scala> 1 + 1
res0: Int = 2
scala> 1.+(1)
warning: there were 1 deprecation warning(s); re-run with -deprecation for details
res1: Double = 2.0
scala> "a" :: List()
res2: List[String] = List(a)
scala> "a".::(List())
<console>:8: error: value :: is not a member of String
"a".::(List())
^
Why does the error occur?

Try this
List().::("a")
The reason is that :: is a method of List.
From ScalaByExample:
Like any infix operator, :: is also implemented as a method of an
object. In this case, the object is the list that is extended. This is
possible, because operators ending with a ‘:’ character are treated
specially in Scala. All such operators are treated as methods of their
right operand. E.g.,
x :: y = y.::(x) whereas x + y = x.+(y)
Note, however, that operands of a binary operation are in each case
evaluated from left to right. So, if D and E are expressions with
possible side-effects,
D :: E
is translated to
{val x = D; E.::(x)}
in order to maintain the left-to-right order of operand evaluation.

In scala methods which ends with : got applied in reverse order.
So when you write a::list it is actually list.::(a). String doesn't have :: method, so the solution is to write List().::("a") or Nil.::("a")

Because of operator precedence. In Scala methods which ends with : are right associative. So you should call List().::("a")
If you want to use left associative method then you should write List("a") ++ List(), but that's not always a good choice, cause it has linear execution time

Related

Type mismatch when using + operator

I'm currently trying to learn how to use Scala but I'm stuck with some syntax problems.
When I type in the scala prompt:
import scala.collection.immutable._
var q = Queue[Int](1)
println((q+1).toString)
I get the following error:
<console>:12: error: type mismatch;
found : Int(1)
required: String
println((q+1).toString)
I just wanted to use the overloaded + operator of the queue defined as below:
def +[B >: A](elem : B) : Queue[B]
Creates a new queue with element added at the end of the old queue.
Parameters
elem - the element to insert
But it seems that scala does a string concatenation. So, can you help me to understand how to add an element to the queue (without using enqueue which works perfectly; I would like to use the + operator)? And maybe, could you give me some explaination about that behaviour that seems a bit strange for a beginner?
Thank you
You are using the wrong operator (see docs):
scala> var q = Queue[Int](1)
q: scala.collection.immutable.Queue[Int] = Queue(1)
scala> q :+ 2
res1: scala.collection.immutable.Queue[Int] = Queue(1, 2)
scala> 0 +: q
res2: scala.collection.immutable.Queue[Int] = Queue(0, 1)
Since the + operator has no other meaning given those types, Scala defaults to String concatenation, giving you the type mismatch error.

scala - How does method :: works in List?

I notice that List class define the method ::, which adds an element at the beginning of the list
def ::(x: A): List[A]
Example:
1 :: List(2, 3) = List(2, 3).::(1) = List(1, 2, 3)
However, I am confused at How does scala compiler recognize such conversion? Because as far as I am concerned,
1 :: List(2,3)
should raise an error: :: is not a member of Int
Do I miss something about operator definition of scala?
Methods whose names end with : are right-associative when called using infix operator notation. I.e.
a foo_: b
is the same as
b.foo_:(a)
This rule exists specifically for the case of methods like this, which are commonly (in other languages such as Haskell and ML) operators like : or ::.

How can I find the definition of methods/operations defined in an implicitly-converted class?

I'm looking at someone else's source code (Scala), where I see the operator :+= being called on a variable of type IndexedSeq. I am looking all over the scaladocs page for that class to figure out what that operator does, but I do not see it. I'm thinking that either it's defined in a class outside of IndexedSeq's inheritance hierarchy, or else the javascript on the scaladocs page is hiding it somewhere I can't see it. (Actually it's neither; see answer below.)
I've hit every button on the scaladocs page trying to unhide everything. I've looked in the web-page's HTML code. There has got to be a way to look up an operator from the documentation of a class to which it can be applied. Hasn't there?
(N.B.: I looked up that operator using symbolhound, so I know what that operator means now. This question is about scala documentation in general, not that particular operator.)
All operators in Scala are normal methods.
You cannot find it because it is compiler magic for re-assignement, it is not an operator. Or to say it another way: it looks like an operator of its own, but it is actually "an operator followed by the = character".
The compiler will magically turn that into a assignment if the operator (here :+) returns the proper type, and the original value was a var, obviously.
Since it is not provided by any implicit nor explicit method on Seq[T] or whatever, it does not appear anywhere in the generated scaladoc.
So to answer the general question:
It is a language construct, so the only place where it is documented is the specification, sadly,
but, if you find some "<?>=" unknown operator somewhere, look for the definition of "<?>", that one is sure to be documented.
Edit: I finally found where this is defined in the SLS:
§6.12.4:
An assignment operator is an operator symbol (syntax category op in (§1.1)) that
ends in an equals character “=”, with the exception of operators for which one of the
following conditions holds:
(1) the operator also starts with an equals character, or
(2) the operator is one of (<=), (>=), (!=).
It also says later on that it only happens when all other options have been tried (including potential implicits).
Is this value assigned to a variable? If it's the case I think this syntax sugar:
scala> var x = IndexedSeq(1,2,3)
x: IndexedSeq[Int] = Vector(1, 2, 3)
scala> x :+= 10
scala> x
res59: IndexedSeq[Int] = Vector(1, 2, 3, 10)
scala> val y = IndexedSeq(1,2,3)
y: IndexedSeq[Int] = Vector(1, 2, 3)
scala> y :+= 10
<console>:16: error: value :+= is not a member of IndexedSeq[Int]
y :+= 10
^
It is syntax sugar for "operation and assignment", like +=:
scala> var x = 10
x: Int = 10
scala> x += 1
scala> x
res63: Int = 11
Which de-sugars to x = x + 1.

Why can I not use :: after a list to append a string to it in Scala?

So I assume my issue has to do with what is going on under the hood, but I don't understand why this doesn't works:
scala> b = b :: "apple";
<console>:8: error: value :: is not a member of java.lang.String
but this does:
scala> b = "apple" :: b;
b: List[java.lang.String] = List(apple, pear)
Thanks.
Method names that end in : are right associative, so b :: "apple" tries to call the :: method on a String, which doesn't exist.
The normal strategy for appending things if you must use a List is to add things to the beginning then reverse the result when you're done. But as Rex says, using a Vector might be better.
:: always joins a new item to the head of a list. Adding an item to the end can be done, but it takes time proportional to the length of the list (since the entire list must not only be traversed but, actually, also rebuilt).
If you really must add a item to the end of a list, use :+:
List("pear","orange") :+ "apple"
Better yet, use Vector when you need to add to the end (it's much faster at double-ended additions):
Vector("grape","peach") :+ "apple"
Another possibility is to explicitly add Nil to the end of chain:
scala> val a = "apple"
a: java.lang.String = apple
scala> val b = "pear"
b: java.lang.String = pear
scala> a::b::Nil
res0: List[java.lang.String] = List(apple, pear)

Scala: oddness adding to a Map

I have just noticed that whilst I need clarifying parens when adding a pair to a map, I don't need them when doing a re-assignment:
Welcome to Scala version 2.9.1.final (Java HotSpot(TM) Server VM, Java 1.6.0_18).
Type in expressions to have them evaluated.
Type :help for more information.
Get me some values
scala> var n = Map.empty[Int, String]
n: scala.collection.immutable.Map[Int,String] = Map()
Trying to add to the map with no clarifying parentheses:
scala> n + 1 -> ""
<console>:30: error: type mismatch;
found : Int(1)
required: (Int, ?)
n + 1 -> ""
^
Fails as I expected it to. But doing the same via a re-assignment does not:
scala> n += 1 -> ""
scala> n
res12: scala.collection.immutable.Map[Int,String] = Map(1 -> "")
What is going on? Why is this not failing? Is scalac adding parens itself?
It's just a precedence issue. + and - have the same precedence. = is lower. So Scala sees (n + 1) -> "" in the former case, and n += (1 -> "") in the latter (which is then transformed to n = (n + (1 -> "")) according to the normal rules for assignment operators.
According to scala reference (6.12.4):
Assignment operators are treated specially in that they can be expanded to assignments if no other interpretation is valid. ... The
re-interpretation occurs if the following two conditions are fulfilled.
1. The left-hand-side l does not have a member named +=, and also cannot be converted by an implicit conversion (§6.26) to a value with a member named > +=.
2. The assignment l = l + r is type-correct. In particular this implies that l refers to a variable or object that can be assigned to, and that is convertible to a value with a member named +.
(1) Immutable Map has no member named += (mutable has) and AFAIK, has no implicit conversions to something with that and assignment is definitely type correct (2): n defined as a variable and has member +.