I have made heavy use of case classes in my code, replying on the underlying equality definitions of case class to behave correctly. Then now I found that I need to add another field member to a case class.
So if I add a var field member in case class, will it mess up the equality attributes for the case class?
If 1 is yes, then what if I just change the var field value once, after that, no any reassignment will happen, before the case class goes into any collections or do equality comparison, will that still mess up the equality behaviors?
Case class equality is based solely on its primary constructor attributes, whether they're var or val (yes, you can make them var by giving an explicit var to override the implied val that case class constructor args possess.) Adding properties in the body of a case class does not influence the compiler-generated equals(other: Any) method.
Witness:
package rrs.scribble
object CCVarEq
{
case class CC1(i: Int, j: Float, var k: Double)
case class CC2(i: Int, j: Float, var k: Double) {
var l = math.Pi
}
def show {
val cc11 = CC1(1, 2.0f, 3.0)
val cc12 = CC1(1, 2.0f, 3.0)
val cc21 = CC2(1, 2.0f, 3.0); cc21.l = math.E
val cc22 = CC2(1, 2.0f, 3.0)
printf("cc11 == cc12: %s%n", cc11 == cc12); cc12.k = math.Pi * math.E
printf("cc11 == cc12: %s%n", cc11 == cc12)
printf("cc21 == cc22: %s%n", cc21 == cc22)
}
}
In the REPL:
scala> import rrs.scribble.CCVarEq._
import rrs.scribble.CCVarEq._
scala> show
cc11 == cc12: true
cc11 == cc12: false
cc21 == cc22: true
And all jamie's points about concurrency are valid, too.
It's not that simple. You update a case class var on one thread, and another thread is performing an equality check. Unless the var is volatile, it's plausible that the change to the var won't be propagated to the other thread before the equality check is performed. So you could have race conditions. Regardless if this happens only once or many times.
Is the value to be changed once undefined up to that point? If so, use a lazy val. It has a synchronization concern that could slow high-performance code, but would otherwise meet your use case.
Related
In ScalaTest, I have the following check:
"abc".r shouldBe "abc".r
But it is not equal. I don't understand.
abc was not equal to abc
ScalaTestFailureLocation: com.ing.cybrct.flink.clickstream.ConfigsTest$$anonfun$6 at (ConfigsTest.scala:97)
Expected :abc
Actual :abc
While it's possible to decide whether two regular expressions accept the same language, it seems to be rather complicated and not all that terribly useful for everyday regex usage. Therefore, equality on compiled regex patterns is just referential equality:
val x = "abc".r
val y = "abc".r
x == y
// res0: Boolean = false
The method shouldBe in Scalatest 3.0.5 delegates the equality check to the areEqualComparingArraysStructurally method:
def shouldBe(right: Any): Assertion = {
if (!areEqualComparingArraysStructurally(leftSideValue, right)) {
val (leftee, rightee) = Suite.getObjectsForFailureMessage(leftSideValue, right)
val localPrettifier = prettifier // Grabbing a local copy so we don't attempt to serialize AnyShouldWrapper (since first param to indicateFailure is a by-name)
indicateFailure(FailureMessages.wasNotEqualTo(localPrettifier, leftee, rightee), None, pos)
}
else indicateSuccess(FailureMessages.wasEqualTo(prettifier, leftSideValue, right))
}
which in turn simply delegates the equality check (as you can expect) to the == operator:
private[scalatest] def areEqualComparingArraysStructurally(left: Any, right: Any): Boolean = {
// Prior to 2.0 this only called .deep if both sides were arrays. Loosened it
// when nearing 2.0.M6 to call .deep if either left or right side is an array.
// TODO: this is the same algo as in scalactic.DefaultEquality. Put that one in
// a singleton and use it in both places.
left match {
case leftArray: Array[_] =>
right match {
case rightArray: Array[_] => leftArray.deep == rightArray.deep
case _ => leftArray.deep == right
}
case _ => {
right match {
case rightArray: Array[_] => left == rightArray.deep
case _ => left == right
}
}
}
}
In Scala, at least on the JVM, == simply calls equals, which, if not overridden, checks whether the compared variables point to the same object. case classes are peculiar in that the compiler overrides equals for you to compare the constructor arguments.
You can test it very easily with the following (but as you can imagine, the same applies to simply using == on your own):
package org.example
import org.scalatest.{FlatSpec, Matchers}
final class MyClass(val a: Int)
final case class MyCaseClass(a: Int)
final class TestSpec extends FlatSpec with Matchers {
"equality on case classes" should "succeed" in {
new MyCaseClass(1) shouldBe new MyCaseClass(1)
}
"equality on non-case classes" should "fail" in {
new MyClass(1) shouldNot be(new MyClass(1))
}
"equality between Regex objects" should "fail" in {
"abc".r shouldNot be("abc".r)
}
}
What the r method does is instantiating a new Regex object, which is not a case class and does not override the equality definition, thus yielding the result you see.
Yes, they are not equal. Both "abc".r are different references to two different memory locations. shouldbe is to check equality but not identity.
I want to maintain a Map of case class objects, such that I can add new instances and look them up by an ID.
My current (very ugly) solution (stripped down):
case class cc(i: Int)
var myccmap: Map[Int, cc] = null
def addcc(thecc: cc): cc = {
if (myccmap == null) {
myccmap = Map(thecc.hashCode, thecc)
}
else {
myccmap = myccmap ++ Map(thecc.hashCode, thecc)
}
thecc
}
And then elsewhere I can use val somecc = addcc(cc(56)), for example, to maintain a Map of my cc objects added with addcc.
This way, I can store the key, which is just the hashCode, with some data in a file, and then when I read the file, I can extract the cc object by looking up the hashCode (which may or may not be present in myccmap).
Is there a better way to do this, ideally without relying on a check for null?
Your code can be simplified, just use a HashSet if you want to use the hash anyway:
import collection.immutable.HashSet
case class Cc(i: Int)
var myccmap: HashSet[Cc] = HashSet.empty
def addcc(thecc: Cc): thecc.type = {
myccmap += thecc
thecc
}
Also, by convention, classes should start with an uppercase letter. I also used a singleton-type as return value of addcc, this way it's clear that this function really just returns its parameter
Depends on whether you really want that key or not. I suppose the key of hashcode is still required because you store the key somewhere. And be noticed, don't use var in most of the cases, instead, use mutable map will help.
case class cc(i: Int)
val myccmap: mutable.Map[Int, cc] = mutable.Map.empty
def addcc(thecc: cc): cc = {
myccmap += (thecc.hashCode -> thecc)
thecc
}
Is there some idiomatic scala type to limit a floating point value to a given float range that is defined by a upper an lower bound?
Concrete i want to have a float type that is only allowed to have values between 0.0 and 1.0.
More concrete i am about to write a function that takes a Int and another function that maps this Int to the range between 0.0 and 1.0, in pseudo-scala:
def foo(x : Int, f : (Int => {0.0,...,1.0})) {
// ....
}
Already searched the boards, but found nothing appropriate. some implicit-magic or custom typedef would be also ok for me.
I wouldn't know how to do it statically, except with dependent types (example), which Scala doesn't have. If you only dealt with constants it should be possible to use macros or a compiler plug-in that performs the necessary checks, but if you have arbitrary float-typed expressions it is very likely that you have to resort to runtime checks.
Here is an approach. Define a class that performs a runtime check to ensure that the float value is in the required range:
abstract class AbstractRangedFloat(lb: Float, ub: Float) {
require (lb <= value && value <= ub, s"Requires $lb <= $value <= $ub to hold")
def value: Float
}
You could use it as follows:
case class NormalisedFloat(val value: Float)
extends AbstractRangedFloat(0.0f, 1.0f)
NormalisedFloat(0.99f)
NormalisedFloat(-0.1f) // Exception
Or as:
case class RangedFloat(val lb: Float, val ub: Float)(val value: Float)
extends AbstractRangedFloat(lb, ub)
val RF = RangedFloat(-0.1f, 0.1f) _
RF(0.0f)
RF(0.2f) // Exception
It would be nice if one could use value classes in order to gain some performance, but the call to requires in the constructor (currently) prohibits that.
EDIT : addressing comments by #paradigmatic
Here is an intuitive argument why types depending on natural numbers can be encoded in a type system that does not (fully) support dependent types, but ranged floats probably cannot: The natural numbers are an enumerable set, which makes it possible to encode each element as path-dependent types using Peano numerals. The real numbers, however, are not enumerable any more, and it is thus no longer possible to systematically create types corresponding to each element of the reals.
Now, computer floats and reals are eventually finite sets, but still way to large to be reasonably efficiently enumerable in a type system. The set of computer natural numbers is of course also very large and thus poses a problem for arithmetic over Peano numerals encoded as types, see the last paragraph of this article. However, I claim that it is often sufficient to work with the first n (for a rather small n) natural numbers, as, for example, evidenced by HLists. Making the corresponding claim for floats is less convincing - would it be better to encode 10,000 floats between 0.0 and 1.0, or rather 10,000 between 0.0 and 100.0?
Here is another approach using an implicit class:
object ImplicitMyFloatClassContainer {
implicit class MyFloat(val f: Float) {
check(f)
val checksEnabled = true
override def toString: String = {
// The "*" is just to show that this method gets called actually
f.toString() + "*"
}
#inline
def check(f: Float) {
if (checksEnabled) {
print(s"Checking $f")
assert(0.0 <= f && f <= 1.0, "Out of range")
println(" OK")
}
}
#inline
def add(f2: Float): MyFloat = {
check(f2)
val result = f + f2
check(result)
result
}
#inline
def +(f2: Float): MyFloat = add(f2)
}
}
object MyFloatDemo {
def main(args: Array[String]) {
import ImplicitMyFloatClassContainer._
println("= Checked =")
val a: MyFloat = 0.3f
val b = a + 0.4f
println(s"Result 1: $b")
val c = 0.3f add 0.5f
println("Result 2: " + c)
println("= Unchecked =")
val x = 0.3f + 0.8f
println(x)
val f = 0.5f
val r = f + 0.3f
println(r)
println("= Check applied =")
try {
println(0.3f add 0.9f)
} catch {
case e: IllegalArgumentException => println("Failed as expected")
}
}
}
It requires a hint for the compiler to use the implicit class, either by typing the summands explicitly or by choosing a method which is not provided by Scala's Float.
This way at least the checks are centralized, so you can turn it off, if performance is an issue. As mhs pointed out, if this class is converted to an implicit value class, the checks must be removed from the constructor.
I have added #inline annotations, but I'm not sure, if this is helpful/necessary with implicit classes.
Finally, I have had no success to unimport the Scala Float "+" with
import scala.{Float => RealFloat}
import scala.Predef.{float2Float => _}
import scala.Predef.{Float2float => _}
possibly there is another way to achieve this in order to push the compiler to use the implict class
You can use value classes as pointed by mhs:
case class Prob private( val x: Double ) extends AnyVal {
def *( that: Prob ) = Prob( this.x * that.x )
def opposite = Prob( 1-x )
}
object Prob {
def make( x: Double ) =
if( x >=0 && x <= 1 )
Prob(x)
else
throw new RuntimeException( "X must be between 0 and 1" )
}
They must be created using the factory method in the companion object, which will check that the range is correct:
scala> val x = Prob.make(0.5)
x: Prob = Prob(0.5)
scala> val y = Prob.make(1.1)
java.lang.RuntimeException: X must be between 0 and 1
However using operations that will never produce a number outside the range will not require validity check. For instance * or opposite.
I am trying to use the should matchers on a case class
case class ListOfByteArrayCaseConfig(
#BeanProperty
permissions: java.util.List[Array[Byte]]
)
With the following test case
val orig = ListOfByteArrayCaseConfig(List(Array[Byte](10, 20, 30)))
val orig2 = ListOfByteArrayCaseConfig(List(Array[Byte](10, 20, 30)))
orig2 should be === orig
Obviously this would fail because the two byte arrays are not equal reference wise. What I want to do is somehow make this work without changing the test case code and still keeping the case class.
Is it even possible? (like adding a custom equals method to the case class?)
I found the solution. Apparently I can override the equals method in a case class
Scala: Ignore case class field for equals/hascode?
Though it gets rid of the reason for using case classes in the first place which is to simplify data objects.
case class ListOfByteArrayCaseConfig(
#BeanProperty
permissions: java.util.List[Array[Byte]]
) {
override def equals(arg: Any): Boolean = {
val obj = arg.asInstanceOf[ListOfByteArrayCaseConfig]
var i: Int = 0
for (i <- 0 until permissions.size()) {
if (!util.Arrays.equals(permissions.get(i), obj.permissions.get(i))) {
return false
}
}
return true
}
}
I'm still learning Scala, but one thing I thought was interesting is that Scala blurs the line between methods and fields. For instance, I can build a class like this...
class MutableNumber(var value: Int)
The key here is that the var in the constructor-argument automatically allows me to use the 'value' field like a getter/setter in java.
// use number...
val num = new MutableNumber(5)
num.value = 6
println(num.value)
If I want to add constraints, I can do so by switching to using methods in place of the instance-fields:
// require all mutable numbers to be >= 0
class MutableNumber(private var _value: Int) {
require(_value >= 0)
def value: Int = _value
def value_=(other: Int) {
require(other >=0)
_value = other
}
}
The client side code doesn't break since the API doesn't change:
// use number...
val num = new MutableNumber(5)
num.value = 6
println(num.value)
My hang-up is with the named-parameter feature that was added to Scala-2.8. If I use named-parameters, my API does change and it does break the api.
val num = new MutableNumber(value=5) // old API
val num = new MutableNumber(_value=5) // new API
num.value = 6
println(num.value)
Is there any elegant solution to this? How should I design my MutableNumber class so that I can add constraints later on without breaking the API?
Thanks!
You can use the same trick that case classes do: use a companion object.
object Example {
class MutableNumber private (private var _value: Int) {
require (_value >= 0)
def value: Int = _value
def value_=(i: Int) { require (i>=0); _value = i }
override def toString = "mutable " + _value
}
object MutableNumber {
def apply(value: Int = 0) = new MutableNumber(value)
}
}
And here it is working (and demonstrating that, as constructed, you must use the object for creations, since the constructor is marked private):
scala> new Example.MutableNumber(5)
<console>:10: error: constructor MutableNumber cannot be accessed in object $iw
new Example.MutableNumber(5)
^
scala> Example.MutableNumber(value = 2)
res0: Example.MutableNumber = mutable 2
scala> Example.MutableNumber()
res1: Example.MutableNumber = mutable 0
Thanks for the answer! As an aside, I think the Scala-guys might be aware that there's an issue:
What's New in Scala 2.8: Named and Default Parameters
...
Until now, the names of arguments were a somewhat arbitrary choice for library developers, and weren't considered an important part of the API. This has suddenly changed, so that a method call to mkString(sep = " ") will fail to compile if the argument sep were renamed to separator in a later version.
Scala 2.9 implements a neat solution to this problem, but while we're waiting for that, be cautious about referring to arguments by name if their names may change in the future.
http://www.artima.com/scalazine/articles/named_and_default_parameters_in_scala.html
class MutableNumber {
private var _value = 0 //needs to be initialized
def value: Int = _value
def value_=(other: Int) {
require(other >=0) //this requirement was two times there
_value = other
}
}
you can modify all members of any class within curly braces
val n = new MutableNumber{value = 17}