Misleading compile time error - scala

Why does the following compile?
scala> val ch1 = 'a' + 'b'
ch1: Int = 195
but the following doesn't?
scala> var ch1 = 'a'
ch1: Char = a
scala> ch1 += 'b'
<console>:9: error: type mismatch;
found : Int
required: Char
ch1 += 'b'
^
scala> ch1 = ch1 + 'b'
<console>:8: error: type mismatch;
found : Int
required: Char
ch1 = ch1 + 'b'
^
And why is the error message so misleading? Why does it say required: Char when what I am passing is clearly a Char?

When you add a Char and another Char, the result is an Int.
scala> 'a' + 'c'
res2: Int = 196
That's the "found" part of the error message.

This might help:
What is the concept of "weak conformance" in Scala?
http://lmazy.verrech.net/wp-content/uploads/2011/02/scala_type_hierarchy.png
Regards,
raichoo

I guess you have to help the compiler here if you annotate the ch1 as Int it works as expected?
The problem is I guess your intend is misread by the compiler :)
How should it know that you declare a Char to get it's int value to add another Int out if it? You are trying to change the type of a variable after assignment , how could , should that work? So start with var ch1:Int='a' and it works.

Related

Scala behaviour when assigning literals or variables to Char

In Scala, if I have a variable declaration, e.g.
var x: Char = 'a'
If I then try and update this character by adding 1, e.g.
x = x + 1
I get a compilation error: Type mismatch, found Int required Char. However, I can do this without a compilation error:
x = 'a' + 1
I'm guessing this has something to do with literal values vs objects, however, I'm trying to get my head around the exact behaviour. You can clearly assign a literal integer to a Char, e.g. 97, and you can also assign the result of 97-32. However if I say 97-32+5 then I get a type mismatch error. At what point does the compiler differentiate between an expression that results in a literal vs one that result in an object?
Assignment is the key here.
Look at the following REPL session:
alex#POSITRON ~ $ scala
Welcome to Scala version 2.11.6 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_131).
Type in expressions to have them evaluated.
Type :help for more information.
scala> val x:Char = 'a'
x: Char = a
scala> x + 1
res0: Int = 98
scala> var y:Char = 'a'
y: Char = a
scala> y + 1
res1: Int = 98
scala> y = y + 1
<console>:8: error: type mismatch;
found : Int
required: Char
y = y + 1
^
scala>
So as you can see unless you try to reassign the variable values everything goes fine. When you write 'a'+1 or x + 1 it gets converted to Int.
So when you finally try to x = x + 1 reassign then you are trying to assign Int value to the Char variable. This explain why compilation error occurs.
In the Char companion object there is implicit def char2int(x: Char): Int method.
I think in var x:Char = 'a' + 1 the first thing which happens is invocation of this method to convert 'a' to 97. Then 97 is added 1, both as Ints. Then the variable x gets instantiated in the same way as in val y:Char = 98. This I think explains how variable initialization works.
There's a special rule for typing literals in the spec.
However, if the expected type pt of a literal in an expression is
either Byte, Short, or Char and the integer number fits in the numeric
range defined by the type, then the number is converted to type pt and
the literal's type is pt.
That lets you write:
scala> 97 : Char
res0: Char = a
scala> 97 + 1 : Char
res1: Char = b
Sneakily, they mean constant expressions here. But the definition of constant expression is platform-specific, so technically res1 could also fail to compile, if it were not constant-folded.
A related question about constant folding in scaladoc shows the weird loss of type checking under scaladoc:
$ ~/scala-2.12.3/bin/scaladoc -d /tmp folded.scala
folded.scala:9: error: type mismatch;
found : Int
required: Char
x = 'a' - 32 + 5
^
model contains 4 documentable templates
one error found

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.

Why the result is not the same, f Interpolator in scala

val bb =0
val cc ="%07d"
println(f"$bb$cc") //0%07d
println(f"$bb%07d") //0000000
I expect
println(f"$bb$cc") //0000000
println(f"$bb%07d") //0000000
Why isn't the result the same?
How can I make it the same?
By using f"string" you are declaring that you want to make a formatted string.
with f"$bb$cc" you are using the variables bb and cc, so it accesses the strings for them
with f"$bb%07d" you are telling it to transform bb into a number with 7 decimal places, and 0s if the number isn't large enough
further examples to help understanding:
val bb = 1
println(f"$bb%07d") // 0000001
println(f"$bb%05d") // 00001
val bb = 11
println(f"$bb%05d") //00011
to get the same string, try using s"string"
val bb = 0
println(s"$bb%07d") // 0%07d
to use a string as a formatter:
val cc = "%07d"
val bb = 0
println(cc.format(bb)) //0000000
//for more information http://docs.scala-lang.org/overviews/core/string-interpolation.html
I think that you already know that, but just to be clear:
$ is used for marking of variable references
% is used for formatting the references
Generally you use f interpolator like this: f"$<reference>%<format>"
When you write:
println(f"$bb$cc")
This is what happens:
Parser recognizes $bb with no formatting.
Parser recognizes $cc with no formatting.
Substitutes $bb with 0
Substitutes $bb with "%07d"
But when you write:
println(f"$bb%07d")
This is what happens:
Parser recognizes $bb with formatting 07d
Substitutes $bb with 0 and formats it according to "07d", that is: 7 digits and filling with 0 is number is shorter than 7 digits.
You might have thought of this method as being preprocessing like #define cc "%07d" in C but it's simply not.
I'm not currently aware of a way to store the formatting in a separate string. You might consider using a class-based formatter for doing so.
String interpolation syntax requires a string literal.
The point of the f-interpolator macro is to give compile-time safety.
scala> val x = 0.1
x: Double = 0.1
scala> f"$x"
res0: String = 0.1
scala> f"$x%5.5f"
res1: String = 0.10000
scala> f"$x%5d"
<console>:13: error: type mismatch;
found : Double
required: Int
f"$x%5d"
^
scala> val fmt = "%5d"
fmt: String = %5d
scala> fmt format x
java.util.IllegalFormatConversionException: d != java.lang.Double
at java.util.Formatter$FormatSpecifier.failConversion(Formatter.java:4302)
at java.util.Formatter$FormatSpecifier.printInteger(Formatter.java:2793)
at java.util.Formatter$FormatSpecifier.print(Formatter.java:2747)
at java.util.Formatter.format(Formatter.java:2520)
at java.util.Formatter.format(Formatter.java:2455)
at java.lang.String.format(String.java:2940)
at scala.collection.immutable.StringLike$class.format(StringLike.scala:318)
at scala.collection.immutable.StringOps.format(StringOps.scala:29)
... 32 elided
This isn't convenient, but you can decompose the string constants and roll it by hand:
scala> final val fmt = "%5d"
fmt: String("%5d") = %5d
scala> new StringContext("", fmt).f(x)
<console>:14: error: type mismatch;
found : Double
required: Int
new StringContext("", fmt).f(x)
^
or
scala> final val fmt = "%5.5f"
fmt: String("%5.5f") = %5.5f
scala> new StringContext("", fmt).f(x)
res8: String = 0.10000
scala> final val fmt = "%5.5"
fmt: String("%5.5") = %5.5
scala> new StringContext("", fmt + "f").f(x)
res9: String = 0.10000
scala> new StringContext("", fmt + "d").f(x)
<console>:14: error: precision not allowed
new StringContext("", fmt + "d").f(x)
^
scala> final val fmt = "%5"
fmt: String("%5") = %5
scala> new StringContext("", fmt + "d").f(x)
<console>:14: error: type mismatch;
found : Double
required: Int
new StringContext("", fmt + "d").f(x)
^
The error if you try it with non-constant String:
scala> val fmt = "%5d"
fmt: String = %5d
scala> new StringContext("", fmt).f(x)
<console>:14: error: exception during macro expansion:
java.lang.IllegalArgumentException: internal error: argument parts must be a list of string literals
at scala.tools.reflect.FormatInterpolator.scala$tools$reflect$FormatInterpolator$$copyPart$1(FormatInterpolator.scala:82)
at scala.tools.reflect.FormatInterpolator.interpolated(FormatInterpolator.scala:181)
at scala.tools.reflect.FormatInterpolator.interpolate(FormatInterpolator.scala:38)
at scala.tools.reflect.FastTrack$$anonfun$1$$anonfun$apply$5$$anonfun$applyOrElse$5.apply(FastTrack.scala:54)
at scala.tools.reflect.FastTrack$$anonfun$1$$anonfun$apply$5$$anonfun$applyOrElse$5.apply(FastTrack.scala:54)
at scala.tools.reflect.FastTrack$FastTrackEntry.apply(FastTrack.scala:41)
at scala.tools.reflect.FastTrack$FastTrackEntry.apply(FastTrack.scala:36)
at scala.tools.nsc.typechecker.Macros$class.macroExpandWithRuntime(Macros.scala:763)
new StringContext("", fmt).f(x)
^

What is the most concise way to increment a variable of type Short in Scala?

I've been working a bit lately on implementing a binary network protocol in Scala. Many of the fields in the packets map naturally to Scala Shorts. I would like to concisely increment a Short variable (not a value). Ideally, I would like something like s += 1 (which works for Ints).
scala> var s = 0:Short
s: Short = 0
scala> s += 1
<console>:9: error: type mismatch;
found : Int
required: Short
s += 1
^
scala> s = s + 1
<console>:8: error: type mismatch;
found : Int
required: Short
s = s + 1
^
scala> s = (s + 1).toShort
s: Short = 1
scala> s = (s + 1.toShort)
<console>:8: error: type mismatch;
found : Int
required: Short
s = (s + 1.toShort)
^
scala> s = (s + 1.toShort).toShort
s: Short = 2
The += operator is not defined on Short, so there appears to be an implicit converting s to an Int preceding the addition. Furthermore Short's + operator returns an Int.
Here's how it works for Ints:
scala> var i = 0
i: Int = 0
scala> i += 1
scala> i
res2: Int = 1
For now I'll go with s = (s + 1).toShort
Any ideas?
You could define an implicit method that will convert the Int to a Short:
scala> var s: Short = 0
s: Short = 0
scala> implicit def toShort(x: Int): Short = x.toShort
toShort: (x: Int)Short
scala> s = s + 1
s: Short = 1
The compiler will use it to make the types match. Note though that implicits also have a shortfall, somewhere you could have a conversion happening without even knowing why, just because the method was imported in the scope, code readability suffers too.

+= on a Vector gives strange / wrong type errors

I have a variable v that is a Vector, and I'm trying to add an element to it using +=. It complains that it expects a String instead of an Int:
Welcome to Scala version 2.10.3 (Java HotSpot(TM) 64-Bit Server VM, Java 1.7.0_45).
Type in expressions to have them evaluated.
Type :help for more information.
scala> var v = Vector[Int]()
v: scala.collection.immutable.Vector[Int] = Vector()
scala> v += 3
<console>:9: error: type mismatch;
found : Int(3)
required: String
v += 3
^
Why does it expect a String? When I give it a String (which is ofcourse wrong), it says it expects a Vector[Int]:
scala> v += "three"
<console>:9: error: type mismatch;
found : String
required: scala.collection.immutable.Vector[Int]
v += "three"
^
And when I give it a Vector[Int], it again expects a String:
scala> v += Vector(3)
<console>:9: error: type mismatch;
found : scala.collection.immutable.Vector[Int]
required: String
v += Vector(3)
^
Why does this happen?
I know I can add an element using +:=. But why can I not use +=, like for a Set?
Let's go through this cases one by one:
scala> v += 3
<console>:9: error: type mismatch;
found : Int(3)
required: String
v += 3
^
Here is the main problem that Vector have no + method, so compiler will default to string concatination (which is highly criticized recently as a design flaw, by the way). The problem is that left side (vector) is convertible automatically to string (via Vector.toString), but right one is not.
scala> v += "three"
<console>:9: error: type mismatch;
found : String
required: scala.collection.immutable.Vector[Int]
v += "three"
^
Here concatenation is ok, but you're trying to put result of type String to variable of type Vector[Int], which is why compiler complains. But if you define v as Any compiler will stop complaining:
var v: Any = Vector[Int]()
v += "foo"
// res1: Any = Vector()foo
Now, next case
scala> v += Vector(3)
<console>:9: error: type mismatch;
found : scala.collection.immutable.Vector[Int]
required: String
v += Vector(3)
^
String concatenation again, and again, result of type String goes to the variable of type Vector.
Now, talking about why Vector does not have the very same + operation: ordinary Set have no notion of order, whereas Vector, and Seq in general have and + would be confusing: do I add to the end or to the start? So instead of implicit rule, you have to explicitly decide whether you use :+ or +:.