I have a class with getter/setter:
class Person {
private var _age = 0
//getter
def age = _age
//setter
def age_=(value: Int): Unit = _age = value
}
We know that we can invoke setter method like this:
val p = new Person()
p.age= (2)
p age= 11
p.age= 8-4
What made interesthing in this case is: the underscore (_) in def age_= can be removed when the method is invoked.
My question is what is the underscore used for in this case?
Someone told me it is used to separate non-alphanum character in identifier. So I tried this:
var x_= = 20
x_= = 10
x= = 5 // I got error here
Why I can't remove the underscore in this case?
Also, if I tried to use the underscore more than once:
val x_=_x = 1
I got compile error too.
Is there a rule about the underscore usage and what is the term for this underscore usage?
The compile error says it all, really:
scala> var x_= = 20
<console>:10: error: Names of vals or vars may not end in `_='
Only methods are allowed to have names ending in _=. This makes sense, because it would be really confusing to allow a val to be named x_=
However it is true that the underscore is used to separate alpha-numeric characters from special characters. It's just that in the case of a val or var, you can't end it with =
scala> var x_# = 20
x_#: Int = 20
scala> x_# = 10
x_$hash: Int = 10
I don't think another underscore is allowed after the first underscore that precedes special characters.
val x_y_^ = 1 // Ok
val x_^_^ = 1 // Not ok
Based on the Scala language spec :
First, an identifier can start with a letter which can be followed by an arbitrary sequence of letters and digits. This may be followed by underscore ‘’ characters and another string composed of either letters and digits or of operator characters.
See also Example 1.1.1 in the linked specification for examples of valid identifiers.
Related
Hello fellow Scalaists,
I recently took another look at setters in Scala and found out that _ in a method name seems to translate to "There might be a space or not and oh also treat the next special character as part of the method name".
So first of all, is this correct?
Secondly, can someone please explain why the second to last line doesnt work?
class Person() {
private var _name: String = "Hans"
def name = _name
def name_=(aName: String) = _name = aName.toUpperCase
}
val myP = new Person()
myP.name = "test"
myP.name= "test"
myP.name_= "test" //Bad doesnt work
myP.name_=("test")//Now It works
Lastly, removing the getter breaks the above example
class Person() {
private var _name: String = "Hans"
def name_=(aName: String) = _name = aName.toUpperCase
}
val myP = new Person()
myP.name = "test" //Doesnt work anymore
myP.name= "test" //Doesnt work anymore
myP.name_= "test" //Still doesnt work
myP.name_=("test")//Still works
Edit:
Here is a quote(seemingly false) from the source which I originally read, and which spawned this question:
This line is a bit more tricky but I'll explain. First, the method
name is "age_=". The underscore is a special character in Scala and in
this case, allows for a space in the method name which essentially
makes the name "age ="
http://dustinmartin.net/getters-and-setters-in-scala/
So first of all, is this correct?
No, underscores in method names do not work exactly like what you described. It doesn't mean "there might be a space and the character after the space is also part of the method name".
Section 4.2 of the Scala Language Specification explains what a method that has a name that ends with _= means.
A variable declaration var x: T is equivalent to the declarations of both a getter function x and a setter function x_=:
def x: T
def x_= (y: T): Unit
An implementation of a class may define a declared variable using a variable definition, or by defining the corresponding setter and getter methods.
Note that if you only define the setter method and not the getter method, then the magic of the setter method disappears - it's treated as just another method that has a name that happens to end with _=, but this has no special meaning in this case.
Only if there are a getter and setter, the method with _= acts as the setter and can be used as such - that's why myP.name = "test" doesn't work anymore if you remove the getter.
The rules are given in http://www.scala-lang.org/files/archive/spec/2.11/01-lexical-syntax.html#identifiers:
First, an identifier can start with a letter which can be followed by an arbitrary sequence of letters and digits. This may be followed by underscore _ characters and another string composed of either letters and digits or of operator characters.
So space isn't allowed after _. (It is actually allowed in identifiers between backquotes.) There are additional special rules for the case where the "string composed of either letters and digits or of operator characters" is precisely =, already described in Jesper's answer.
Secondly, can someone please explain why the second to last line doesnt work?
There are simply no special rules for this case. name_= works here as any other foo method would and you can't write myP.foo "test". But you can write myP foo "test" or myP name_= "test".
Apparently scala does not support the jdk7 and later underscores in numeric literals?
I am using jdk 8
scala> System.getProperty("java.version")
res0: String = 1.8.0_40
Here we try to use a jdk7 (and later) numeric literal:
scala> val x = 1_000_000
<console>:1: error: Invalid literal number
val x = 1_000_000
^
Is there a scala language option for this?
In the Scala land you may have seen things like:
s"My name is $firstName"
and
sql"select id, name from members where id = ${id}"
There's no reason not to have:
i"1 000 000"
or even:
animal"Dog" // checks that Dog is on the list of animal words
There is no i string interpolation built in to the Scala library however you can use:
implicit class IntContext(val sc: StringContext) {
def i(args: Any*): Int = {
val orig = sc.s(args: _*)
orig.replace(" ", "").toInt
}
}
i"1 000 000" // res0: Int = 1000000
Note that starting Scala 2.13, underscore is accepted as a numeric literal separator:
val x = 1_000_000
// x: Int = 1000000
val pi = 3_14e-0_2
// pi: Double = 3.14
You can now write lengthy numeric literals that have a great readability score many thanks to the update done in Java 7. Read More on Adeva's blog https://adevait.com/java/5-best-and-worst-practices-in-java-coding
Before using Underscore:
int minUploadSize = 05437326;
long debitBalance = 5000000000000000L;
float pi = 3.141592653589F;
After making use of Underscore:
int minUploadSize = 05_437_326;
long debitBalance = 5_000_000_000_000_000L;
float pi = 3.141_592_653_589F;
The above-written declarations in Java show the importance of using underscores in numeric literals to improve the readability of codes. When you compare the two declarations above, you would be able to deduce that the one with included underscores is more readable compared to the other one.
In some book I've got a code similar to this:
object ValVarsSamples extends App {
val pattern = "([ 0-9] +) ([ A-Za-z] +)". r // RegEx
val pattern( count, fruit) = "100 Bananas"
}
This is supposed to be a trick, it should like defining same names for two vals, but it is not.
So, this fails with an exception.
The question: what this might be about? (what's that supposed to be?) and why it does not work?
--
As I understand first: val pattern - refers to RegEx constructor function.. And in second val we are trying to pass the params using such a syntax? just putting a string
This is an extractor:
val pattern( count, fruit) = "100 Bananas"
This code is equivalent
val res = pattern.unapplySeq("100 Bananas")
count = res.get(0)
fruit = res.get(1)
The problem is your regex doesn't match, you should change it to:
val pattern = "([ 0-9]+) ([ A-Za-z]+)". r
The space before + in [ A-Za-z] + means you are matching a single character in the class [ A-Za-z] and then at least one space character. You have the same issue with [ 0-9] +.
Scala regexes define an extractor, which returns a sequence of matching groups in the regular expression. Your regex defines two groups so if the match succeeds the sequence will contain two elements.
I am learning Scala, and playing with right associate unapply object. I know that if the name ends with ':' then it becomes right associative. However, there seems to be some strange restrictions on the naming
e.g.
These are invalid
object cons: { def unapply(value: String): Option[(Char, List[Char])] = ??? }
object :_cons_: { def unapply(value: String): Option[(Char, List[Char])] = ??? }
These are valid
object cons_: { def unapply(value: String): Option[(Char, List[Char])] = ??? }
object >>: { def unapply(value: String): Option[(Char, List[Char])] = ??? }
So there seems to be some weirdness about mixing alpha-numeric characters and symbols in identifiers.
So basically, I want to have a descriptive name i.e. 'cons' and still have right associativity. Also, I would like my operator to be symetric for aesthetic reasons :-), so I dont really like cons_:
Is there a way to make something associate to the right without using a colon? Or any other suggestions to achieve this?
:_cons_: seems to be the closest, but, for some reason the identifier can't start with ':' and have alphanumerics
From the spec (section 1.1):
There are three ways to form an identifier. First, an identifier can
start with a letter which can be followed by an arbitrary sequence of
letters and digits. This may be followed by underscore ‘_’ characters
and another string composed of either letters and digits or of
operator characters. Second, an identifier can start with an operator
character followed by an arbitrary sequence of operator characters.
The preceding two forms are called plain identifiers. Finally, an
identifier may also be formed by an arbitrary string between
back-quotes (host systems may impose some restrictions on which
strings are legal for identifiers). The identifier then is composed of
all characters excluding the backquotes themselves.
So it looks like you're out of luck—if your identifier starts with a : it can't contain non-operator characters. Note, though, that you can write the following (which isn't intended to do anything meaningful—just to demonstrate the syntax):
scala> class X { def `:cons:`(i: Int) = i }
defined class X
scala> val x = new X
x: X = X#6a665da6
scala> 1 `:cons:` x
res1: Int = 1
The method name still ends with a colon, so you get the right associativity you're looking for.
String interpolation is available in Scala starting Scala 2.10
This is the basic example
val name = "World" //> name : String = World
val message = s"Hello $name" //> message : String = Hello World
I was wondering if there is a way to do dynamic interpolation, e.g. the following (doesn't compile, just for illustration purposes)
val name = "World" //> name : String = World
val template = "Hello $name" //> template : String = Hello $name
//just for illustration:
val message = s(template) //> doesn't compile (not found: value s)
Is there a way to "dynamically" evaluate a String like that? (or is it inherently wrong / not possible)
And what is s exactly? it's not a method def (apparently it is a method on StringContext), and not an object (if it was, it would have thrown a different compile error than not found I think)
s is actually a method on StringContext (or something which can be implicitly converted from StringContext). When you write
whatever"Here is text $identifier and more text"
the compiler desugars it into
StringContext("Here is text ", " and more text").whatever(identifier)
By default, StringContext gives you s, f, and raw* methods.
As you can see, the compiler itself picks out the name and gives it to the method. Since this happens at compile time, you can't sensibly do it dynamically--the compiler doesn't have information about variable names at runtime.
You can use vars, however, so you can swap in values that you want. And the default s method just calls toString (as you'd expect) so you can play games like
class PrintCounter {
var i = 0
override def toString = { val ans = i.toString; i += 1; ans }
}
val pc = new PrintCounter
def pr[A](a: A) { println(s"$pc: $a") }
scala> List("salmon","herring").foreach(pr)
1: salmon
2: herring
(0 was already called by the REPL in this example).
That's about the best you can do.
*raw is broken and isn't slated to be fixed until 2.10.1; only text before a variable is actually raw (no escape processing). So hold off on using that one until 2.10.1 is out, or look at the source code and define your own. By default, there is no escape processing, so defining your own is pretty easy.
Here is a possible solution to #1 in the context of the original question based on Rex's excellent answer
val name = "World" //> name: String = World
val template = name=>s"Hello $name" //> template: Seq[Any]=>String = <function1>
val message = template(name) //> message: String = Hello World
String interpolation happens at compile time, so the compiler does not generally have enough information to interpolate s(str). It expects a string literal, according to the SIP.
Under Advanced Usage in the documentation you linked, it is explained that an expression of the form id"Hello $name ." is translated at compile time to new StringContext("Hello", "."). id(name).
Note that id can be a user-defined interpolator introduced through an implicit class. The documentation gives an example for a json interpolator,
implicit class JsonHelper(val sc: StringContext) extends AnyVal {
def json(args: Any*): JSONObject = {
...
}
}
This is inherently impossible in the current implementation: local variable names are not available at execution time -- may be kept around as debug symbols, but can also have been stripped. (Member variable names are, but that's not what you're describing here).