Passing case objects in Scala - scala

I have the following definitions:
sealed trait MyTrait
case object One extends MyTrait
case object Two extends MyTrait
object Test extends (MyTrait => MyTraitReturnVal) {
def apply(myTrait: MyTrait) = { ... }
def myMethod(myTrait: MyTrait) = {
...
}
}
When I call Test(One), it complains that it is expecting an interface instead of a concrete type. Any suggestions on how to get around this?
So calling:
Test(One)
Complains that it is expecting MyTrait and the actual parameter is One.type!

You are inheriting Test object from Function1 class, so you need to implement 'apply' method instead of 'myMethod'. This code compiles and runs:
sealed trait MyTrait
case object One extends MyTrait
case object Two extends MyTrait
case class MyTraitReturnVal(my: MyTrait)
object Test extends (MyTrait => MyTraitReturnVal) {
def apply(myTrait: MyTrait) =
new MyTraitReturnVal(myTrait)
}
println(Test(One))
println(Test(Two))

Related

How do I limit the parameter types of a generic argument based on another parameter in Scala?

I'm trying to create a map of different configurations, where each configuration has a given key object and some options, e.g.
FirstConfig can be either:
FirstConfigOptionA
FirstConfigOptionB
SecondConfig can be either:
SecondConfigOptionA
SecondConfigOptionB
...
And I'm having trouble with general typing and signature of the setter function so it checks at compile time I'm supplying the correct objects, e.g.
// 1. this should compile normally
set(FirstConfig, FirstConfigOptionA)
// 2. should NOT compile due to `SecondConfigOptionA` parameter not being a valid option for `FirstConfig`
set(FirstConfig, SecondConfigOptionA)
So far, my attempts still allow the second case above to compile.
abstract sealed class Configuration
trait OptionKey[T <: Configuration] {}
trait OptionVariant[T <: Configuration] {}
// First Config
trait FirstConfig extends Configuration
object FirstConfigKey extends OptionKey[FirstConfig];
object FirstConfigOptionA extends OptionVariant[FirstConfig]
object FirstConfigOptionB extends OptionVariant[FirstConfig]
// Second Config
trait SecondConfig extends Configuration
object SecondConfigKey extends OptionKey[SecondConfig];
object SecondConfigOptionA extends OptionVariant[SecondConfig]
object SecondConfigOptionB extends OptionVariant[SecondConfig]
def set[T](k: OptionKey[T], v: OptionVariant[T]): Unit = {}
set(FirstConfigKey, FirstConfigOptionA)
set(FirstConfigKey, SecondConfigOptionA) // This still compiles
I've also tried using Enumerations with similar results:
object FirstConfig extends Enumeration {
type FirstConfig = Value
val FirstConfigOptionA, FirstConfigOptionB = Value
}
object SecondConfig extends Enumeration {
type SecondConfig = Value
val SecondConfigOptionA, SecondConfigOptionB = Value
}
def set(k: Enumeration, v: Enumeration#Value): Unit = {}
set(FirstConfig, FirstConfig.FirstConfigOptionA)
set(FirstConfig, SecondConfig.SecondConfigOptionA) // This still compiles
What is the correct way to express this relationship between a config and its available options or what should be set's signature to enforce it?
What about using path-dependant types like this:
trait Configuration {
sealed trait OptionKey
val key: OptionKey
sealed trait OptionVariant
}
object Configuration {
def set(config: Configuration)(variant: config.OptionVariant): Unit = {
println(s"${config} - ${config.key} - ${variant}")
}
}
case object FirstConfig extends Configuration {
private case object FirstConfigKey extends OptionKey
override final val key: OptionKey = FirstConfigKey
case object FirstConfigOptionA extends OptionVariant
case object FirstConfigOptionB extends OptionVariant
}
case object SecondConfig extends Configuration {
private case object SecondConfigKey extends OptionKey
override final val key: OptionKey = SecondConfigKey
case object SecondConfigOptionA extends OptionVariant
case object SecondConfigOptionB extends OptionVariant
}
You can see it working as expected here.
Why do you need to store them as key/value pairs? You can just represent it as an algebraic data type:
enum FirstConfig:
case OptionA
case OptionB
enum SecondConfig:
case OptionA
case OptionB
enum Config:
case First(value: FirstConfig)
case Second(value: SecondConfig)
def set(config: Config): Unit = …

Scala: Inherited enum object doesn't satisfy parent trait

I am trying to use an Enumeration object in a method. The enum object extends from a trait, and the method takes the trait as a parameter. Here is the sample code.
sealed trait canAdd
object DAdder extends Enumeration with canAdd
{
type DAdder = Value
val P, Q = Value
}
class ClassTypeTest extends AnyFlatSpec with Matchers
{
class AClass
{
def add(v: canAdd) = if(v.isInstanceOf[DAdder]) println("Got DAdder") else println("Got IAdder")
def note(v: canAdd) = println("Got some canAdd trait object")
}
val aobj = new AClass
val aseq: Seq[DAdder] = Seq(DAdder.P, DAdder.Q, DAdder.P)
//*** Error in below line *****
aseq.foreach(aobj.add(_))
}
The compiler gives the following error:
Error:(23, 23) type mismatch;
found : x.x.DAdder.DAdder
(which expands to) x.x.DAdder.Value
required: x.x.canAdd
aseq.map(aobj.add(_))
Shouldn't I be able to pass an object that inherits the trait in a method that takes the trait as an argument? How do I fix this?
Enumeration class doesn't allow you to extends its values functionality. It is basically:
abstract class Enumeratum {
sealed trait Value { /* utilities */ }
object Value { /* factory of Values */ }
// utilities
}
Since you have the control only on Enumeratum class you cannot extend Values which is what you ask for.
You can easily have this functionality with sealed traits and case objects
sealed trait DAdder extends canadd
object DAdder {
case object P extends DAdder
case object Q extends DAdder
}
This however misses some utilities like finding value by its name, listing all values, etc.
This problem is solved by Enumeratum library which require that you mix in some traits and cope paste one line (val values = findValues) to have all functionalities of Enumeration and more
import enumeratum._
sealed trait DAdder extends canadd
with EnumEntry // mixin here
object DAdder extends Enum[DAdder] { // and mixin here
case object P extends DAdder
case object Q extends DAdder
val values = findValues // and one line calling macro
}
(There are also specializations for enums that should store some primitives/strings as their values in enumeratum.values._).
In Scala 3 this will probably look slightly different as it introduced enum keyword
sealed trait DAdder extends canadd
object DAdder {
case object P extends DAdder
case object Q extends DAdder
}
will become
enum DAdder extends canadd {
case P, Q
}
Scala 3's enum will ordinal method defined, so libraries like Enumeratum will have to provide a little fewer functionalities. (I guess by mixing in Java's Enum trait you will have access to values so everything else is a matter of extension methods).

Implicit macro. Default implicit value. How?

I don't even know how to ask the question.
I have a macro that creates an instance of IsEnum[T] for a type T.
I'm doing testing for it, and want to make sure that the implicit is not found for types that are not sealed, or that, in general, don't meet the requirements of an enum.
So I created this method for testing
def enumOf[T](implicit isEnum:IsEnum[T] = null) = isEnum
And then I ensure that enumOf[NotAnEnum] == null
But instead, it fails at compile time.
One thing is the macro erroring. Another thing is the macro just not applying for a given case. How to make that distinction when creating macros?
Edit: I've used c.abort and c.error, both giving me the same results.
Sounds like you didn't make your macro materializing type class IsEnum whitebox. Normally implicit macros should be whitebox.
import scala.language.experimental.macros
import scala.reflect.macros.whitebox
trait IsEnum[T]
object IsEnum {
implicit def mkIsEnum[T]: IsEnum[T] = macro mkIsEnumImpl[T]
def mkIsEnumImpl[T: c.WeakTypeTag](c: whitebox.Context): c.Tree = {
import c.universe._
val typ = weakTypeOf[T]
val classSymbol = typ.typeSymbol.asClass
if (!classSymbol.isTrait || !classSymbol.isSealed) c.abort(c.enclosingPosition, s"$typ must be sealed trait")
val symbols = classSymbol.knownDirectSubclasses
symbols.collectFirst {
case symbol if !symbol.isModuleClass || !symbol.asClass.isCaseClass =>
c.abort(c.enclosingPosition, s"${symbol.asClass.toType} must be case object")
}
q"new IsEnum[$typ] {}"
}
}
def enumOf[T](implicit isEnum: IsEnum[T] = null) = isEnum
Usage:
sealed trait A
object A {
case object B extends A
case object C extends A
case class D() extends A
}
enumOf[A] //null
sealed trait A
object A {
case object B extends A
case object C extends A
class D extends A
}
enumOf[A] //null
sealed trait A
object A {
case object B extends A
object C extends A
}
enumOf[A] //null
trait A
object A {
case object B extends A
case object C extends A
}
enumOf[A] //null
sealed trait A
object A {
case object B extends A
case object C extends A
}
enumOf[A] //App$$anon$1#47f37ef1
Runtime of macros is compile time of main code. If a blackbox macro (even implicit blackbox macro) throws an exception then it will be a compile error during compilation of main code. If a whitebox implicit macro throws an exception then during compilation of main code the implicit will be silently removed from candidates.
https://docs.scala-lang.org/overviews/macros/blackbox-whitebox.html

Case object extending trait

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".

Scala Generic Subtypes and Cake Pattern

I have the following class hierarchy:
abstract class Event(val timeStamp:Long,val id:Long )
case class StudentEvent(override val timeStamp:Long, override val id:Long,
firstName:String,lastName:String) extends Event(timeStamp,id )
case class TeacherEvent(override val timeStamp:Long, override val id:Long,
firstName:String,lastName:String.....) extends Event(timeStamp,id)
Now I have the following trait:
trait Action[T <: Event] {
def act[T](event:T)
}
Now I would like to extend this trait for students and teachers:
trait StudentAction extends Action[StudentEvent]{
def act(event:StudentEvent) = println(event)
}
trait TeacherAction extends Action[TeacherEvent]{
def act(event:TeacherEvent) = println(event)
}
Now I would like to to create Handler class which take cars for all type of events:
class Handler{
self:Action[Event] =>
def handle(event:Event) = act(event)
}
Now when I try to create Handler for some type of Event, I'm getting compilation error:
val studentHandler = new Handler with StudentAction
illegal inheritance; self-type Handler with StudentAction does not conform to Handler's selftype Handler
with Action[Event]
What am I missing?
Handler type had to be parametrized too:
scala> class Handler[T<:Event] {
| self:Action[T] =>
| def handle(event:T) = act(event)
| }
defined class Handler
#Ashalynd is correct. up voted.
also to extract the inherit logic into a purer code looks like this:
class PARENT
class CHILD extends PARENT
trait A[T <: PARENT]
trait AA extends A[CHILD]
class B[T <: PARENT] {
self: A[T] =>
}
val b = new B[CHILD]() with AA