scala case object pollution - scala

Let me explain ;-)
both classes below are in package com.company.foo
RoleGroup.scala
abstract class RoleGroup
case object ADMIN extends RoleGroup
case object MEMBER extends RoleGroup
MailSender.scala
abstract class MailSender
case object ADMIN extends MailSender
case object STAFF extends MailSender
case object ACCOUNTANT extends MailSender
The problem here is that ADMIN is ambiguous since there is no namespace separation with case objects. It seems there can only be one uniquely named case object per package.
I suppose I could name case objects based on function a la mailADMIN, roleADMIN and so on. Or should I just create proper Enums and forget about case objects? Or take some other approach?

you could do something like:
object RoleGroup {
sealed trait RoleGroup
case object ADMIN extends RoleGroup
case object MEMBER extends Rolegroup
}
and similarly for MailSender. Then in situations where you're only using one, you can do import MailSender._ or vise versa, but when using both you refer to them as RoleGroup.ADMIN, etc.
Whether you want to take this approach or use Enums mostly depends on how you intend to use them. In this aproach, each "enum" is a type, whereas with Enums each enum is a value. The former works better for pattern matching since the compiler can check if your matches are exhaustive, the latter is better (IMO) for working with serialization.

You don't need enums here, as much as probably anywhere else. All you need is proper namespacing. I find the companion object approach to be the most benefitial:
sealed abstract class RoleGroup
object RoleGroup {
case object Admin extends RoleGroup
case object Member extends RoleGroup
// also just in case
sealed case class SomeParameterizedGroup (
param1: Any,
param2: Int
) extends RoleGroup
}
Using it this way will very much remind you of Java's enums, while also providing you with strengths of Scala:
def foo (roleGroup: RoleGroup) =
roleGroup match {
case RoleGroup.Admin =>
case RoleGroup.SomeParameterizedGroup(param1, param2) =>
}
Please note that in Dan's solution the type of roleGroup would be RoleGroup.RoleGroup, which kinda feels unnatural. Also note that uppercase names violate Scala's style conventions and from examples above you can kinda see why.

Related

How to inherit generic case class to reuse its properties?

How to properly implement this in Scala?
case class WithPagingJson[T](meta: PagingMetaJson, objects: Array[T])
case class CandidatesListJson extends WithPagingJson[CandidateJson]
case class OpeningsListJson extends WithPagingJson[OpeningJson]
As you see, these classes are models for JSON data received from various APIs.
They all have same basic structure with two top-level properties for paging info and for array of requested objects.
Compiler tells me case to case inheritance is prohibited.
What is a proper Scala way of doing this?
If CandidatesJson really doesn't add anything to WithPagingJson, it can be a type alias:
type CandidatesJson = WithPagingJson[CandidateJson]
Otherwise, does WithPagingJson really need to be a case class? If you make it abstract and all its subtypes case classes, then you effectively still get all the equality, hash code, etc. benefits.
Would you mind showing the code for inheriting from abstract class?
abstract class WithPagingJson[T](val meta: PagingMetaJson, val objects: Array[T])
case class CandidatesJson(override val meta: PagingMetaJson, override val objects: Array[CandidateJson]) extends WithPagingJson[CandidateJson](meta, objects)
it seems I will have to exactly repeat whole parameters list for case class anyways? I actually was hoping to avoid exactly that.
If they are the same, yes, but they don't have to be. Maybe you want to make the base type a trait instead
trait class WithPagingJson[T] {
val meta: PagingMetaJson
val objects: Array[T]
}
case class CandidatesJson(meta: PagingMetaJson, objects: Array[CandidateJson]) extends WithPagingJson[CandidateJson]

Scala "or" in generic bounds

I know that I can write something like this:
case class Something[T <: Foo with Bar](state: T)
This accepts classes which have the traits (or class and trait) Foo and Bar. This is an AND example where it is needed to extend both Foo and Bar. Is there an option which allows me to pass classes extending Foo OR Bar to pattern match against them?
The use case is that I have multiple Classes with different behaviors which consume states which are of shared types:
trait FooState
trait BarState
trait BazState
case class Foo(state: FooState) // must not accept BarState or BazState
case class Bar(state: BarState) // must not accept FooState or BazState
case class Baz(state: BazState) // must not accept FooState or BarState
case class FooBar(state: FooState or BarState) // must not accept BazState
case class FooBaz(state: FooState or BazState) // must not accept BarState
case class BarBaz(state: BarState or BazState) // must not accept FooState
I know I can create another trait for every compound class, but this would force me to add it to everything that extends any of these previous traits.
Yes, you would usually use a typeclass to achieve what you want, and a context bound. Here's how:
trait Acceptable
object Acceptable {
implicit val fooIsGood = new Acceptable[Foo] {}
implicit val barIsGood = new Acceptable[Bar] {}
}
case class Something[T : Acceptable](state: T)
And you can play with it to implement whatever functionality you want using this pattern. Achieving a real union type bound be done with Either or co-products, but in most scenarios this may be simpler.
One possible way to do this is to use the Either type:
case class FooBar(state: Either[FooState, BarState]) {
def someOperation() = {
state match {
case Left(fooState) => ???
case Right(barState) => ???
}
}
}
What you've described is a union type. The current version of Scala does not support them as you've described them, however it is planned for Dotty.
If you need more flexibility than that (more than two types for example) consider using a Coproduct from a functional programming library. Scalaz, Cats and Shapeless all expose them.

Case class without parameters alternative

During some simple scala coding exercise I ran into ideological problem of case classes without parameters and constructor parameters duplication.
It all started with the following two quite simple classes:
trait Namespace
case class Reply[T](namespace: Namespace, correlation: String, data: Try[T])
abstract class Request(val namespace: Namespace, val id: String = UUID.randomUUID().toString) {
def success[T](data: T) = Reply(namespace, id, Try(data))
def failure(msg: String) = Reply(namespace, id, Failure(new RuntimeException(msg)))
}
Now let's assume i have an entity Post and I want to add All class as a command to query all records of type Post. In my current set up it would be easier to actually write the following:
case class All extends Request(Posts)
However in this case I get compiler warning that case classes without parameters are deprecated. So one might suggest to rewrite it into the following:
case object All extends Request(Posts)
However in this case object All will be instantiated only once along with its id field which would like to avoid having unique id for each request.
Could you please suggest a better way of representing All command so that it would not be required to duplicate constructor arguments?
Thanks in advance?
The actual warning is that
case classes without a parameter list are not allowed; use either case
objects or case classes with an explicit `()' as a parameter list.
So give this class an empty parameter list, just as suggested by the compiler:
case class All() extends Requests(Posts)
Don't use a case class or case object, just use a companion apply instead. You don't really want an object here anyway, if you need a unique ID for every request.
class All extends Requests(Posts)
object All {
def apply(): All = new All()
}
getPosts(All())

How to use Scala Enumerations in Classes

I'm learning Scala and am trying to get a simple enum setup for a project. I've checked a few examples and none seem to fit, all of the examples in the Scala documentation and on StackOverflow are for enums inside objects, not classes. I'm getting an IDE warning that I don't understand. I'm coming from a beginner Java background into Scala, which may be the cause of my confusion.
Here's the code:
class Car(maxSpeed: Integer) {
// Enums
object CarType extends Enumeration {
type CarType = Value
val PERIPHERAL, COMPUTER, EMPTY = Value
}
import CarType._
// Fields
val repeated: CarType
}
When I mouse over the class name, I can see this Intellij warning:
Class 'Car' must either be declared abstract or implement abstract member 'typed: Car.this.CarType.CarType' in 'Car'
I'm not sure why it wants me to implement my variable, and the class is not intended to be abstract. I'd like to use Enums similarly to how they are used in Java.
Move the enumeration outside the class:
// Enums
object CarType extends Enumeration {
type CarType = Value
val PERIPHERAL, COMPUTER, EMPTY = Value
}
class Car(maxSpeed: Integer) {
import CarType._
// Fields
val repeated: CarType
}
Or move it to a companion object:
class Car(maxSpeed: Integer) {
import Car.CarType._
// Fields
val repeated: CarType
}
object Car {
object CarType extends Enumeration {
type CarType = Value
val PERIPHERAL, COMPUTER, EMPTY = Value
}
}
The problem is that things defined inside of a class are scoped to the the instance of that class (unlike some other languages).
That said, I recommend using algebraic data types instead of enums:
sealed trait CarType
object CarType {
case object Peripheral extends CarType // strange choice of names
case object Computer extends CarType
case object Empty extends CarType
}
case class Car(maxSpeed: Int, carType: CarType)
For more info about sealed traits see this SO q&a
What you're looking for is a Scala Case Class.
class Car(maxSpeed: Integer)
case class Minivan(maxSpeed: Integer) extends Car(maxSpeed: Integer)
case class Hodrod(maxSpeed: Integer) extends Car(maxSpeed: Integer)
case class Coupe(maxSpeed: Integer) extends Car(maxSpeed: Integer)
Enumerations as they exist in Java aren't really used in Scala. By having a construction like what I have above, you can leverage Scala's powerful pattern matching do something like this:
val unknownCar = getCar() // Some function that gets a car
unknownCar match {
case Minivan => println("Driving the kids")
case Hodrod => println("Tearing up the drag")
case Coupe => println("Riding low")
}
...while still allowing you to treat it as a Car.
Because they are case classes Scala has a lot of things that help you out.
Note the documentation for Enumeration:
Typically these values enumerate all possible forms something can take and provide a lightweight alternative to case classes.
You should really only use it if you aren't planning to use those values for anything else - but even there case classes will generally serve you better.

scala value class multiple inheritance

I have in my project objects that represent IDs.
Let's say it is ChairId, TableId, LampId. I want them all to inherit from GenericId. And I want to be able to call def f(x: GenericId) = x.id
I want them to hold only single id: String so I would like to make them extend AnyVal.
Also I would like for each type to provide function generate which would generate my specific ID i.e. I would like to type something like ChairId.generate()
I have typed this:
sealed abstract class GenericId(val id: String)
final case class ChairId(override val id: String) extends GenericId(id)
final case class TableId(override val id: String) extends GenericId(id
And I though if GenericId would inherit from AnyVal that would work but so far no luck ;/ I also tried making GenericId a trait and make case classes extend AnyVal with GenericId but also won't compile :/
Another thing with TableId.generate() I can provide companion object just with function generate and that basically solve my problem but I wondered if there is possibility to solve that without defining companion object? (i.e. through implicits somehow)
// edit
regarding comment to provide code which doesn't compile(and I would like to):
sealed abstract class AbstractId(val id: String) extends AnyVal
final case class CatId(override val id: String) extends AbstractId(id)
final case class DogId(override val id: String) extends AbstractId(id)
Value classes cannot work this way for a couple of reasons.
First, from the documentation, value classes cannot be extended by any other class, so AbstractId cannot extend AnyVal. (Limitation #7)
scala> abstract class AbstractId(val id: String) extends AnyVal
<console>:10: error: `abstract' modifier cannot be used with value classes
abstract class AbstractId(val id: String) extends AnyVal
^
Second, even if you make AbstractId a trait, and define the other ids like this:
final case class DogId(val id: String) extends AnyVal with AbstractId
.. the usage of the value class wouldn't fit your case, because the class itself would still get allocated. See the allocation summary:
A value class is actually instantiated when:
a value class is treated as another type.
a value class is assigned to an array.
doing runtime type tests, such as pattern matching.
Some quotes from the value classes SIP that are likely to clarify your doubts:
Value classes...
...must have only a primary constructor with exactly one public, val
parameter whose type is not a value class.
... cannot be extended by another class.
As per 1. it can not be abstract; per 2. your encoding doesn't work.
There is another caveat:
A value class can only extend universal traits and cannot be extended
itself. A universal trait is a trait that extends Any, only has defs
as members, and does no initialization. Universal traits allow basic
inheritance of methods for value classes, but they incur the overhead
of allocation.
With all that in mind, based on your last snippet, this might work:
sealed trait AbstractId extends Any { def id: String }
final case class CatId(id: String) extends AnyVal with AbstractId
final case class DogId(id: String) extends AnyVal with AbstractId
But keep in mind the allocation only occurs if you want to use CatId and DogId as an AbstractId. For better understanding I recommend reading the SIP.