I am facing some inconsistency in calling toString method for case-classes in Scala. The first code sample:
case class Person(name: String, age: Int)
val jim = Person("jim", 42)
println(jim)
output: Person(jim,42)
For the next code sample I used a case class that extends Exception:
case class JimOverslept(msg: String) extends Exception
try {
throw JimOverslept(msg = "went to bed late")
} catch {
case e: JimOverslept => println(e)
}
output: playground.CaseClassOutput$JimOverslept
Actually, I would prefer the output like JimOverslept(went to bed late)
What is the reason the both outputs are so different? And what is the best way to obtain the output looks like desired one (JimOverslept(went to bed late))
According to SLS 5.3.2 Case Classes
Every case class implicitly overrides some method definitions of class
scala.AnyRef unless a definition of the same method is already given
in the case class itself or a concrete definition of the same method
is given in some base class of the case class different from AnyRef.
Now toString is already provided by base class in
case class JimOverslept(msg: String) extends Exception
where Exception extends base Throwable which provides toString definition. Hence try providing an override within the case class itself like so
case class JimOverslept(msg: String) extends Exception {
override def toString: String = scala.runtime.ScalaRunTime._toString(this)
}
Related
I am currently using Enumeratum 1.5.13 in Scala 2.12.6. I have the following defined:
sealed abstract class PartOfSpeech(val value: Int) extends IntEnumEntry
case object PartOfSpeech extends IntEnum[PartOfSpeech] {
val values = findValues
case object Noun extends PartOfSpeech(0)
case object Adjective extends PartOfSpeech(1)
case object Verb extends PartOfSpeech(2)
case object Adverb extends PartOfSpeech(3)
case object Numeric extends PartOfSpeech(4)
case object Exclamation extends PartOfSpeech(5)
case object Preposition extends PartOfSpeech(6)
case object Pronoun extends PartOfSpeech(7)
case object Conjunction extends PartOfSpeech(8)
case object Determiner extends PartOfSpeech(9)
case object Article extends PartOfSpeech(10)
}
Then, when I attempt to use the withName() method like this:
val pos = PartOfSpeech.withName("Noun")
...I receive a compilation error indicating the method withName is not found. So, given I don't see a ScalaDoc for Enumeratum (at least I have not been able to find them), I don't know how to answer this without digging through its source code. Before investing time in that, I thought I might see if someone else has a simple and/or obvious solution.
What you are looking for was implemented on version 1.7.0 of Enumeratum.
you have 2 options to work with:
EnumEntry with "withName" function.
StringEnumEntry with "withValue" function.
In both ways you can get your needs
// Option 1:
sealed trait PartOfSpeech(val value: Int) extends EnumEntry
case object PartOfSpeech extends Enum[PartOfSpeech] {
val values = findValues
case object Noun extends PartOfSpeech(0)
case object Adjective extends PartOfSpeech(1)
case object Verb extends PartOfSpeech(2)
case object Adverb extends PartOfSpeech(3)
}
val pos = PartOfSpeech.withName("Noun") // return 0
// Option 2:
sealed trait PartOfSpeech(val value: String, val index: Int) extends StringEnumEntry
case object PartOfSpeech extends StringEnum[PartOfSpeech] {
val values = findValues
case object Noun extends PartOfSpeech("Noun", 0)
case object Adjective extends PartOfSpeech("Adjective", 1)
case object Verb extends PartOfSpeech("Verb", 2)
case object Adverb extends PartOfSpeech("Adverb", 3)
}
val pos = PartOfSpeech.withValue("None") // return 0
From what I can tell, the only way to make this work is for me to implement it myself within the case object. So, I added this:
private val enumEntryByClassSimpleNameLowerCase: Map[String, PartOfSpeech] =
values.map(enumEntry => (enumEntry.getClass.getSimpleName.takeWhile(_ != '$').toLowerCase, enumEntry)).toMap
def withName(value: String): Option[PartOfSpeech] =
enumEntryByClassSimpleNameLowerCase.get(value.toLowerCase)
It feels like a hack. I sure hope there's something inside of Enumeratum I am just missing. Please let me know if there is a better way to achieve this outcome. I use enums a whole bunch. So, this boilerplate is going to leak all over my codebase.
We can supply parameter to a class extending trait with the same name as an abstract method like
trait Trr{
def m: String
}
case class Trrrr(m: String) extends Trr //fine
This example compiles fine. But I tried to do something like that with case objects and failed:
trait Command{
def name: String
}
case object Unload("unld") extends Command //compile error
Is there a way to write this concisely while leaving Command a trait, not an abstract class with parameter? I mean not like that:
case object Unload extends Command {
override def name: String = "unld"
}
or
abstract class Command(name: String)
case object Unload extends Command("unld")
case object Unload extends Command { val name = "unld" }
Object don't have arguments, things won't get any shorted than the above...
You can instantiate the trait directly like so:
val newTrr = new Trr { val m = "example" }
At this point you can use the newTrr value just like any class instance...
println(newTrr.m)
which will print out: "example".
I've got a case class hierarchy to encode some request and processing errors:
sealed trait OpError
sealed trait RequestErrorType
sealed trait ProcessingErrorType
final case class InvalidEndpoint(reason: String) extends RequestErrorType
final case class InvalidParameters(reason: String) extends RequestErrorType
final case class InvalidFormat(response: String) extends ProcessingErrorType
final case class EntityNotFound(id: Long) extends ProcessingErrorType
final case class RequestError(errorType: RequestErrorType) extends OpError
final case class ProcessingError(errorType: ProcessingErrorType) extends OpError
If I write a simple match across all patterns:
def printMatches(error: OpError): Unit = error match {
case RequestError(InvalidEndpoint(reason)) => //print something
case RequestError(InvalidParameters(reason)) => //print something
case ProcessingError(InvalidFormat(format)) => //print something
case ProcessingError(EntityNotFound(entityId)) => //print something
}
the compiler gives me a warning about missing match:
match may not be exhaustive.
It would fail on the following input: ProcessingError(_)
def printMatches(error: OpError): Unit = error match {
But ProcessingError takes in a ProcessingErrorType with only two extensions: InvalidFormat and EntityNotFound, both which are accounted for in the pattern match. What am I missing?
Even more curious is that if I change the parameter type of InvalidParameters or InvalidEndpoint to a String*, I don't get the error:
final case class InvalidParameters(reason: String*) extends RequestErrorType
Any ideas?
This is a confirmed bug. I filed a bug report for this and it has been since fixed for Scala 2.12.0-M4.
Very interesting! Unfortunately, I haven't found an answer. I've been revolving around http://www.scala-lang.org/files/archive/spec/2.11/08-pattern-matching.html#constructor-patterns but I haven't really found a valid explanation for what's going on.
Here's a simpler demo (hope you don't mind):
sealed abstract class ClassOne
case class ClassOneImpl() extends ClassOne
sealed abstract class ClassTwo()
case class ClassTwoImpl() extends ClassTwo
sealed abstract class Foo
case class FooOne(x: ClassOne) extends Foo
case class FooTwo(x: ClassTwo) extends Foo
def printMatches(st: Foo): Unit = st match {
case FooOne(ClassOneImpl()) => println()
case FooTwo(ClassTwoImpl()) => println()
}
I've observed that each of the following two modifications removes the warning:
1) Changing FooOne and FooTwo signatures so that instead of taking ClassOne and ClassTwo they take ClassOneImpl and ClassTwoImpl
2) Removing FooOne or FooTwo so that there's only one case class extending Foo (which leads to only one case in pattern matching).
Perhaps we could submit an issue and see what they say?
You can help the compiler with an unchecked annotation:
... = (error: #unchecked) match ...
but you should be sure, your match is exhaustive.
I think exhaustiveness matching works on a single inheritance level.
RequestErrorType and ProcessingErrorType are part of the constructor where that exhaustiveness is not checked.
You can see it from the reading of the code, but it seem that compiler does not.
Given the code:
abstract class activity {
case class Fun(sports: String, eat: String) extends activity
val fun = Fun("soccer","apple")
}
What is the return type of fun?
fun is neither a function nor a method, so it has no return type. fun is of type Fun.
Interesting. It's a recursive dependency on activity class. The type should be Fun. But if you try to instantiate some concrete class and access the member:
abstract class activity {
case class Fun(sports: String,eat:String) extends activity
val fun = Fun("soccer","apple")
}
object Main extends App {
class concrete extends activity
val x = new concrete // StackOverflowError exception!
}
you get an StackOverflowError exception. Is this the expected behavior?
It's of type Fun
abstract class activity {
case class Fun(sports: String, eat: String) extends activity
val fun: Fun = Fun("soccer", "apple")
}
When you do Fun("soccer", "apple") it boils down to Fun.apply("soccer", "apple") and the default apply method of case classes companion object's returns an instance of the class, therefore it has return type Fun.
I am learning scala, so bear with me if this a stupid question. I am experimenting with case classes and trying out the following:-
case class A {
def eval(x: Int): Boolean
}
case class B extends A
case class C extends A {
override def eval(x: Int): Boolean = true
// Compiler error is -
// Multiple markers at this line
// - Missing closing brace `}' assumed here
// - expected start of definition
}
Is it not possible for case classes to implement abstract methods?
A case class cannot inherit another case class (the compiler does a lot of magic). In scala, you would typically use a trait, so your code would look like:
trait A {
def eval(x: Int): Boolean
}
case class C() extends A {
override def eval(x: Int): Boolean = true
}
abstract case class is a contradiction in terms. It is not advisable to extend from a case class and by making a case class abstract you are forced to sub-class it to have an instantiable class.
The common pattern is an abstract class or trait as the base with one or more (often more than one) case classes always at the leafs of the inheritance tree.