Let's say I have an enum Color containing the possible color-values for my program: red, blue and orange.
And then I would like to add methods that work on those colors. How can I add those methods in Scala?
enum Color {
case Red
case Blue
case Orange
}
I'm using Scala version 3.
I tried making a class that takes as parameter a value of the enum type Color, and contains the methods needed for the enum. I do however think that there is a better way to handle this case.
You can add methods directly to the enum.
enum Color {
case Red
case Blue
case Orange
def colorToInt: Int = this match {
case Red => ...
case Blue => ...
case Green => ...
}
// etc ...
}
Alternatively, each enum case can be given its own methods. Depending on how many enum cases you have, it can be neater to write code in this style.
enum Color {
case Red extends Color {
override def colorToInt: Int = ...
}
case Blue extends Color {
override def colorToInt: Int = ...
}
case Orange extends Color {
override def colorToInt: Int = ...
}
def colorToInt: Int // Note: abstract method
}
Enums and their cases are effectively syntax sugar for features that have already existed in Scala 2, such as case classes and sealed abstract classes. So conceptually, you can think of enum as defining a full-fledged abstract class and each case as defining a subclass. They can all have methods, the cases inherit methods from the enum, and the whole thing works like an OOP class hierarchy, because it actually is one.
You can read about the exact translations that take place on the original issue tracker for the feature. Effectively, every case gets compiled to either a case class or a val, depending on whether any custom methods are needed on it.
Related
Let's say I have some data in "dumb" models. In this example, I'll use Circle and Triangle, which extend a trait Shape.
I'm looking for a way to isolate behavior that could use these shapes, but I'm not sure the best way to structure it. If I'm trying to draw these shapes onto a document, I'd want to be able to write code that looked like this:
shapes.foreach(doc.add)
The trick here is that shapes is Seq[Shape], and the add method is something I want to add implicitly since I can't modify the shapes themselves (nor would I want to bake this specific functionality into them).
Where I'm getting stuck is, I don't know how to mix implicit conversions with the subclasses. See QUESTION: below in the code for more info.
// Let's assume I'm working with some shape models that are defined in some
// external library that's out of my control.
sealed trait Shape
case class Circle() extends Shape
case class Triangle() extends Shape
// Now I'm building an add that adds stuff to a Document
// and I want to locally implement methods that work on these general shapes.
case class Document()
// Using implicit conversion to add methods to a case class that's just holding data
implicit class DocumentExtensions(doc: Document) {
// I don't want this to be called
def add(shape: Shape): Unit = println("Add a shape")
// I want to use shape-specific methods
def add(shape: Circle): Unit = println("Add a circle")
def add(shape: Triangle): Unit = println("Add a triangle")
}
val doc = Document()
val shapes = Seq(Circle(), Triangle())
// This just prints "Add a shape" for the Circle and Triangle.
// I want to it to print "Add a circle" and "Add a triangle".
shapes.foreach { shape =>
// QUESTION:
// Is there a way or pattern to have this call the add for the
// subclass instead of for Shape? I want this to be fully dynamic
// so that I don't have to list out each subclass. Even more ideally,
// the compiler could warn me if there was a subclass that there wasn't
// an implicit add for.
doc.add(shape)
}
// This would work, but I'm wondering if there's a way to do this more
// dynamically without listing everything out.
shapes.foreach {
case c: Circle => doc.add(c)
case t: Triangle => doc.add(t)
}
I'm sure there's a name for what I'm looking for, but I just don't know what it is or what to search for.
Problem: compiler cannot choose and use an implicit value specific to handle a subclass. It's basically impossible to decide what method to call (for Triangle or Circle) when you only know that it's a Shape. This is actually a classical problem, which has standard solutions.
Solution 1
Pattern matching inside DocumentExtension.add
Pros:
Since your trait Shape is defined as sealed, compiler will you if you miss a case for a certain ancestor.
Separation of class definition and action handling
Cons:
Boilerplate required to list all subclasses of your trait
Solution 2
Classical Visitor pattern
sealed trait Shape {
def addToDoc(doc: Document, visitor: ShapeDrawer)
}
final class Triangle extends Shape {
def addToDoc(doc: Document, visitor: ShapeDrawer) = visitor.draw(doc, this)
}
final class Circle extends Shape {
def addToDoc(doc: Document, visitor: ShapeDrawer) = visitor.draw(doc, this)
}
trait ShapeDrawer {
def draw(doc: Document, t: Circle)
def draw(doc: Document, t: Triangle)
}
val drawer: ShapeDrawer = ???
val doc: Document = ???
val shapes = Seq.empty[Shape]
shapes.foreach(_.addToDoc(doc, drawer))
This solution also matches the requirement of being sure at compile time that you've handled every subclass of Shape, but requires adding strange methods to the trait itself.
Scala throws "reassignment to val" error for the following code.
abstract case class Gun(var bulletCount:Int)
class Pistol(bulletCount:Int) extends Gun(bulletCount){
def fire() { bulletCount=bulletCount-1 }
}
Anything I missed here?
For starters, you should consider case class as final, and not extend them.
Second, do not use var with case class, you should rather create a copy of a case class to get one of its field changed.
Third, if you want a common type, you can use a base trait.
All in one, here's what it could look like:
sealed trait Gun {
def bulletCount: Int
}
case class Pistol(bulletCount: Int) extends Gun {
def fire(): Pistol = copy(bulletCount=bulletCount)
}
You're referring to bulletCount field generated by Pistol primary constructor parameter. To set base class variable, you need to directly call field using super:
class Pistol(bulletCount: Int) extends Gun(bulletCount) {
def fire(): Unit = {
super.bulletCount = super.bulletCount - 1
}
}
Alternatively, you can label parameter-generated field with override var:
class Pistol(override var bulletCount: Int) extends Gun(bulletCount) {
def fire(): Unit = {
bulletCount = bulletCount - 1
}
}
On a side note, as Frederic A. suggested in his answer, you should avoid inheriting case classes. They are syntactic sugar, and code generation don't work over inheritance - you'll need to implement all the fancy stuff like apply or unapply methods in companion class all by yourself. Scala compiler team tried to support case class to case class inheritance, but discovered that it breaks structural equality and lots of other things.
What are the differences among these ways of defining Animal:
First way:
trait Animal {
def color: String
}
Second way:
trait Animal {
val color: String
}
Third way:
abstract class Animal(color: String) {}
Dog is a subclass of Animal. Consider the first way and the second way of defining Animal, what are the differences among the following ways of defining Dog:
First way:
case class Dog() extends Animal {
override def color:String = "black"
}
Second way:
case class Dog() extends Animal {
val color = "black"
}
Third way:
case class Dog(color: String) extends Animal {}
Forth way:
case class Dog(override val color: String) extends Animal(color) {}
Whoa, a lot to be answered here.
Regarding your first question, if you use a val then all subclasses must also use val. If you use def, subclasses can implement it either using def, val or lazy val. If color is a stable, immutable value, then declaring it as "val" in the trait makes sense since it imposes that all implementations in concrete subclasses will also be immutable.
The third way makes color only available in the constructor body and not visible from the outside. However, if you wrote
abstract class Animal(val color: String) {}
then it would be the same as the second way, only using abstract class instead of the trait. You could create a new animal and access its color attribute.
Regarding dog, defining color as def means that it will be computed every time it is invoked (i.e. when someone tries to access myDog.color). Defining it as val means that it will be an immutable value calculated once and for all when dog object is created. If it were a lazy val, then it would be calculated once and for all, but not when the dog is created, but when its color attribute is invoked (the calculation is postponed until the point of usage, hence the "lazy").
As I said above, if the Animal trait uses a val, then the Dog must also use a val. If Animal uses a def, then Dog can implement that as a def, val or lazy val.
Third way of writing a Dog is simply providing a parameter in case of writing an Animal with a class parameter (which was also third way in animal case). As I said earlier, in this case you cannot access the color attribute from the outside (that is, have val myDog = new Dog("blue") and access myDog.color).
Fourth way of writing a dog is implementing the Animal in case it was written in the way I have shown you above in the code (with using a val keyword). Now the color attribute will be visible. Override is not mandatory since you are implementing an abstract method, not overriding a concrete method, but you can leave it if you like (this way compiler will warn you if you, say, misspell "color" or someone removes the color from Animal class).
Perhaps this article can help too.
Is there a way to restrict the values of a parameter in a Scala function? For example, if I have one paramater called flag and I only want the user to be able to submit the values 0 or 1 as valid values for that parameter.
I know I could write a simple if statement that checked the values and returned an error message of some sort if it isn't acceptable, but I thought there might be a more succinct way to do it, say when the parameter is named in the function declaration.
What you want is "dependent typing". This sort of call would be a compile error in a language with support for it. Unfortunately scala doesn't support it.
Two typical workarounds would be to use an ADT instead of the larger type, or a wrapper with a restricted method of construction.
object ZeroOrOne {
def apply(i: Int): Option[ZeroOrOne] = if (i == 0 || i == 1) Some(ZeroOrOne(i)) else None
}
case class ZeroOrOne private (i: Int)
def doStuff(zo: ZeroOrOne) { // use zo.i }
or
sealed trait EnableStatus
case object Enabled extends EnableStatus
case object Disabled extends EnableStatus
def setEnabled(es: EnableStatus)
The way I would typically approach this in Scala is to make a base trait with case objects:
sealed trait Color
case object Red extends Color
case object Green extends Color
case object Blue extends Color
//...
def myFn(arg:Color) = //...
New to scala and trying to get the hang of the class system. Here's a simple set up:
sealed trait Shape{
def sides:Int
}
final case class Square() extends Shape {
def sides() = 4
}
final case class Triangle() extends Shape {
def sides() = 3
}
Now, I want to create a function that takes anything of type shape, which we know will have a sides() method implemented, and make use of that method.
def someFunction(a: Shape)={
val aShape = a()
aShape.sides()
}
But this hits an error at val aShape = a(), as there's no type a.
I realize that in this example, it's excessive to create someFunction, since sides() can be accessed directly from the objects. But my primary question is in the context of someFunction - I'd like to pass a class to a function, and instantiate an object of that class and then do something with that object. Thanks for your help.
What are you trying to do with that line of code? You already have a shape, the one passed in called a. Just remove that line and call a.sides().
Stylistically, there are several problems. First of all, class names should start with a capital letter. Second, sides seems like an immutable property, not a mutating method, so it should be declared and overridden with no parentheses. You also need override modifiers in your subclass. Last, you can do without the empty braces: {4} should just be 4.
There is several methods to do this. One is a complex one using reflection, second is little bit simplier, using a builder and third is most straightforward for your use case.
Just change definition of someFunction to
def someFunction(a: ()=>Shape)={
val aShape = a()
aShape.sides
}
so someFunction(Square) return 4 and someFunction(Triangle) returns 3 . Note this work only with case classes because real thing, we are passing here is not class itself, but it's auto-generated companion object
But more often there no need to define classes, you could write inside any context except top level thing just like
def square() = new Shape{
def sides() = 4
}
def triangle() = new Shape{
def sides() = 3
}
Next thing: methods with empty parameter list are generally reading as method that have side effects. So it is more convenient to define your type like
sealed trait Shape{
def sides:Int
}
and if you define your builders like
def square = new Shape{
def sides = 4
}
def triangle = new Shape{
def sides = 3
}
you should use them as someFunction(square _) telling, that you gonna use method call and not the value it's returning
And last thing is: if you really need the code, that creates some object, but it could contain complex computations, resource handling or some probable exception, so you want to hold over it's execution until it' really needed, you could use call-by-name parameters which is equivalent to R , which i assume you are familiar with
Unless shape has an apply function, you cannot call () on a shape object.
If you want to assign aShape to a, simply write val aShape = a.
But since I do not see the added value, you might as well call the sides function directly on a:
def someFunction(a:shape) = {
val sides = a.sides
// use sides
}
This is the closest translation for what you wrote:
sealed trait Shape {
def sides: Int
}
case object Square extends Shape {
override val sides = 4
}
case object Triangle extends Shape {
override val sides = 3
}
def someFunction(a: Shape) =
val shapeSides = a.sides
Some notes:
Classes in scala should be CamelCase
Your subclasses have no instance members, so you can use a singleton object
You if you have a: Shape it means that a is a Shape, and you haven't defined anything that would let you call () on it.
You can omit braces when there's only one expression inside
You can override a def with val if it's static