I'm reading a book "Scala in action" to learn scala. On page 59, the author provide following code. But when I run it, it doesn't compile.
class Person(var firstName:String, var lastName:String, private var _age:Int) {
def age = _age
def age_ = (newAge: Int) = _age = newAge //Error
}
It reports:
<console>:3: ';' expected but '=' found.
def age_ = (newAge: Int) = _age = newAge}
This book use scala 2.10. I'm using scala 2.11.
Does scala change the usage of setter method in 2.11?
I'm not sure what the context of def age_ = (newAge: Int) = _age = newAge or what the author is trying to demonstrate with that line. Parameters of the constructor are public by default so removing private allows you to use the setter for free.
scala> class Person(var firstName:String, var lastName:String, var _age:Int) {}
defined class Person
scala> val bob = new Person("Bob", "Smith", 42)
bob: Person = Person#290d210d
scala> bob._age = 43
bob._age: Int = 43
scala> bob._age
res9: Int = 43
It looks like the only problem is the missprint ' ' ('space') simbol of the errored line between 'underscore' and 'equals'.
The right code is:
def age_= (newAge: Int) = _age = newAge //NO Error
Lets remove syntax sugar (plus abit classic formatting) and we'll have more clear method that is really the setter for "variable" age "backed" to private var _age
def age_=(newAge: Int): Unit = {
_age = newAge
}
Think about such variables as if it is virtual-like: this setter with previously defined getter but possibly with no storage at all. For instance, both uses HTTP requests (GET & PUT) to store/retrieve the value).
So, the first '=' is really the part of the method name age_= that is the syntax Scala uses to define setter for age.
Related
I'm new to scala, and is trying to follow the tutorial on the documentation. I understand that this is used to 'access' a class' methods or fields, but on this code block, this is called as without any 'option' but it default to call the 'toString' method.
Here's the code block i'm talking about
class Person(var firstName: String, var lastName: String) {
println("the constructor begins")
// 'public' access by default
var age = 0
// some class fields
private val HOME = System.getProperty("user.home")
// some methods
override def toString(): String = s"$firstName $lastName is $age years old"
def printHome(): Unit = println(s"HOME = $HOME")
def printFullName(): Unit = println(this)
printHome()
printFullName()
println("you've reached the end of the constructor")
}
Output in REPLL:
scala> val p = new Person("Kim", "Carnes")
the constructor begins
HOME = /Users/al
Kim Carnes is 0 years old
you've reached the end of the constructor
p: Person = Kim Carnes is 0 years old
Thanks to Luis. His answer:
println calls the toString method of whatever is passed to it. So the object itself was passed to the println function which then called toString to it. Does that makes sense?
Basically, println(this) has the same output when you call println(p).
I am trying to add a setter method in an object:
object Foo {
def foo_=(s: String) = println(s)
}
Foo.foo = "test"
This does not work as expected, and compiler complains about "value foo is not a member of object Foo".
Why does it not work? How can I make it work, other than make a setter method without _=?
This seems to work just right in Scala 2.13
object Foo {
private var f: String = ""
def foo_=(s: String): Unit =
this.f = s.toLowerCase
def foo: String =
this.f
}
You can check like following.
Foo.foo
// res: String = ""
Foo.foo = "HELLO"
Foo.foo
// res: String = "hello"
Scala only recognizes this method as a setter if you have a getter def foo = ... as well:
If x is a parameterless method defined in some template, and the same template contains a setter method x_= as member, then the assignment x = e is interpreted as the invocation x_=(e) of that setter method.
From your comment:
Looks like 2.12.9 fixed it. It was broken with the version I was using 2.12.8.
Testing at https://scastie.scala-lang.org/8uAu5WCAQJa3darpMkCidg, I get the expected result: without a getter it fails in both 2.12.8 and 2.13.1, with one it works in both.
I am reading "Scala for the Impatient" and in 8.10 there is an example:
class Animal {
val range: Int = 10
val env: Array[Int] = new Array[Int](range)
}
class Ant extends Animal {
override val range: Int = 2
}
The author explains why the env ends up being an empty Array[Int]:
[..]
3. The Animal constructor, in order to initialize the env array, calls the range() getter.
That method is overridden to yield the (as yet uninitialized) range field of the Ant class.
The range method returns 0. (That is the initial value of all integer fields when an object is allocated.)
env is set to an array of length 0.
The Ant constructor continues, setting its range field to 2.[..]
I don't understand the 4th and therefore the next steps are also not clear. range() method is overridden with 2, so why doesn't it set the range already in the 4th step?
Doe is work this way? When the val is overridden, then it gets uninitialised and also all val's that include this overridden gals get modified as well. Is it correct? If yes, why there is a different behaviour with def as outlined here. Why is def defined before the constructor call and val after?
After your comment, I decided to actually look at what exactly is written in the book. After reading the explanation, I decided that I cannot express it any clearer. So instead, I propose to take a look at the completely desugared code, for which there was no place in the short book.
Save this as a Scala script:
class Animal {
val range: Int = 10
val env: Array[Int] = new Array[Int](range)
}
class Ant extends Animal {
override val range: Int = 2
}
val ant = new Ant
println(ant.range)
println(ant.env.size)
and then run it using the -print-option:
> scala -nc -print yourScript.scala
you should see something like this:
class anon$1$Animal extends Object {
private[this] val range: Int = _;
<stable> <accessor> def range(): Int = anon$1$Animal.this.range;
private[this] val env: Array[Int] = _;
<stable> <accessor> def env(): Array[Int] = anon$1$Animal.this.env;
<synthetic> <paramaccessor> <artifact> protected val $outer: <$anon: Object> = _;
<synthetic> <stable> <artifact> def $outer(): <$anon: Object> = anon$1$Animal.this.$outer;
def <init>($outer: <$anon: Object>): <$anon: Object> = {
if ($outer.eq(null))
throw null
else
anon$1$Animal.this.$outer = $outer;
anon$1$Animal.super.<init>();
anon$1$Animal.this.range = 10;
anon$1$Animal.this.env = new Array[Int](anon$1$Animal.this.range());
()
}
};
class anon$1$Ant extends <$anon: Object> {
private[this] val range: Int = _;
override <stable> <accessor> def range(): Int = anon$1$Ant.this.range;
<synthetic> <stable> <artifact> def $outer(): <$anon: Object> = anon$1$Ant.this.$outer;
def <init>($outer: <$anon: Object>): <$anon: anon$1$Animal> = {
anon$1$Ant.super.<init>($outer);
anon$1$Ant.this.range = 2;
()
}
}
This is the desugared code as it is seen by the compiler in later stages of compilation. It's a bit hard to read, but what it is important are these declarations:
// in Animal:
private[this] val range: Int = _;
<stable> <accessor> def range(): Int = anon$1$Animal.this.range;
// in Ant:
private[this] val range: Int = _;
override <stable> <accessor> def range(): Int =
anon$1$Ant.this.range;
and also the statement in the initializer of Animal:
anon$1$Animal.this.env = new Array[Int](anon$1$Animal.this.range())
What you can see here is that there are actually two different variables range: one is Animal.this.range and the other is Ant.this.range. Moreover, there are completely separate defs which are also called range in the desugared code: these are the getters which are generated automatically for vals.
The first variable is indeed initialized in Animal and set to 10:
anon$1$Animal.this.range = 10;
However, this does not matter, because the env is initialized using the getter range(), which is overridden to return Ant.this.range. The variable Ant.this.range is assigned the value 2 once, but after the initializer of Animal has completed. During the initialization of Animal, the variable Ant.this.range holds the default value 0, hence the counter-intuitive result.
If you simplify the desugared code a little bit, you obtain a compilable and readable example that behaves in the same way:
class Animal {
private[this] var _Animal_range: Int = 0
def range: Int = _Animal_range
_Animal_range = 10
val env: Array[Int] = new Array[Int](range)
}
class Ant extends Animal {
private[this] var _Ant_range: Int = 0
override def range: Int = _Ant_range
_Ant_range = 2
}
val ant = new Ant
println(ant.range)
println(ant.env.size)
Here, the same happens:
_Animal_range is allocated with default value 0
_Ant_range is allocated with default value 0
Animal base class begins initialization
_Animal_range is initialized with value 10
To initialize env, the getter range is invoked. It is overridden in the Ant-class, and returns _Ant_range, which is still 0
env is set to an empty array
Animal base class finishes initialization
Ant begins initialization
Only now does it set _Ant_range to 2.
This is why both code snippets print 2 and 0.
Hope that helps.
defs are called when asked for, but vals are kept in memory, so in this case the val version is still set to zero because it hasn’t been initialised, where the def version is called and therefore can never give a misleading value.
It’s only because the val here is overloaded that it isn’t initialised in time, because classes must be initialised starting at the top of the inheritance hierarchy and building down. If env were a def you’d be fine also, because it wouldn’t be created until called, by which time the vals would all be initialised. Of course this way you’d get different lists every time you called env so you might prefer to use lazy val, which is initialised the first time it’s called, but then remains the same and is kept in memory.
Search results so far have led me to believe this is impossible without either a non-primary constructor
class Foo { // NOT OK: 2 extra lines--doesn't leverage Scala's conciseness
private var _x = 0
def this(x: Int) { this(); _x = x }
def x = _x
}
val f = new Foo(x = 123) // OK: named parameter is 'x'
or sacrificing the name of the parameter in the primary constructor (making calls using named parameters ugly)
class Foo(private var _x: Int) { // OK: concise
def x = _x
}
val f = new Foo(_x = 123) // NOT OK: named parameter should be 'x' not '_x'
ideally, one could do something like this:
class Foo(private var x: Int) { // OK: concise
// make just the getter public
public x
}
val f = new Foo(x = 123) // OK: named parameter is 'x'
I know named parameters are a new thing in the Java world, so it's probably not that important to most, but coming from a language where named parameters are more popular (Python), this issue immediately pops up.
So my question is: is this possible? (probably not), and if not, why is such an (in my opinion) important use case left uncovered by the language design? By that, I mean that the code either has to sacrifice clean naming or concise definitions, which is a hallmark of Scala.
P.S. Consider the case where a public field needs suddenly to be made private, while keeping the getter public, in which case the developer has to change 1 line and add 3 lines to achieve the effect while keeping the interface identical:
class Foo(var x: Int) {} // no boilerplate
->
class Foo { // lots of boilerplate
private var _x: Int = 0
def this(x: Int) { this(); _x = x }
def x = _x
}
Whether this is indeed a design flaw is rather debatable. One would consider that complicating the syntax to allow this particular use case is not worthwhile.
Also, Scala is after all a predominantly functional language, so the presence of vars in your program should not be that frequent, again raising the question if this particular use case needs to be handled in a special way.
However, it seems that a simple solution to your problem would be to use an apply method in the companion object:
class Foo private(private var _x: Int) {
def x = _x
}
object Foo {
def apply(x: Int): Foo = new Foo(x)
}
Usage:
val f = Foo(x = 3)
println(f.x)
LATER EDIT:
Here is a solution similar to what you originally requested, but that changes the naming a bit:
class Foo(initialX: Int) {
private var _x = initialX
def x = _x
}
Usage:
val f = new Foo(initialX = 3)
The concept you are trying to express, which is an object whose state is mutable from within the object and yet immutable from the perspective of other objects ... that would probably be expressed as an Akka actor within the context of an actor system. Outside the context of an actor system, it would seem to be a Java conception of what it means to be an object, transplanted to Scala.
import akka.actor.Actor
class Foo(var x: Int) extends Actor {
import Foo._
def receive = {
case WhatIsX => sender ! x
}
}
object Foo {
object WhatIsX
}
Not sure about earlier versions, but In Scala 3 it can easily be implemented like follows:
// class with no argument constructor
class Foo {
// prive field
private var _x: Int = 0
// public getter
def x: Int = _x
// public setter
def x_=(newValue: Int): Unit =
_x = newValue
//auxiliary constructor
def this(value: Int) =
this()
_x = value
}
Note
Any definition within the primary constructor makes the definition public, unless you prepend it with private modifier
Append _= after a method name with Unit return type to make it a setter
Prepending a constructor parameter neither with val nor with var, makes it private
Then it follows:
val noArgFoo = Foo() // no argument case
println(noArgFoo.x) // the public getter prints 0
val withArgFoo = Foo(5) // with argument case
println(withArgFoo.x) // the public getter prints 5
noArgFoo.x = 100 // use the public setter to update x value
println(noArgFoo.x) // the public getter prints 100
withArgFoo.x = 1000 // use the public setter to update x value
println(withArgFoo.x) // the public getter prints 1000
This solution is exactly what you asked; in a principled way and without any ad hoc workaround e.g. using companion objects and the apply method.
I have this piece of code that loads Properties from a file:
class Config {
val properties: Properties = {
val p = new Properties()
p.load(Thread.currentThread().getContextClassLoader.getResourceAsStream("props"))
p
}
val forumId = properties.get("forum_id")
}
This seems to be working fine.
I have tried moving the initialization of properties into another val, loadedProperties, like this:
class Config {
val properties: Properties = loadedProps
val forumId = properties.get("forum_id")
private val loadedProps = {
val p = new Properties()
p.load(Thread.currentThread().getContextClassLoader.getResourceAsStream("props"))
p
}
}
But it doesn't work! (properties is null in properties.get("forum_id") ).
Why would that be? Isn't loadedProps evaluated when referenced by properties?
Secondly, is this a good way to initialize variables that require non-trivial processing? In Java, I would declare them final fields, and do the initialization-related operations in the constructor.
Is there a pattern for this scenario in Scala?
Thank you!
Vals are initialized in the order they are declared (well, precisely, non-lazy vals are), so properties is getting initialized before loadedProps. Or in other words, loadedProps is still null when propertiesis getting initialized.
The simplest solution here is to define loadedProps before properties:
class Config {
private val loadedProps = {
val p = new Properties()
p.load(Thread.currentThread().getContextClassLoader.getResourceAsStream("props"))
p
}
val properties: Properties = loadedProps
val forumId = properties.get("forum_id")
}
You could also make loadedProps lazy, meaning that it will be initialized on its first access:
class Config {
val properties: Properties = loadedProps
val forumId = properties.get("forum_id")
private lazy val loadedProps = {
val p = new Properties()
p.load(Thread.currentThread().getContextClassLoader.getResourceAsStream("props"))
p
}
}
Using lazy val has the advantage that your code is more robust to refactoring, as merely changing the declaration order of your vals won't break your code.
Also in this particular occurence, you can just turn loadedProps into a def (as suggested by #NIA) as it is only used once anyway.
I think here loadedProps can be simply turned into a function by simply replacing val with def:
private def loadedProps = {
// Tons of code
}
In this case you are sure that it is called when you call it.
But not sure is it a pattern for this case.
Just an addition with a little more explanation:
Your properties field initializes earlier than loadedProps field here. null is a field's value before initialization - that's why you get it. In def case it's just a method call instead of accessing some field, so everything is fine (as method's code may be called several times - no initialization here). See, http://docs.scala-lang.org/tutorials/FAQ/initialization-order.html. You may use def or lazy val to fix it
Why def is so different? That's because def may be called several times, but val - only once (so its first and only one call is actually initialization of the fileld).
lazy val can initialize only when you call it, so it also would help.
Another, simpler example of what's going on:
scala> class A {val a = b; val b = 5}
<console>:7: warning: Reference to uninitialized value b
class A {val a = b; val b = 5}
^
defined class A
scala> (new A).a
res2: Int = 0 //null
Talking more generally, theoretically scala could analize the dependency graph between fields (which field needs other field) and start initialization from final nodes. But in practice every module is compiled separately and compiler might not even know those dependencies (it might be even Java, which calls Scala, which calls Java), so it's just do sequential initialization.
So, because of that, it couldn't even detect simple loops:
scala> class A {val a: Int = b; val b: Int = a}
<console>:7: warning: Reference to uninitialized value b
class A {val a: Int = b; val b: Int = a}
^
defined class A
scala> (new A).a
res4: Int = 0
scala> class A {lazy val a: Int = b; lazy val b: Int = a}
defined class A
scala> (new A).a
java.lang.StackOverflowError
Actually, such loop (inside one module) can be theoretically detected in separate build, but it won't help much as it's pretty obvious.