How to create an objectthat extends a trait and uses early definition syntax?
Let's say we have two traits:
trait Trait {
val value : String
}
trait Simple
Let's say we have also a simple class:
class Class
We can create a new object of type Class and make it extend a Simple trait:
new Class extends Simple
Is it possible to create a new object of type Class that extends the Trait and uses the early definition syntax to set value member? I tried something like:
new Class extends { override val value = "42" } with Trait
But this gives a syntax error:
Error:(12, 17) ';' expected but 'extends' found.
new Class extends { val value = "42" } with Trait
trait T { val value: String }
class C
new C with T { val value = "ok" }
Related
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).
Have been using Scala for a while, and hoping to get into domain modeling using type constraints. In the following code, I am trying to design a domain. Excuse the cryptic classnames, wanted to focus on the specific problem I am facing at hand.
Here is the code:
import scala.collection.mutable
class ClassTypeTest
{
trait TSM
{
def fit[T <: TSM](): FR[T]
}
abstract class FM extends TSM{}
trait MP[T <: TSM]
{
def name: String
}
trait FiM[T <: TSM]
{
val fittedParams = mutable.HashMap[String, MP[T]]()
}
class FR[T <: TSM](fm: FiM[T])
// Now define classes related to SMA
//===================================
abstract class SMAP extends MP[SMA]
class SMAO(n: String) extends SMAP
{
override def name = n
}
class SMAM(smao: SMAO) extends FiM[SMA]
{
fittedParams(smao.name) = smao
}
class SMA extends FM
{
override def fit[SMA]() =
{
val fim = new SMAM(new SMAO("x"))
//*******************************
// Following line shows the error:
// Error:(40, 25) type mismatch;
// found : ClassTypeTest.this.SMAM
// required: ClassTypeTest.this.FiM[SMA]
// new FR[SMA](fim)
//*******************************************
new FR[SMA](fim)
}
}
}
I am defining SMAM as extends FiM[SMA], so why is the compiler complaining about Type mismatch. Required FiM[SMA], found: SMAM. Have I ill-defined one of the type parameters or constraints?
I am trying to constrain the type of fit method, and the FR object to one of the subclasses of TSM. How do I achieve this?
I am defining SMAM as extends FiM[SMA], so why is the compiler complaining about Type mismatch. Required FiM[SMA], found: SMAM.
SMA in def fit[SMA]() = ... in
class SMA extends FM
{
override def fit[SMA]() =
{
val fim = new SMAM(new SMAO("x"))
new FR[SMA](fim)
}
}
is a type parameter. It's arbitrary type, not the class SMA (the type parameter SMA hid class SMA). You could use arbitrary identifier here, e.g. T
class SMA extends FM
{
override def fit[T]() =
{
val fim = new SMAM(new SMAO("x"))
new FR[T](fim)
}
}
So I guess the error
type mismatch;
found : SMAM
required: FiM[T]
is clear now.
trait TSM
{
def fit[T <: TSM](): FR[T]
}
This defines a trait with a method fit that for any type T returns an instance of FR[T]
class SMA extends TSM
{
override def fit[SMA]() =
{
val fim = new SMAM(new SMAO("x"))
new FR[SMA](fim)
}
}
This overrides method fit defined in the trait. SMA in method definition is a type parameter, not a reference to the class name. You can (probably, should) replace it with T, it'll be equivalent, but a bit less confusing:
override def fit[T <: TSM](): FR[T] = new FR(new SMAM(new SMAO("x")))
This will give you a more descriptive error - something to the effect that you are trying to return FR[SMA] where FR[T] is expected.
The bottom line is you cannot make a method in subclass less "generic" than it is declared in a superclass. To better understand why that is the case, consider this:
abstract class Foo extends TSM
val sma: TSM = new SMA()
val foo: FR[Foo] = sma.fit[Foo]()
This should work: sma is an instance of TSM, and TSM.fit is supposed to work for any type parameter which is a subclass of TSM, which Foo clearly is.
But your SMA.fit as written can only return FR[SMA], so the snippet above would not work if it was allowed.
How to fix this?
Well, that depends on what you actually want. One possibility is to parametrize the trait itself rather than the method:
trait TSM[T <: TSM[_]] {
def fit(): FR[T]
}
class SMA extends TSM[SMA]
{
override def fit() = new FR(new SMAM(new SMAO("x")))
}
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 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))
If I have the following Scala code:
trait BaseTrait[EnumType <: Enumeration] {
protected val enum: EnumType
protected val valueManifest: Manifest[EnumType#Value]
}
object MyEnum extends Enumeration {
val Tag1, Tag2 = Value
}
And I want to create a class which implements BaseTrait using MyEnum, I can do it like this:
class BaseClass[EnumType <: Enumeration]
(protected val enum: EnumType)
(implicit protected val valueManifest: Manifest[EnumType#Value])
extends BaseTrait[EnumType] {
}
class Test extends BaseClass(MyEnum)
But how can I do it without an intermediary base class? All other attempts always resulted in a compile error.
You did not write what you tried but my guess is that you had your class extend BaseTrait[MyEnum]. As MyEnum is an object the type MyEnum does not exist (unless you also define a class or trait with that name).
You have to explicitly supply the singleton type MyEnum.type as type parameter.
class Test extends BaseTrait[MyEnum.type] {
protected val enum = MyEnum
protected val valueManifest = manifest[MyEnum.type#Value]
}