Scala case class vs object: how is it instantiated? - scala

Some context so that this question actually makes some sense.
I have a trait from which I extend a case class and an object.
I have a method that pattern matches to decide which to pull based on given conditions.
When I pattern match and get the case class, everything works fine. But when I pattern match and get the object, an error is thrown.
Within the trait, I have methods that call the DB. And I also have vals that need overriding.
The curious thing is the error that is thrown when it the object is instantiated, is a DB specific error
java.sql.SQLInvalidAuthorizationSpecException: (conn=41) Transaction characteristics can't be changed while a transaction is in progress
Which led me on wild goose chase to hunt down whether the code was opening up another session unknowingly etc. But having checked my code, I knew I was passing around the one DB connection I had started and there wasn't any strange behaviour.
I figured, since my case class instance works, I wanted to see if converting my object into a case class would change anything.
Having converted my object to a case class, I then got an NPE error thrown which pointed me directly to the errant line of code that was causing problems. I then changed my vals to def in the trait and everything works fine.
So my questions is: Why is it that when I converted the object to the case class, it then threw up the error that was the true culprit. Which is why I asked the question: how is a case class vs object instantiated? Because I suspect this was the cause of the misleading error being thrown.
Feel free to correct my assumption if it is wrong.

Here is the crucial line:
I then changed my vals to def in the trait and everything works fine.
The problem was almost certainly caused by an abstract val in the trait being used in the constructor before it had been initialised by the subclass.
As a general rule, abstract values in a trait should always be def.

Related

Why does spray-json apply this hierarchy way in RootJsonFormat?

Recently, I am reading the source code of Spray-json. I noted that the following hierarchy relation in JsonFormat.scala, please see below code snippet
/**
* A special JsonFormat signaling that the format produces a legal JSON root
* object, i.e. either a JSON array
* or a JSON object.
*/
trait RootJsonFormat[T] extends JsonFormat[T] with RootJsonReader[T] with RootJsonWriter[T]
To express the confusion more convenient, I draw the following diagram of hierarchy:
According to my limited knowledge of Scala, I think the JsonFormat[T] with should be removed from the above code. Then I cloned the repository of Spary-json, and comment the code JsonFormat[T] with
trait RootJsonFormat[T] extends RootJsonReader[T] with RootJsonWriter[T]
Then I compile it in SBT(use package/compile command) and it passed to the compiling process and generates a spray-json_2.11-1.3.4.jar successfully.
However, when I run the test cases via test command of SBT, it failed.
So I would like to know why. Thanks in advance.
I suggest you to not think of it in terms of OOP. Think of it in terms of type classes. In case when some entity must be serialized and deserialized at the same time, there is a type class JsonFormat that includes both JsonWriter and JsonReader. This is convenient since you don't need to search for 2 type class instances when you need both capabilities. But in order for this approach to work, there has to be an instance of JsonFormat type class. This is why you can't just throw it away from hierarchy. For instance:
def myMethod[T](t: T)(implicit format: JsonFormat[T]): Unit = {
format.read(format.write(t))
}
If you want this method to work properly there has to be a direct descendant of JsonFormat and a concrete implicit instance of it for a specific type T.
UPD: By creating an instance of the JsonFormat type class, you get instances for JsonWriter and JsonReader type classes automatically (in case when you need both). So this is also a way to reduce boilerplate.

How to copy a case class and add a mixin?

I'd like to have a base case class I can extend at will into different types. I had something like this working previously, but I must have screwed something up, because now I'm getting compile errors.
trait JobLike
case class Task(name: String) {
def as[T <: JobLike]: Task with T = new Task(this.name) with T
}
The problem is, this gives me a compiler error like this:
java.lang.UnsupportedOperationException: addChild inapplicable for type T
at scala.reflect.internal.Symbols$Symbol.addChild(Symbols.scala:1835)
at scala.tools.nsc.typechecker.Namers$Namer.$anonfun$templateSig$1(Namers.scala:1119)
at scala.tools.nsc.typechecker.Namers$Namer.templateSig(Namers.scala:1107)
at scala.tools.nsc.typechecker.Namers$Namer.classSig(Namers.scala:1178)
at scala.tools.nsc.typechecker.Namers$Namer.memberSig(Namers.scala:1788)
at scala.tools.nsc.typechecker.Namers$Namer.typeSig(Namers.scala:1751)
at scala.tools.nsc.typechecker.Namers$Namer$MonoTypeCompleter.completeImpl(Namers.scala:836)
...
at scala.tools.nsc.MainGenericRunner.runTarget$1(MainGenericRunner.scala:82)
at scala.tools.nsc.MainGenericRunner.run$1(MainGenericRunner.scala:85)
at scala.tools.nsc.MainGenericRunner.process(MainGenericRunner.scala:96)
at scala.tools.nsc.MainGenericRunner$.main(MainGenericRunner.scala:101)
at scala.tools.nsc.MainGenericRunner.main(MainGenericRunner.scala)</code>
I swear, I had something working yesterday that basically did just this, but now it's throwing this error.
You managed to hit a compiler bug! While that's certainly an issue, and should be reported, the thing is that your code isn't possible anyway, and it's better that it's crashing and bringing you here than "working" and doing something horribly wrong. Because T can be any subtype of JobLike, there's simply no way to mix it in at compile time. I could very well do trait Boom extends JobLike { def something: Int }; task.as[Boom], and then task couldn't create a Boom because there's no implementation for something.
You can provide asJobLike: Task with JobLike instead of a general as[T]: Task with T, which is completely possible. (Note that it's discouraged to make subtypes of case classes, so you may want to create a new class. The reason is that it breaks the reflexivity of equals.)
Something rather filthy that you can do is call (new Task(this.name) with JobLike).asInstanceOf[Task with T]. asInstanceOf only checks the cast for the leftmost type in the with, so this will not ClassCastException off the bat (you can also do "a".asInstanceOf[String with Int].). It will generally sorta work (modulus asInstanceOf[T] being false) as long as T has no members.
Something horrible that you can also do is use runtime reflection to dynamically create the required class. The trick as a whole is filthy and completely unsafe, so I won't say it here, but have a link.

Case object extending class with constructor in Scala

I am a beginner in Scala and was playing around to learn more about Abstract data types. I defined the following definition to replicate Option type:
sealed abstract class Maybe[+A](x:A)
case object Nothing extends Maybe[Nothing](Nothing)
case class Just[A](x:A) extends Maybe[A](x)
But I encountered the following error.
found : Nothing.type
required: Nothing
case object Nothing extends Maybe[Nothing](Nothing)
How do I pass Nothing instead of Nothing.type?
I referred to the following question for hints:
How to extend an object in Scala with an abstract class with constructor?, but it was not helpful.
Maybe more like this. Your Nothing shouldnt have a value, just the type. Also people usually use traits instead of abstract classes.
sealed trait Maybe[+A]
case object None extends Maybe[Nothing]
case class Just[A](x:A) extends Maybe[A]
You probably shouldnt create your own Nothing, thats going to be confusing, you will confuse yourself and the compiler about if you are referring to your one, or the one at the bottom of the type hierarchy.
As mentioned by Stephen, the correct way to do this would be not to have trait and not an abstract class, however, I thought it might be informative to explain why the current methodology fails and how to fix it.
The main issue is with this line:
case object Nothing extends Maybe[Nothing](Nothing)
First thing (as mentioned) you shouldn't call your object Nothing. Secondly, you set the object to extend Maybe[Nothing]. Nothing can't have any actual values so you can't use it as an object. Also, you can't use the object itself as the constructor parameter because that would cause a cyclic behavior.
What you need is to have a bottom type (i.e. a type which all A have in common) and an object of that type. Nothing is a bottom type but has no objects.
A possible solution is to limit yourself to AnyRef (i.e. nullable objects) and use the Null bottom type which has a valid object (null):
sealed abstract class Maybe[+A <: AnyRef](x:A)
case object None extends Maybe[Null](null)
This is a bit of clarification for Assaf Mendelson's answer, but it's too big for a comment.
case object Nothing extends Maybe[Nothing](Nothing)
Scala has separate namespaces for types and values. Nothing in case object Nothing is a value. Nothing in Maybe[Nothing] is a type. Since you didn't define a type called Nothing, it refers to the automatically imported scala.Nothing and you must pass a value of this type as an argument. By definition it has no values but e.g. case object Nothing extends Maybe[Nothing](throw new Exception) would compile, as the type of throw expressions is Nothing. Instead you pass the value Nothing, i.e. the same case object you are defining; its type is written as Nothing.type.
How do I pass Nothing instead of Nothing.type?
It seems like there is no way to do so.
As it says at http://www.scala-lang.org/api/2.9.1/scala/Nothing.html:
there exist no instances of this type.

If parameter-less case classes without parentheses are deprecated in Scala, why does Akka still allow them to be passed?

I spent two days trying to debug an issue where a message wasn't being handled by an actor, ultimately to find out the message being sent to it didn't have the parentheses following its name. If parameter-less case classes without parentheses are deprecated in Scala, why does Akka still allow them to be sent without warning?
I guess that the problem here isn't that the companion object is not Serializable. You would get warning anyway - and an exception if you use akka remoting. The problem probably was that the receive function in the receieving actor doesn't have a case for the companion object.
Akka simply doesn't care which messages are you sending as you can send any object as a message. Doesn't make sense to me to have a check that you shouldn't be sending a deprecated class.
I'm guessing here but sounds like what you were sending was the companion object of the case class and not the case class itself. The case class would have been serializable by default (all case classes are serializable if constructed from serializable components) but the companion is not guaranteed to be serializable.

How do I force the Scala compiler to tell me when my class is abstract?

Why is the "abstract" keyword for class definition optional in Scala, and how do I force the Scala compiler to tell me when my class is abstract?
Here an example that I wrote in Eclipse:
class Toto[T] {
def get(index: Int): T
}
object Toto {
def create[T]: Toto[T] = new Toto[T]
}
This seems to be a perfectly valid class definition in Scala, although it does NOT define the required get method, and is NOT prefixed with abstract. If you don't need the abstract keyword, then why does it exist? And if you want to be told that your class is actually abstract, how do you get the compiler to tell you?
This is not valid scala code, abstract is required, and instanciation forbidden. From the spec (5.2, p63):
The abstract modifier is used in class
definitions. It is redundant for
traits, and mandatory for all other
classes which have incomplete members.
Ab- stract classes cannot be
instantiated (ยง6.10) with a
constructor invocation unless
followed by mixins and/or a refinement
which override all incomplete members
of the class. Only abstract classes
and traits can have abstract term
members.
The code produces an error in the REPL : error: class Toto needs to be abstract, since method get is not defined
I get the proper behavior with the same message in Eclipse too. You should check whether you get the same error with and without eclipse. Whichever is true, I guess if you have exactly the code you posted without an error (does it run?), a bug repport will be warranted.
To answer my own question: In Eclipse, you can only tell if a class is correct if all other classes compile without errors! In other word, you can't trust anything Eclipse says about a class unless there are no errors in other classes.
So if you have errors in several classes, then there is no way of knowing which ones are the real errors, and neither if a class without errors is correct.
You just have to repeatedly loop on the errors, fixing any one that makes sense, and hoping the others errors that don't make sense are eventually going to just disappear.