why 'n = 5 is the valid syntax?[Scala] - scala

object solution extends App {
'n = 5
}
It gives the compile time Error: value update is not a member of object Symbol
println('n = 'n) which is understandable. Because literals are the fixed values in the source code. But what is the reason the above syntax is valid?

The reason the syntax is valid is … well … because it is:
implicit class UpdateableSymbol(val s: Symbol.type) extends AnyVal {
def update[A](s: String, v: A) = println(s"`Symbol.update` called with s = $s and v = $v")
}
'n = 5
// `Symbol.update` called with s = n and v = 5
As you can see, there is absolutely nothing wrong with the syntax, so why should it be invalid? The error message tells you what the problem is: you are calling Symbol.update but that doesn't exist. A missing method is not a syntactic error, it is a semantic error.

Welcome to Scala 2.12.7 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_92).
Type in expressions for evaluation. Or try :help.
scala> import reflect.runtime.universe._
import reflect.runtime.universe._
scala> reify('n)
res0: reflect.runtime.universe.Expr[Symbol] = Expr[Symbol](Symbol.apply("n"))
scala> val a = 'n
a: Symbol = 'n
scala> a = 5
<console>:15: error: reassignment to val
a = 5
^
scala> a.update(5)
<console>:16: error: value update is not a member of Symbol
a.update(5)
^
Desugar it, and you will find the answer.
In Scala, operators are methods.
For Symbol, see https://github.com/scala/scala/blob/2.13.x/src/library/scala/Symbol.scala

Related

In Scala reflection, why reflection function on TypeTag still has type erasure?

Considering the following scala program:
val arr: Seq[String] = Seq("abc", "def")
val cls = arr.head.getClass
println(cls)
val ttg: TypeTag[Seq[String]] = typeOf[Seq[String]]
val fns = ttg.tpe
.members
val fn = fns
.filter(_.name.toString == "head")
.head // Unsafely access it for now, use Option and map under normal conditions
.asMethod // Coerce to a method symbol
val fnTp = fn.returnType
println(fnTp)
val fnCls = ttg.mirror.runtimeClass(fnTp)
assert(fnTp == cls)
Since TypeTag has both Seq and String information, I would expect that fn.returnType give the correct result "String", but in this case I got the following program output:
cls = class java.lang.String
fnTp = A
And subsequently throw this exception:
A needed class was not found. This could be due to an error in your runpath. Missing class: no Java class corresponding to A found
java.lang.NoClassDefFoundError: no Java class corresponding to A found
at scala.reflect.runtime.JavaMirrors$JavaMirror.typeToJavaClass(JavaMirrors.scala:1258)
at scala.reflect.runtime.JavaMirrors$JavaMirror.runtimeClass(JavaMirrors.scala:202)
at scala.reflect.runtime.JavaMirrors$JavaMirror.runtimeClass(JavaMirrors.scala:65)
Obviously type String was erased, leaving only a wildcard type 'A'
Why TypeTag is unable to yield the correct erased type as intended?
Seq.head is defined as def head: A. And fn is just a method symbol of the method head from a generic class Seq[A], it doesn't know anything about the concrete type. So its returnType is exactly that A just as defined in Seq.
If you want to know what that A would be in some concrete Type, you'd have to specify that explicitly. For instance, you can use infoIn on the method symbol:
scala> val fnTp = fn.infoIn(ttg.tpe)
fnTp: reflect.runtime.universe.Type = => String
scala> val fnRetTp = fnTp.resultType
fnRetTp: reflect.runtime.universe.Type = String

Two Strings, the same type, one has "toInt" method, the other doesn't

I have following code:
val nodePortString: scala.Predef.String = List.fromArray(args, 0, args.length).toList.headOption.getOrElse(throw new IllegalArgumentException())
val a: scala.Predef.String = "1234"
val b = a.toInt
val nodePort = nodePortString.toInt
I even added explicit types, with package, to be sure. The error is thrown from last line:
[error] /Users/amorfis/idea-workspace/akka-journal-stress/src/main/scala/core/core.scala:29: value toInt is not a member of String
[error] val nodePort = nodePortString.toInt
[error] ^
How on Earth is it possible, that I have 2 Strings, of exactly the same type, and one of them has "toInt" method, while the other hasn't?
It's on Scala 2.10.4
scala.Predef.String is an alias for java.lang.String, which doesn't have a toInt method.
The reason why it normally works is that String is implicitly converted to StringOps, which provides all the scala niceties to the java type.
For some reason the implicit is not triggering and my hypothesis is that using the fromArray (deprecated) method messes up the types (arrays are funny beasts, due to java compabitility)
Can you try using
val nodePortString = args.toList.headOption.getOrElse(throw new IllegalArgumentException())
instead?
Update
I tested it on scala 2.10.4 and it compiles for me, here's a REPL session:
Welcome to Scala version 2.10.4 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_20).
Type in expressions to have them evaluated.
Type :help for more information.
scala> object Main extends App {
| override def main(args: Array[String]) = {
| val nodePortString: scala.Predef.String = List.fromArray(args, 0, args.length).toList.headOption.getOrElse(throw new IllegalArgumentException())
| val nodePort = nodePortString.toInt
| }
| }
warning: there were 1 deprecation warning(s); re-run with -deprecation for details
defined module Main
I assumed args to be the parameter of the main method of App, but it shouldn't make a difference as long as it's an Array[String]. Is that your actual code? Can you show a reproducible self-contained example?
Do you sure that nodePortString has same value ?!
The method .toInt it's from StringLike e appears to us via implicit conversion.
What is the args's value ?!
I've tried here with val args = Array("1","2","3","4") and got nodePort: Int = 1 ...
Works fine, but you must to sure ...

Defining a val in terms of the member of an abstract val (Scala bug?)

I came across a runtime error and was wondering whether this is a bug in Scala, or if it shouldn't at least be caught during compile time.
This code produces a NullPointerException:
object Main extends App {
trait A {
val data: { val x: Int }
val x = data.x
}
val a = new A {
val data = new Object { val x = 42 }
}
a.x
}
Of course it's easy to fix by making A.x lazy or a def, but as obvious as that may be in this minimal example, in more realistic code it can be a little perplexing.
This is confusing when you encounter it for the first time, but it is expected behaviour.
The normal initialization order is that vals in super traits are initialized first. In your example this means that val x in trait A gets initialized before val data in your anonymous subclass, therefore causing a NullPointer.
If you want to make your example work you have to use a feature called "Early Defintions" (5.1.6 in the language specification).
In your concrete example this is the syntax you'd need to use:
val a = new {
val data = new Object { val x = 42 }
} with A
This initializes the data val before initializing the vals in A.
I had forgotten that this option is mentioned in the one-question FAQ.
$ scala -Xcheckinit
Welcome to Scala version 2.11.0-RC1 (OpenJDK 64-Bit Server VM, Java 1.7.0_25).
Type in expressions to have them evaluated.
Type :help for more information.
scala> object Main extends App {
| trait A {
| val data: { val x: Int }
| val x = data.x
| }
| val a = new A {
| val data = new Object { val x = 42 }
| }
| a.x
| }
<console>:15: warning: a pure expression does nothing in statement position; you may be omitting necessary parentheses
a.x
^
warning: there were 1 feature warning(s); re-run with -feature for details
defined object Main
scala> Main main null
scala.UninitializedFieldError: Uninitialized field: <console>: 13
at Main$$anon$1.data(<console>:13)
at Main$A$class.$init$(<console>:10)
... 43 elided

Is there a way to provide custom compile error messages in Scala?

Say I am building a library and I want to provide certain custom compile time error messages to users. Is there a way to provide this in Scala, perhaps using annotations?
#implicitNotFound
You could use #annotation.implicitNotFound(msg = "Custom message.") to provide your custom error message in case if implicit parameter is not found. See this answer as example of usage.
Macros
You could also provide custom compilation error messages from macro implementation using method Context#abort.
scala> f"%s"
<console>:8: error: percent signs not directly following splicees must be escaped
f"%s"
^
This messages is provided by function macro_StringInterpolation_f.
Example:
import scala.language.experimental.macros
import reflect.macros.Context
def half(ie: Int): Int = macro halfImpl
def halfImpl(c: Context)(ie: c.Expr[Int]): c.Expr[Int] = {
import c.universe.{ Constant, Apply, Literal }
val i = ie.tree match {
case Literal(Constant(i: Int)) => i
case _ => c.abort(c.enclosingPosition, "call this method with literal argument only.")
}
if (i % 2 == 0) c.literal(i / 2)
else c.abort(ie.tree.pos, "can't get half of odd number.")
}
Errors:
scala> half(2)
res0: Int = 1
scala> half(3)
<console>:1: error: can't get half of odd number.
half(3)
^
scala> val i = 0
i: Int = 0
scala> half(i)
<console>:2: error: call this method with literal argument only.
half(i)
^

Forbid using named arguments in Scala

Is there a way in Scala to forbid using named arguments for a function?
Example:
def func(num: Int, power: Int) = math.pow(num, power)
func(3, 2) // OK
func(num = 3, power = 2) // Forbidden
You could use a function literal:
val func = (num: Int, power: Int) => math.pow(num, power)
func(3, 2)
func(num = 3, power = 2) // "error: not found: value num"
(Although Function2's apply still has argument names:)
func(v1 = 3, v2 = 2)
Here is what I would suggest:
apm#mara:~$ scalac -deprecation -Xfatal-warnings
Welcome to Scala version 2.11.0-20130923-052707-7d570b54c3 (OpenJDK 64-Bit Server VM, Java 1.7.0_25).
Type in expressions to have them evaluated.
Type :help for more information.
scala> def f(#deprecatedName('x) x: Int) = 2 * x
f: (x: Int)Int
scala> f(3)
res0: Int = 6
scala> f(x = 4)
<console>:9: warning: naming parameter x has been deprecated.
f(x = 4)
^
error: No warnings can be incurred under -Xfatal-warnings.
Unfortunately, it doesn't work that way today, although it's an easy tweak.
The error you'd see today is:
error: deprecated parameter name x has to be distinct from any other parameter name (deprecated or not).
Does it make sense to deprecate the current name of a parameter?
The JavaDoc says:
A program element annotated #Deprecated is one that programmers are
discouraged from using, typically because it is dangerous, or because
a better alternative exists.
If you can motivate discouraging users from using named arguments when calling a function, then you should be able to deprecate that usage outright.
Maybe there is better wording for the new error:
warning: that parameter is he-who-must-not-be-named!