What is the meaning of the word "case" in scala's "case class"? - scala

I understand that case class causes the compiler to augment a class with boilerplate to implement a certain useful pattern ("plain and immutable data-holding objects that should exclusively depend on their constructor arguments").
But the word "case" itself has no meaning to me in this context. I'm accustomed to its use as part of switch statements in C#, but that seems to have no relevance to Scala's use of the word.
I find it easier to program when I can attach words to particular meanings. Right now my mental model is case => boilerplate, in the same way that it could be blurg => boilerplate. It's a working mental model, but ambiguity makes it easy to misunderstand or to forget altogether.
So what does the word case have to do with what the compiler actually does?
I'm not looking for "what was in the mind of the designers" but rather "what's a rational way to relate this term to its meaning, given general knowledge of the language's design."

In my opinion, the term case comes from case analysis which is a reasoning technique enabled by special structures called algebraic data types. By itself case in case class might not make much sense, but when it forms a part of a sealed structure, which is how Scala defines ADTs, for example
sealed trait Number
case object Zero extends Number
case class Succ(v: Number) extends Number
then we see there are two forms of constructing Numbers, namely using Zero and Succ constructors. Hence whenever we have to think about Numbers, we at least know there are two different cases to consider. For example, say we want to define addition on Numbers, then its definition will have to handle two cases, perhaps, like so
def sum(a: Number, b: Number): Number =
a match {
case Zero => b
case Succ(v) => Succ(sum(v, b))
}
where
sum(Succ(Zero), Succ(Zero)) == Succ(Succ(Zero)) // 1 + 1 = 2
sum(Succ(Succ(Zero)), Succ(Zero)) == Succ(Succ(Succ(Zero))) // 2 + 1 = 3
sum(Succ(Zero), Succ(Succ(Zero))) == Succ(Succ(Succ(Zero))) // 1 + 2 = 3
sum(Zero, Succ(Succ(Zero))) == Succ(Succ(Zero)) // 0 + 2 = 2
sum(Succ(Succ(Zero)), Zero) == Succ(Succ(Zero)) // 2 + 0 = 2
Note how Scala in order to define ADT uses terms like class, object, trait etc., which appear to be from the object-oriented paradigm, however ADTs conceptually have little in common with class hierarchies found in OO. Personally I find this confusing, but we must remember Scala is both functional and OO language, which might be a reason for such terminological spillover. In some other more "pure" languages case class of ADT is represented simply by a vertical bar |, say like so
Inductive nat : Type :=
| O : nat
| S : nat -> nat.
My suggestion would be to try not to be a "slave to words" but instead words should serve you. What is important is the meaning behind the words or terms, not the words themselves. Do not build mental models around the terms, instead build mental models around the heavy concepts they are struggling to carry across feeble bridges of communication. In my opinion, the concept case is trying to communicate is that of ADT.

C# has a switch / case language feature which allows controlling program flow by matching an input to a set of possible values.
public static GetEmail(string name)
{
switch (name)
{
case "bill":
return "bill#example.com";
case "jane":
return "jane#example.com";
default:
return null;
}
}
Here, case roughly means "in the case that the given value is equal to this one, do X."
Scala's match / case feature is sort of like C#'s feature.
def getEmail(name: String): Option[String] = {
name match {
case "bill" => Option("bill#example.com")
case "jane" => Option("jane#example.com")
case _ => None
}
}
But it's much more powerful. It is designed to evaluate "matches" against things farm more complex than strings. To take advantage of this powerful matching feature, you define immutable data-holding classes with case class.
Here is a trivial, but hopefully helpful, example of a case class and its use with match / case:
case class Person(name: String, hasEmail: Boolean)
object EmailHelper {
def getEmail(person: Person): Option[String] = {
person match {
case Person(_, false) => None
case Person("bill", true) => Option("bill#example.com")
case Person("jane", true) => Option("jane#example.com")
case _ => None
}
}
}
In short, case class enforces a set of constraints which make a class usable with match / case.

Related

Scala match case with multiple branch with if

I have a match case with if and the expression is always the same.
I put some pseudo code:
value match {
case A => same expression
case B(_) if condition1 => same expression
case _ if condition2 => same expression
...
case _ => different expression //similar to an else
}
The match contains both case object (case A) matching and case class(case B(_))
Is it the best practice?
Try to explain this code in words. "This function returns one of two values. The first is returned if the input is A. Or if the input is of type B and a condition holds. Oh, or if a different condition holds. Otherwise, it's the other value". That sounds incredibly complex to me.
I have to recommend breaking this down at least a bit. At minimum, you've got two target expressions, and which one is chosen depends on some predicate of value. That sounds like a Boolean to me. Assuming value is of some trait type Foo (which A.type and B extend), you could write
sealed trait Foo {
def isFrobnicated: Boolean = this match {
case A => true
case B(_) if condition1 => true
case _ => condition2
}
}
...
if (value.isFrobnicated) {
same expression
} else {
different expression
}
Now the cognitive load is split into two different, smaller chunks of code to digest, and presumably isFrobnicated will be given a self-documenting name and a chunk of comments explaining why this distinction is important. Anyone reading the bottom snippet can simply understand "Oh, there's two options, based on the frobnication status", and if they want more details, there's some lovely prose they can go read in the isFrobnicated docs. And all of the complexity of "A or B if this or anything if that" is thrown into its own function, separate from everything else.
If A and B don't have a common supertype that you control, then you can always write a standalone function, an implicit class, or (if you're in Scala 3) a proper extension method. Take your pick.
Depending on your actual use case, there may be more that can be done, but this should be a start.

'with' keyword usage in scala with case classes

I though if something like this makes sense in scala:
object CaseClassUnion extends App {
case class HttpConfig(bindUrl: String, port: String)
case class DbConfig(url: String, usr: String, pass: String)
val combined: HttpConfig with DbConfig = ???
//HttpConfig("0.0.0.0", "21") ++ DbConfig("localhost", "root", "root")
//would be nice to have something like that
}
At least this compiles... Is there a way, probably with macros magic to achieve union of two classes given their instances?
In zio I believe there is something like in reverse:
val live: ZLayer[ProfileConfiguration with Logging, Nothing, ApplicationConfiguration] =
ZLayer.fromServices[ProfileConfigurationModule.Service, Logger[String], Service] { (profileConfig, logger) => ???
where we convert ProfileConfiguration with Logging to function of ProfileConfigurationModule.Service, Logger[String] => Service
Several things.
When you have several traits combined with with Scala does a trait linearization to combine them into one class with a linear hierarchy. But that's true for traits which doesn't have constructors!
case class (which is not a trait) cannot be extended with another case class (at all) because that would break contracts like:
case class A(a: Int)
case class B(a: Int, b: String) extends A(a)
A(1) == B(1, "") // because B is A and their respective fields match
B(1, "") != A(1) // because A is not B
B(1, "").hashCode != A(1).hashCode // A == B is true but hashCodes are different!
which means that you cannot even generate case class combination manually. You want to "combine" them, use some product: a tuple, another case class, etc.
If you are curious about ZIO it:
uses traits
uses them as some sort of type-level trick to represent an unordered set of dependencies, where type inference would calculate set sum when you combine operations and some clever trickery to remove traits from the list using .provide to remove dependency from the set
ZLayers are just making these shenanigans easier
so and if you even pass there some A with B you either combined it yourself by using cake pattern, or you passed dependencies one by one. ZIO developer might never be faced with the problem of needing some macro to combine several case classes (.provide(combineMagically(A, B, C, D, ...)) as they could pass implementations of each dependency one by one (.provide(A).provide(B)) and the code underneath would never need the combination of these types as one value - it's just a compile-time trick that might never translate to the requirement of an actual value of type A with B with C with D ....
TL;DR: You cannot generate a combination of 2 case classes; ZIO uses compound types as some sort of type-level set to trace dependencies and it doesn't actually require creating values of the compound types.

Enumeration concept in Scala - Which option to take?

Scala programmers have a few options when defining enumerations:
Use Scala Enumeration
Mimic enumerations using Scala sealed case objects.
Use Scalaz Enum
Use Java Enumeration
When researching the best practices around enumerations in Scala, I stumbled upon the Google post entitled Enumerations must DIE and also this blog which highlights a potential problem using the Scala Enumeration class. Both these references have cast a negative shadow over the Scala Enumeration class.
Option 2 seems like a lot of work and with regards to Option 3, I have not used the Scalaz library as of yet so I would interested to know the experience others have had with using Scalaz Enum. The final option is to inter-op with Java which I try to avoid since I like to take a purist approach in my Scala programming.
The point of this post is to leverage the community experience in order to detail the context(s) when one option would be preferred over another and also in which context(s) would using a particular option be wrong or likely to cause serious issues, so that an informed decision can be made in choosing one option over another. I am not looking for opinions but rather concrete use context(s) when one option is better than the other(s); opinions are likely to get this post closed down so please avoid that.
I have used both of the first two options in the past depending on the circumstances. I can't speak for the other options but I would be reluctant to use Java Enums. Generally I will always prefer a Scala solution over a Java solution where one is available. I would also be reluctant to introduce a library just for Enumerations. It seems a bit heavy to introduce a large library to accomplish such a small task, especially when there are built in ways to accomplish that task. It might be different though if the library was offering other features that I wanted which were not built in.
Part of it depends on what you need the enumeration to accomplish. If you need it just to create a set of discrete values, then I would lean towards Option 2. It really isn't much work at all. You can make it more complex if your needs require it, but the most basic scenario is:
trait MyEnum
case object MyValue1 extends MyEnum
case object MyValue2 extends MyEnum
If on the other hand you need something that actually provides you with an "ordered" set of discrete values that you can iterate over, obtain numeric values for etc, then I might lean more towards the Scala Enumeration.
Scalaz Enum
Enumeration concept in Scalaz is modelled on the Haskell Enum
Provides useful operations on sequentially ordered types
Develop rich enumerations through sealed case classes
Iterate over those sealed case classes in a type-safe manner
More analysis is required on the part of implementer to define the notion of order for the type that is sought to be enumerated.
Required to define succ and pred functions
Required to override the order function from the Order type class.
Example
import scalaz.Ordering.{EQ, GT, LT}
import scalaz.{Enum, Ordering, Show}
sealed abstract class Coloring(val toInt: Int, val name: String)
object Coloring extends ColoringInstances {
case object RED extends Coloring(1, "RED")
case object BLUE extends Coloring(2, "BLUE")
case object GREEN extends Coloring(3, "GREEN")
}
sealed abstract class ColoringInstances {
import Coloring._
implicit val coloringInstance: Enum[Coloring] with Show[Coloring] = new Enum[Coloring] with Show[Coloring] {
def order(a1: Coloring, a2: Coloring): Ordering = (a1, a2) match {
case (RED, RED) => EQ
case (RED, BLUE | GREEN) => LT
case (BLUE, BLUE) => EQ
case (BLUE, GREEN) => LT
case (BLUE, RED) => GT
case (GREEN, RED) => GT
case (GREEN, BLUE) => GT
case (GREEN, GREEN) => EQ
}
def append(c1: Coloring, c2: => Coloring): Coloring = c1 match {
case Coloring.RED => c2
case o => o
}
override def shows(c: Coloring) = c.name
def zero: Coloring = Coloring.RED
def succ(c: Coloring) = c match {
case Coloring.RED => Coloring.BLUE
case Coloring.BLUE => Coloring.GREEN
case Coloring.GREEN => Coloring.RED
}
def pred(c: Coloring) = c match {
case Coloring.GREEN => Coloring.BLUE
case Coloring.BLUE => Coloring.RED
case Coloring.RED => Coloring.GREEN
}
override def max = Some(GREEN)
override def min = Some(RED)
}
}
Example Output:
val f = Enum[Coloring]
println(f.fromToL(Coloring.RED, Coloring.GREEN))
res1 : List(RED, BLUE, GREEN)
You can also use Enumeratum. Description taken from their documentation:
Enumeratum is a type-safe and powerful enumeration implementation for
Scala that offers exhaustive pattern match warnings, integrations with
popular Scala libraries, and idiomatic usage that won't break your
IDE. It aims to be similar enough to Scala's built in Enumeration to
be easy-to-use and understand while offering more flexibility,
type-safety and richer enum values without having to maintain your
own collection of values.

How to account for all cases of an enum on the right-hand side of a pattern match

Exhaustive pattern matching is great, but it only appears to work on the left-hand side of the case (=>) operator.
I am curious if there is a way that a person can verify that the output of a function (or expression) can be bound to that enumeration. The goal would be to have the compiler tell me when I forget to output an item from the enumeration.
In my example, I make use of the following enumeration containing three items:
object MyEnum {
sealed trait t
case object E1 extends t
case object E2 extends t
case object E3 extends t
}
And here is a pattern-match expression that would produce a compile-time warning (as we already know):
def foo( e : MyEnum.t ) : Boolean =
e match {
case MyEnum.E1 => true
case MyEnum.E2 => false
case MyEnum.E3 => true // if we leave this line out, we get a warning
}
Scala would complain if we left MyEnum.E3 out of the pattern matching expression, citing a non-exhaustive pattern match. This is profoundly beneficial, but I wonder if the reverse is possible.
Can we account for all cases of MyEnum.t on the right-hand side of =>?
Here is an example that highlights this:
def bar( s : String ) : Option[MyEnum.t] =
s match {
case "a" => Some(MyEnum.E1)
case "b" => Some(MyEnum.E2)
case "c" => Some(MyEnum.E3) // if we leave this out, no warning
case _ => None
}
In this example, if we leave out the line with MyEnum.E3, then the compiler just carries on as if nothing is wrong. The assertion I would like to make, is:
forall MyEnum.t (aliased as e) there exists Some(e) | None
I understand that this could be easily covered by a run-time test, but I'm curious if there is a way to check this statically.
Thanks.
I am going to push my luck and claim it is not possible (let's keep this a secret from Odersky). If the Scala compiler was able to detect such situations, I think it would be even slower than it already is ;)
The only way I could see is to define foo similarly to how you have done, and define bar by making it iterate over foo to make a reverse map, but that doesn't make much sense to me, and might not work in your specific case.
I think your case is a very good example for when unit tests are useful, so why not just write one?
No, it is not possible, because this is equivalent to the Halting Problem, which was proven unsolvable in 1936.

How to pattern match multiple values in Scala?

Let's say I want to handle multiple return values from a remote service using the same code. I don't know how to express this in Scala:
code match {
case "1" => // Whatever
case "2" => // Same whatever
case "3" => // Ah, something different
}
I know I can use Extract Method and call that, but there's still repetition in the call. If I were using Ruby, I'd write it like this:
case code
when "1", "2"
# Whatever
when "3"
# Ah, something different
end
Note that I simplified the example, thus I don't want to pattern match on regular expressions or some such. The match values are actually complex values.
You can do:
code match {
case "1" | "2" => // whatever
case "3" =>
}
Note that you cannot bind parts of the pattern to names - you can't do this currently:
code match {
case Left(x) | Right(x) =>
case null =>
}
The other answer correctly says that currently there is no way to pattern-match multiple alternatives while extracting values at the same time.
I'd like to share a coding pattern with you that comes close to doing this.
Scala allows you to pattern-match alternatives without extracting values, e.g. case Dog(_, _) | Cat(_, _) => ... is legal. Using this, you can simply extract the values yourself within the case block.
Here's a somewhat contrived example:
abstract class Animal
case class Dog(age: Int, barkLevel: Int) extends Animal
case class Cat(apparentAge: Int, cutenessLevel: Int) extends Animal
val pet: Animal = Dog(42, 100)
// Assume foo needs to treat the age of dogs and the apparent age
// of cats the same way.
// Same holds for bark and cuteness level.
def foo(pet: Animal): Unit = pet match {
case animal#(Dog(_, _) | Cat(_, _)) =>
// #unchecked suppresses the Scala warning about possibly
// non-exhaustiveness even though this match is exhaustive
val (agelike, level) = (animal: #unchecked) match {
case Dog(age, barkLevel) => (age, barkLevel)
case Cat(apparentAge, cutenessLevel) => (apparentAge, cutenessLevel)
}
???
}
Assume that ??? actually stands for doing something that is equal for dogs and cats. Without this coding pattern, you would need to have two cases, one for dogs and one for cats, forcing you to duplicate code or at least to outsorce code into a function.
Generally, the coding pattern above is suitable if you have sibling case classes that share fields that behave identically only for some algorithms. In those cases, you cannot extract those fields to a common superclass. Still, you would like to pattern-match in a uniform way on those fields in the algorithms that treat them equally. This you can do as shown above.