Scala method notation using def name_= not working? - scala

whenever I try to run this simple code it says that balance is not a member of Playground.cat
I read about this notation in a book, so I am confused on why it isn’t working?
class cat(var _balance:Int) {
def balance_=(nb:Int) = _balance = nb
}
val c = new cat(5)
c.balance +=25
print(c._balance)

whenever I try to run this simple code it says that balance is not a member of Playground.cat
I read about this notation in a book, so I am confused on why it isn’t working?
The error message spells it out pretty clearly: it cannot find the member balance, in other words, you are missing the getter.
It is also spelled out pretty explicitly in section 6.15 Assignments of the Scala Language Specification [bold emphasis mine]:
6.15 Assignments
Expr1 ::= [SimpleExpr ‘.’] id ‘=’ Expr
| SimpleExpr1 ArgumentExprs ‘=’ Expr
The interpretation of an assignment to a simple variable 𝑥 = 𝑒 depends on the definition of 𝑥. If 𝑥 denotes a mutable variable, then the assignment changes the current value of 𝑥 to be the result of evaluating the expression 𝑒. The type of 𝑒 is expected to conform to the type of 𝑥. If 𝑥 is a parameterless method defined in some template, and the same template contains a setter method 𝑥_= as member, then the assignment 𝑥 = 𝑒 is interpreted as the invocation 𝑥_=(𝑒) of that setter method. Analogously, an assignment 𝑓.𝑥 = 𝑒 to a parameterless method 𝑥 is interpreted as the invocation 𝑓.𝑥_=(𝑒).
To fix it, you need to add a getter of the same name in the same template (that's the name the SLS uses for class, trait, or object):
class Cat(var _balance: Int) {
def balance = _balance
def balance_=(nb: Int) = _balance = nb
}
val c = new Cat(5)
c.balance += 25
print(c._balance)

Take a look at this snippet, please:
class cat(var _balance: Int) {
def balance_=(n: Int) = _balance = n
def balance = _balance
}
val c = new cat(5)
c.balance += 25
print(c.balance)
(https://scastie.scala-lang.org/5ZiUDYizQHSJqjR3pYugYA)
If I'm getting it right, scala also needs accessor for _balance value with the same name, as c.balance += 25 gets "expanded" by compiler into
c.balance = c.balance + 25

Related

Assign values of a tuple to single variables in Scala

is there a nice possibilyt to assign the values of a tuple to single variables? Here is what i want to do but it throws an error:
var a : Int = _
var b : Int = _
def init() {
(a,b) = getTuple() // returns (Int, Int)
}
def someFunction = {
// use a here
}
def someOtherFunction = {
// use b here
}
Error: ';' expected but '=' found.
(a, b) = getTuple()
Do i really have to do this ._1 and ._2 thing?
val tuple = getTuple() // returns (Int, Int)
a = tuple._1
b = tuple._2
Thanks for answering.
As you can see in Section 6.15 Assignments of the Scala Language Specification, the lefthand-side of an assignment expression must be either a bare identifier:
foo = ???
// either an assignment to a local mutable `var` or syntactic sugar for:
foo_=(???)
a member access:
foo.bar = ???
// syntactic sugar for:
foo.bar_=(???)
or a method call:
foo(bar) = ???
// syntactic sugar for:
foo.update(bar, ???)
Pattern Matching is only available for value definitions (see Section 4.1 Value Declarations and Definitions) and variable definitions (see Section 4.2 Variable Declarations and Definitions).
So, both of the following would be legal:
var (a, b) = getTuple()
val (a, b) = getTuple()
By the way, there are a number of highly non-idiomatic things in your code:
It is highly unusual to pass an empty argument list to a method call. Normally, methods that have no parameters should have no parameter lists instead of an empty parameter list. The only exception are methods that have side-effects, where the empty parameter list serves as kind of a "warning flag", but methods which have side-effects are highly non-idiomatic as well. So, getTuple should be defined and called like this:
def getTuple = ???
getTuple
instead of
def getTuple() = ???
getTuple()
On the other hand, if the method does have side-effects, it shouldn't be called getTuple because that implies that the method "gets" something, and there is no indication that it also changes something.
In Scala, getters and setters are not called getFoo and setFoo but rather foo and foo_=, so the method should rather be called tuple.
Mutable variables are highly non-idiomatic as well, they should probably rather be vals.
Tuple destructuring is an opaque call to unapply method of TupleN[T1, ..., Tn] companion object, aka extractor object.
As the documentation suggests, one can only have a variable initialization with an extractor object, but not assignment.
So, the only possible way is to use it like this:
def getTuple = (1,2)
def main() = {
val (a,b) = getTuple
// use a,b
}
The fact that you use a var may be the sign you have a design flaw. Try to refactor your code or specify the exact problem you're trying to solve with the var to justify its usage (although what you're trying to do with extractor is still impossible)

Strange behavior of Scala compiler when initializing a class with a lazy argument

How possible that the first is correct Scala code but the second won't even compile?
The one that does compile
object First {
class ABC(body: => Unit) {
val a = 1
val b = 2
println(body)
}
def main(args: Array[String]): Unit = {
val x = new ABC {
a + b
}
}
}
This one doesn't compile on Scala 2.11 and 2.12
object Second {
class ABC(body: => Int) {
val a = 1
val b = 2
println(body)
}
def main(args: Array[String]): Unit = {
val x = new ABC {
a + b
}
}
}
It's not strange at all. Let's look at the first example:
You declare your class ABC to receive a pass by name parameter that returns Unit and you think this snippet:
val x = new ABC {
a + b
}
is passing that body parameter, it isn't.What's really happening is:
val x = new ABC(()) { a + b }
If you run that code you will see that println(body) prints () because you're not passing a value for your body parameter, the compiler allows it to compile because as the scaladoc states there is only 1 value of type Unit:
Unit is a subtype of scala.AnyVal. There is only one value of type Unit, (), and it is not represented by any object in the underlying runtime system. A method with return type Unit is analogous to a Java method which is declared void.
Since there is only one value the compiler allows you to omit it and it will fill in the gap. This doesn't happen with singleton objects because they don't extend AnyVal. Just has the default value for Int is 0 the default value for Unit is () and because there is only this value available the compiler accepts it.
From documentation:
If ee has some value type and the expected type is Unit, ee is converted to the expected type by embedding it in the term { ee; () }.
Singleton objects don't extend AnyVal so they don't get treated the same.
When you use syntax like:
new ABC {
// Here comes code that gets executed after the constructor code.
// Code here can returns Unit by default because a constructor always
// returns the type it is constructing.
}
You're merely adding things to the constructor body, you are not passing parameters.
The second example doesn't compile because the compiler cannot infer a default value for body: => Int thus you have to explicitly pass it.
Conclusion
Code inside brackets to a constructor is not the same as passing a parameter. It might look the same in same cases, but that's due to "magic".
You cannot pass a single argument to a constructor in curly braces, because this would be parsed as defining an anonymous class. If you want to do this, you need to enclose the curly braces in normal braces as well, like this:
new ABC({
a + b
})
As for why does compiler accept new ABC {a + b}, the explanation is a bit intricate and unexpected:
new ABC {...} is equivalent to new ABC() {...}
new ABC() can be parsed as new ABC(()) because of automatic tupling, which is a feature of the parser not mentioned in the specs, see SI-3583 Spec doesn't mention automatic tupling. The same feature casues the following code to compile without an error:
def f(a: Unit) = {}
f()
def g(a: (Int, Int)) = {}
g(0,1)
Note the call produces a warning (even your original example does):
Adaptation of argument list by inserting () has been deprecated: this is unlikely to be what you want.
The warning is produced since 2.11, see issue SI-8035 Deprecate automatic () insertion.

Scala recursive val behaviour

What do you think prints out?
val foo: String = "foo" + foo
println(foo)
val foo2: Int = 3 + foo2
println(foo2)
Answer:
foonull
3
Why? Is there a part in specification that describes/explains this?
EDIT: To clarify my astonishment - I do realize that foo is undefined at val foo: String = "foo" + foo and that's why it has a default value null (zero for integers). But this doesn't seem very "clean", and I see opinions here that agree with me. I was hoping that compiler would stop me from doing something like that. It does make sense in some particular cases, such as when defining Streams which are lazy by nature, but for strings and integers I would expect either stopping me due to reassignment to val or telling me that I'm trying to use an undefined value, just like as if I wrote val foo = whatever (given that whatever was never defined).
To further complicate things, #dk14 points out that this behaviour is only present for values represented as fields and doesn't happen within blocks, e.g.
val bar: String = {
val foo: String = "foo" + foo // error: forward reference extends...
"bar"
}
Yes, see the SLS Section 4.2.
foo is a String which is a reference type. It has a default value of null. foo2 is an Int which has a default value of 0.
In both cases, you are referring to a val which has not been initialized, so the default value is used.
In both cases foo resp. foo2 have their default values according to the JVM specification. That's null for a reference type and 0 for an int or Int - as Scala spells it.
Scala's Int is backed by a primitive type (int) underneath, and its default value (before initialization) is 0.
On the other hand, String is an Object which is indeed null when not initialized.
Unfortunately, scala is not as safe as let's say Haskell because of compromises with Java's OOP model ("Object-oriented meets Functional"). There is a safe subset of Scala called Scalazzi and some of sbt/scalac-plugins can give you more warnings/errors:
https://github.com/puffnfresh/wartremover (doesn't check for the case you've found though)
Returning back to your case, this happens only for values represented as fields and doesn't happen when you're inside a function/method/block:
scala> val foo2: Int = 3 + foo2
foo2: Int = 3
scala> {val foo2: Int = 3 + foo2 }
<console>:14: error: forward reference extends over definition of value foo2
{val foo2: Int = 3 + foo2 }
^
scala> def method = {val a: Int = 3 + a}
<console>:12: error: forward reference extends over definition of value a
def method = {val a: Int = 3 + a}
The reason of this situation is integration with Java as val compiles to the final field in JVM, so Scala saves all initialization specifics of JVM classes (I believe it's part of JSR-133: Java Memory Model). Here is the more complicated example that explains this behavior:
scala> object Z{ val a = b; val b = 5}
<console>:12: warning: Reference to uninitialized value b
object Z{ val a = b; val b = 5}
^
defined object Z
scala> Z.a
res12: Int = 0
So, here you can see the warning that you didn't see in the first place.

Type alias for mix-in type

Since I can do this:
case class A(a: Int)
trait C
val x = new A(10) with C
Why can't I do this:
type X = A with C
val x = new X(10)
? If I can't even construct an instance, what's the use case of type X = A with C?
The error message that you get should give you a hint:
error: class type required but A with C found
new X(10)
^
X, as a type alias, gets rewritten to an A with C type expression, which is not a class type. The latter, according to the Scala Language Specification, is:
a type designator
(§
3.2.3
) that refers to a a class or a trait
(emphasis mine)
In other words, it's not every type expression.
This is not an authoritative answer, but I don't believe it's possible to define a type alias in such a way that it becomes a class type. On the one hand, a new expression theoretically accepts an AnnotType, as defined in Section 5.1.1. However, I don't see how, using the type alias grammar from Section 4.3, you could specify what constructor are you using etc..
tl;dr - unless your type alias is directly rewritable to a class type (e.g. A in your example), you can't use it as a class type, which includes invoking new with it. If you want that, you need a class declaration, i.e. class X(a: Int) extends A(a) with C.
Regarding your second question, you say you can't instantiate X. Ah, but that's where you're wrong! Let me show you an example, based on your code:
def blah(x: X) = x.toString
val x = new A(10) with C
val y = new A(10)
blah(x) //String = A(10)
blah(y) //type error
So, it's useful whenever you need a type constraint, since the "aliased" type will be matched to the type alias, even if it wasn't explicitly declared as such.

Scala class members and constructor parameters name clash

Consider the following class written in Java:
class NonNegativeDouble {
private final double value;
public NonNegativeDouble(double value) {
this.value = Math.abs(value);
}
public double getValue() { return value; }
}
It defines a final field called value that is initialized in the constructor, by taking its parameter called alike and applying a function to it.
I want to write something similar to it in Scala. At first, I tried:
class NonNegativeDouble(value: Double) {
def value = Math.abs(value)
}
But the compiler complains: error: overloaded method value needs result type
Obviously the compiler thinks that the expression value inside the expression Math.abs(value) refers to the method being defined. Therefore, the method being defined is recursive, so I need to state its return type. So, the code I wrote does not do what I expected it to do: I wanted value inside Math.abs(value) to refer to the constructor parameter value, and not to the method being defined. It is as if the compiler implicitly added a this. to Math.abs(this.value).
Adding val or var (or private ... variants) to the constructor parameter doesn't seem to help.
So, my question is: can I define a property with the same name as a constructor parameter, but maybe a different value? If so, how? If not, why?
Thanks!
No, you can't. In Scala, constructor parameters are properties, so it makes no sense to redefine them.
The solution, naturally, is to use another name:
class NonNegativeDouble(initValue: Double) {
val value = Math.abs(initValue)
}
Used like this, initValue won't be part of the instances created. However, if you use it in a def or a pattern matching declaration, then it becomes a part of every instance of the class.
#Daniel C. Sobral
class NonNegativeDouble(initValue: Double) {
val value = Math.abs(initValue)
}
your code is right, but "constructor parameters are properties",this is not true.
A post from the official site said,
A parameter such as class Foo(x : Int) is turned into a field if it is
referenced in one or more methods
And Martin's reply confirms its truth:
That's all true, but it should be treated as an implementation
technique. That's why the spec is silent about it.
So normally, we can still treat primary constructor parameters as normal method parameter, but when the parameters is referenced by any of the methods, the compiler will cleverly turn it into a private field.
If any formal parameter preceded by the val, the compiler generates an getter definition automatically.if var, generates a setter additionally. see the language speification section 5.3.
That's all about primary constructor parameters.
You can consider parametric field
class NonNegativeDouble(val value: Double, private val name: String ){
if (value < 0) throw new IllegalArgumentException("value cannot be negative")
override def toString =
"NonNegativeDouble(value = %s, name = %s)" format (value, name)
}
val tom = "Tom"
val k = -2.3
val a = new NonNegativeDouble(k.abs, tom)
a: NonNegativeDouble = NonNegativeDouble(value = 2.3, name = Tom)
a.value
res13: Double = 2.3
a.name
<console>:12: error: value name in class NonNegativeDouble cannot be accessed in NonNegativeDouble
a.name
val b = new NonNegativeDouble(k, tom)
java.lang.IllegalArgumentException: value cannot be negative
...
It's defines fields and parameters with the same names "value", "name".
You can add modifiers such as private ...
In the case of case classes it should be:
case class NonNegativeDouble(private val initValue: Double) {
val value = Math.abs(initValue)
def copy(value: Double = this.value) = NonNegativeDouble(value)
}
The implementation of copy is required to prevent the sintesized version of the compiler that will bind the initValue argument.
I expect that the compiler is smart enough to not retain the «extra space» for the initValue. I haven't verified this behaviour.