Scala - object extends abstract class and takes parameters - scala

//File Animal.scala
abstract class Animal {
val name: String
def getSomething(tClass: TypeClass): String = {
tClass.tName.split('.').lift(0)
}
def apply(tClass: TypeClass): SomeOtherClassType = {
// something...
}
// File: DogSpike, this is used for some specific cases (overwrites
base class val)
object DogSpike extends Animal {
override val name: String = "Spike"
}
this call then works (calls apply)
myTransformation(() => DogSpike(this))
Now I would like to create a more generic object that one can pass arguments but I am unable to.
It would work to create a derived Object from Animal that takes one arguments and being able to use the apply call
object TheDog(name: String) extends Animal {
override val name: String = "Spike"
//...
}
not sure how to implicitly call Animal.apply for TheDog object where I could pass a parameter (name)
myTransformation(() => TheDog(this))
// also this seems to be incorrect "*Wrong top statement declaration*"
object TheDog(val n: String) extends Animal {
override val name: String = n
//...
}

As of *Wrong top statement declaration* (I can understand only this part of your question) - you can't have constructor in object as object is a singleton, so you should use a case class (ADT):
final case class TheDog(name: String) extends Animal
scala>TheDog("Spike")
res2_2: TheDog = TheDog("Spike")
val and companion object with apply is added automatically for case classes, so you don't need to define your own own apply in Animal. case class TheDog(val name: String) is same as case class TheDog(name: String).
I's also use traits instead of abstract class:
trait Animal {
val name: String
def getSomething: String = {
"Dog: " + name
}
}
I don't understand your TypeClass type, but if you really want type classes:
trait Animal {
def name: String
}
final case class TheDog(name: String) extends Animal
final case class TheCat(name: String) extends Animal
implicit class RichDog(dog: TheDog){
def getSomething: String = {
"Dog" + dog.name
}
}
implicit class RichCat(cat: TheCat){
def getSomething: String = {
"Cat: " + cat.name
}
}
scala> TheDog("Spike").getSomething
res4_5: String = "DogSpike"
scala> TheCat("Tom").getSomething
res4_6: String = "Cat: Tom"
About calling apply "implicitly", I don't know why would anyone need this, but:
trait AnimalFactory[A <: Animal] {
def apply(name: String)(implicit constructor: String => A) = constructor(name)
}
object TheeeDog extends AnimalFactory[TheDog]
implicit def createDog(name: String) = TheDog(name)
TheeeDog("Spike")
Of course you have to provide createDog and make it visible for a client, but it doesn't really make sense if you can just use ADTs and define additional required applys in companion object:
case class TheMouse(name: String)
object TheMouse{
def apply(isJerry: Boolean): TheMouse = if (isJerry) TheMouse("Jerry") else TheMouse("NotJerry")
}
TheMouse(true)
If you want to add some parameter to constructor, just add it:
class AnimalFactory(clazz: SomeClass){
def doSomething = clazz.name
def apply(name: String)
}
val dogFactory = new AnimalFactory(dogClassDescriptor)
val catFactory = new AnimalFactory(catClassDescriptor)
dogFactory("Spike")
catFactory("Tom")
You can even create a factory for factory (I wouldn't recommend - this solution already looks overcomplicated):
object AnimalFactory{ //please don't use classes for that - avoiding `new` is not their purpose
def apply(clazz: SomeClass) = new AnimalFactory(clazz)
}
val dogFactory = AnimalFactory(dogClassDescriptor)
//or even `val spike = AnimalFactory(dogClassDescriptor)("Spike")`
But still what's the point if you could just provide underlying clazz either as a member or just in a wrapper:
final case class ClazzWrapper[T <: Animal](clazz: SomeClass, animal: T)

Related

Defining a generic to be a case class

In this example, I want the generic T to be a case class and a DAOEntity with id, so in the abstract implementation, I can use the copy method.
How to define it?
trait DAOEntity {
def id: String
}
// How to define this generic to force the use of a `case class` to have access to `copy`?
abstract class DAO[T <: DAOEntity] {
def storeInUppercase(entity: T): T = entity.copy(id = entity.id)
}
case class MyEntity(id: String) extends DAOEntity
class MyEntityDAO extends DAO[MyEntity] {
// Other stuff
}
There is no way to know if a type is a case class or not.
And even if there was, you won't get the copy method. The language doesn't provide a way to abstract over constructor; thus neither copy and factories (apply on companions) by extension. Which makes sense, what would be the type signature of such function?
What you can do instead is create a factory-like typeclass and ask for that:
trait DAOFactory[T <: DAOEntity] {
def copy(oldEntity: T, newId: String): T
}
object DAOFactory {
def instance[T <: DAOEntity](f: (T, String) => T): DAOFactory[T] =
new DAOFactory[T] {
override final def copy(oldEntity: T, newId: String): T =
f(oldEntity, newId)
}
}
Which can be used like this:
abstract class DAO[T <: DAOEntity](implicit factory: DAOFactory[T]) {
def storeInUppercase(entity: T): T =
factory.copy(
oldEntity = entity,
newId = entity.id.toUpperCase
)
}
And entities would provide the instance like this:
final case class MyEntity(id: String, age: Int) extends DAOEntity
object MyEntity {
implicit final val MyEntityFactory: DAOFactory[MyEntity] =
DAOFactory.instance {
case (oldEntity, newId) =>
oldEntity.copy(id = newId)
}
}
// This compile thanks to the instance in the companion object.
object MyEntityDAO extends DAO[MyEntity]
You can see the code running here.

How do I pass an inherited variable into a Auxiliary constructor from a trait

I'm looking to get the value bork and pass it to a class into the class in the constuctor.
trait dog {
lazy val bork = "bork"
}
class Animal(val firstName: String, dogCommand: DogCommand) extends dog{
def this(firstName: String) {
this(firstName, DogCommand(bork))
}
def getDogCommand(): Unit = {
dogCommand.getCommand()
}
}
case class DogCommand(command: String) {
def getCommand() :Unit = {
println(command)
}
}
val myDog = new Animal("first")
myDog.getDogCommand()
I usually get - error: not found: value bork
The problem is that bork is a value on dog but there is no instance of dog available to read the bork from. this is a constructor so it doesn't have access to the value that is being constructed.
This is one solution that retains most of the original code:
trait dog {
def bork = dog.DefaultBork
}
object dog {
val DefaultBork = "bork"
}
class Animal(val firstName: String, dogCommand: DogCommand) extends dog {
def this(firstName: String) = this(firstName, DogCommand(dog.DefaultBork))
...
}
You could consider removing the default value of bork in dog so that any class implementing the trait is required to provide a value for bork. (It can always use DefaultBork if it wants to retain the default value)
For one item, your this constructor is incorrect. I also renamed Dog. As for your lazy val that wouldn't work on a trait. See more here why scala don't allow define lazy val in trait?. Even if you make it non-lazy, that value is not initialized yet until construction when that val is not available, yet. Another problem would also be logic, an Animal is not Dog, but the other way around. Here is an alternative: https://scastie.scala-lang.org/WnhxzSn6QtOf5vo1epDDqQ Hope that helps. ;)
trait Dog {
lazy val bork = "bork"
}
class Animal(val firstName: String, dogCommand: DogCommand) extends Dog {
def this(firstName: String) = this(firstName, DogCommand(bork))
def getDogCommand(): Unit = {
dogCommand.getCommand()
}
}
case class DogCommand(command: String) {
def getCommand() :Unit = {
println(command)
}
}
val myDog = new Animal("first")
myDog.getDogCommand()

Does extending a class in scala inherits auxilary constuctor also?

I am trying to extend a class which has primary and auxiliary constructor.
DO my base class has access to auxilary constructors
class learnInheritance (speed:Int) {
val mph: Int = speed
def race() = println("Racing")
def this (speed:Int, model:String) {
this(speed)
println("Speed of call is " + speed + "and model is " + model)
}
}
class Car(speed:Int) extends learnInheritance(speed) {
override val mph: Int = speed
override def race() = println("Racing Car")
}
val a = new Car(10,"Maruti")
println("Speed of Car: " + a.mph)
a.race()
The constructor is not any utility function, it is a function that should guarantees that object is fully initialized after it is run.
So if you call Car constructor it will return fully initialized Car in the process your instance should become fully initialized learnInheritance before you start initializing it as Car. So you should assume that you can call only one parent constructor.
To deal with this restriction you should require that there is some parent constructor that does all the job, where all other set up some defaults. Then you could call that constructor yourself with your constructor that does all the job, and have other constructors act as utilities with defaults.
class Foo(name: String, age: Int) {
def this(name: String) = this(name, 10)
def this(age: Int) = this("", age)
}
class Bar(name: String, age: Int) extends Foo(name, age) {
def this... // bar constructors
}
if you cannot do this, then unfortunately you have to pick only one case in your class.
Alternatively, consider that each case is a separate class:
class Foo private (name: String, age: Int) {
def this(name: String) = this(name, 10)
def this(age: Int) = this("", age)
}
sealed trait Bar { this: Foo =>
// Bar methods
}
object Bar {
final class Name(name: String) extends Foo(name) with Bar
final class Age(age: Int) extends Foo(age) with Bar
def apply(name: String): Bar = new Name(name)
def apply(age: String): Bar = new Age(age)
}
but this has its own issues.
Most of the time, though, usage of only one parent constructor for everything is not a problem. If it is then we can think for a solution for a particular case.
Constructors are not instance methods. That means they are not inherited.

How to create a factory for a parameterized class?

I'm trying to build a factory for implementations of a generic trait.
Given my domain model:
trait Person
case class Man(firstName: String, lastName: String) extends Person
case class Woman(firstName: String, lastName: String) extends Person
I created a repository for those classes like this:
trait Repository[T <: Person] {
def findAll(): List[T]
}
class ManRepository extends Repository[Man] {
override def findAll(): List[Man] = {
List(
Man("Oliver", "Smith"),
Man("Jack", "Russel")
)
}
}
class WomanRepository extends Repository[Woman] {
override def findAll(): List[Woman] = {
List(
Woman("Scarlet", "Johnson"),
Woman("Olivia", "Calme")
)
}
}
So far so good, some pretty simple classes. But I'd wanted to create a factory to create an instance of these repositories depending on some parameters.
object RepositoryFactory {
def create[T <: Person](gender: String): Repository[T] = {
gender match {
case "man" => new ManRepository()
case "woman" => new WomanRepository()
}
}
}
But this last piece won't compile. If I ommit the explicit return type of the factory, it compiles but returns a repository of type Repository[_1] instead of Repository[Man]
I can't seem to find a proper solution, do any of you guys have got some tips for me?
How about this?
Instead of using a string, use a sealed trait that knows how to create the appropriate repo
Use dependent types to return the correct type of repository
Code:
object GenderStuff {
trait Person
case class Man(firstName: String, lastName: String) extends Person
case class Woman(firstName: String, lastName: String) extends Person
// Note that Repository has to be covariant in P.
// See http://blogs.atlassian.com/2013/01/covariance-and-contravariance-in-scala/ for explanation of covariance.
trait Repository[+P <: Person] {
def findAll(): List[P]
}
class ManRepository extends Repository[Man] {
override def findAll(): List[Man] = {
List(
Man("Oliver", "Smith"),
Man("Jack", "Russel")
)
}
}
class WomanRepository extends Repository[Woman] {
override def findAll(): List[Woman] = {
List(
Woman("Scarlet", "Johnson"),
Woman("Olivia", "Calme")
)
}
}
sealed trait Gender {
type P <: Person
def repo: Repository[P]
}
case object Male extends Gender {
type P = Man
def repo = new ManRepository()
}
case object Female extends Gender {
type P = Woman
def repo = new WomanRepository()
}
object RepositoryFactory {
def create(gender: Gender): Repository[gender.P] = gender.repo
// or if you prefer you can write it like this
//def create[G <: Gender](gender: G): Repository[G#P] = gender.repo
}
val manRepo: Repository[Man] = RepositoryFactory.create(Male)
val womanRepo: Repository[Woman] = RepositoryFactory.create(Female)
}
Of course, this makes the factory object pretty pointless - it's easier to just call Male.repo directly :)
You should probably think some more about what you're trying to achieve. In your example, what type would you expect to be returned by RepositoryFactory.create[Man]("woman")? The compiler has no way to associate those arbitrary strings with types.

Method polymorphism

I am trying to write a generic method f[T](id:String) that is something like this:
case class A(x:String)
case class B(y:String)
case class C(z:String)
def f[T](id:String): T = { /* equivalent to T(id) */ }
val result1:A = f[A]("123") // returns A("123")
val result2:B = f{B]("345") // returns B("345")
val result3:C = f[C]("567") // returns C("567")
Unfortunately I cannot figure out how to work with the type T inside the method, besides using reflection. By "working with the type T" i mean for example being able to do something like the following, which I know doesn't work (for illustration purposes only):
T match {
case A => A(id)
case B => B(id)
}
or simply invoke T(ID) to create a new object of whatever type T is.
I can of course break up this into three methods:
def f1(id:String): A = { A(id) }
def f2(id:String): B = { B(id) }
def f3(id:String): C = { C(id) }
val result1:A = f1("123") // returns A("123")
val result2:B = f2("345") // returns B("345")
val result3:C = f3("567") // returns C("567")
but I'm hoping there is a way to keep it as one generic method to avoid some ugly boilerplate code duplication, and still be nearl as fast as the tree method version.
If you do not want to use reflection (ClassTag or TypeTag), you could use a Factory type class to achieve the desired functionality (unless it defeats the purpose of your generic function by generating a lot of duplicated simple code ;)).
case class A(s: String)
case class B(s: String)
case class C(s: String)
trait Factory[T] extends ((String) => T) {
def apply(arg: String): T
}
object Factory {
implicit object AFactory extends Factory[A] {
override def apply(arg: String): A = A(arg)
}
implicit object BFactory extends Factory[B] {
override def apply(arg: String): B = B(arg)
}
implicit object CFactory extends Factory[C] {
override def apply(arg: String): C = C(arg)
}
}
def create[T : Factory](arg: String): T = implicitly[Factory[T]].apply(arg)
create[A]("foo") | -> res0: A = A(foo)