I recently started using scala and I can't make anything of the error messages. For the following code I get the stated message(using eclipse):
def helper: Int => List[Int] = x => x match {
case 2 => 2::1
...
}
I can fix it by using List(2,1), but souldn't that be the same thing as 2::1? I have similar problems where the List(...) approach would be harder to use, so I really want to know where my thinking mistake is.
Infix operators are interpreted as method calls in Scala. If the infix operators ends with a colon, it's a method call on the right operand with the left operand as its argument. Otherwise it's a method call on the left operand with the right operand as its argument.
In other words, if you do x + y
, it's the same as x.+(y)
, i.e. you're calling the method +
on the object x
, with y
as the argument. And if you do x :: y
it's the same as y.::(x)
, calling the method ::
on the object y
.
So in your example you're calling the method ::
on the object 1
, which is an Int
. However the class Int
does not have a ::
method, so this does not work and you get an error message telling you that the ::
method does not exist for the Int
class.
To make ::
work, the right operand needs to be a list (or something else that has a ::
method), so 2 :: 1 :: Nil
would work. However in this case using List()
seems like the cleaner alternative.
The expression 2::1
is interpreted in Scala as:
{ val x = 2; 1.::(x) }
because operators ending in a colon :
are right-associative and if op
is right-associative, then e1 op e2
is interpreted as { val x = e1; e2.op(x) }
(see Scala Language Reference, Section 6.12.3, p. 84, which is p. 92 of the PDF).
For the purposes here, basically the following simplified version is called
1.::(2)
However, 1
is of type Int
and Int
does not have a method with name ::
(and there is also no implicit conversion to another type that has such a method), hence the error.
As ayvango has pointed out above, you could use
2::1::Nil
which is interpreted as
Nil.::(2).::(1)
Now this works perfectly well, because Nil
is of type List[Nothing]
and does have a method ::
, see scala.collection.immutable.List Furthermore, ::
returns something of type List[Int]
, so the subsequent call .::(1)
is also fine.
Another way is
2::List(1)
which becomes List(1).::(2)
and works for the same reason as above.
Your confusion might be due to the fact that you consider List(2,1)
to be the same as 2::1
, however, it is actually 2::1::Nil
. Think of lists as being built inductively as follows:
Nil
is a listhead
is an element and tail
is a list, then head::tail
is a listas witnessed by the implementation of lists (simplified version, omitting traits)
sealed abstract class List[+A]
final case class ::[B](head: B, tl: List[B]) extends List[B]
object Nil extends List[Nothing]
Thus, lists always "end with" Nil
in the ::
form of presentation.
On a sidenote, you could also try to automatically wrap Int
into List[Int]
by using something like
implicit def wrap(x : Int) : List[Int] = List(x)
or use similar functionalities provided by libraries such as Scalaz but this might not always be desirable and probably is a bit beyond the scope of this question.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With