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.
Related
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.
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()
Given this code
class Animal{}
class Dog extends Animal{}
class Cat extends Animal{}
class Pitbull extends Dog{}
object MyClass {
def main(args: Array[String]) {
val animals: List[Animal] = List(new Dog(), new Cat(), new Pitbull(), new Dog(), new Cat())
getElementOftype(animals, PitBull)
}
def getElementOftype(list: List[Animal], givenType: ???): Animal = {
}
}
I want to extract the first element of type Pitbull from this List, How should I proceed with this?
What I tried but feels wrong
trait Identity{
def who: String
}
class Animal extends Identity{
override def who = "Animal"
}
class Dog extends Animal with Identity{
override def who = "Dog"
}
class Cat extends Animal with Identity{
override def who = "Cat"
}
class Pitbull extends Dog with Identity{
override def who = "Pitbull"
}
object MyClass {
def main(args: Array[String]) {
val animals: List[Animal] = List(new Dog(), new Cat(), new Pitbull(), new Dog(), new Cat())
println(getElementOftype(animals, new Pitbull()))
}
def getElementOftype(list: List[Animal], givenType: Animal): Option[Animal] = {
list.collectFirst{case s if s.who == givenType.who => s}
}
}
I want to simply pass Pitbull as a parameter instead of instantiating an empty object.
This one works but I don't think it's the best approach.
There must be a more scalaesque way to do this.
You can do something like this (not tested, there may be some typos):
def getElementOftype[A <: Animal](list: List[Animal])(implicit ct: ClassTag[A]): Option[A] = {
list.collectFirst { case s: A => s }
}
The ClassTag is necessary to be able to match on the A type in collectFirst.
when you write Animal extends Identity you mean Animal IS an Identity, which is wrong, using this approach is preferred:
class Animal { self: Identity =>
}
which means whenever you want to instantiate an Animal, you need to give an Identity to it, but why would you want to do that?
You can simply use pattern matching to filter required type like this:
import scala.reflect.ClassTag
def getElementsOfType[A <: Animal : ClassTag](animals: List[Animal]): Option[A] = {
animals.filter {
case a: A => true
case _ => false
}.map(_.asInstanceOf[A]).headOption
}
headOption is because your api returns Option[A] as result, you can make it return List[A] and get rid of the headOption in case you have multiple Pitbull's inside the list :)
//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)
I am trying to create a type class in Scala and use it to make a simple polymorphic case class. This example doesn't compile and gives "could not find implicit value for parameter writer:A$A228.this.ValueWriter[T]". I can't really figure out what could be going wrong or where to start.
trait Keeper
case class StringKeeper(measure: String) extends Keeper
case class StringLengthKeeper(measure: Int) extends Keeper
trait ValueWriter[A] {
def write(value: String): A
}
object DefaultValueWriters {
implicit val stringWriter = new ValueWriter[StringKeeper] {
def write(value: String) = StringKeeper(value)
}
implicit val stringLengthWriter = new ValueWriter[StringLengthKeeper] {
def write(value: String) = StringLengthKeeper(value.length)
}
}
object Write {
def toWrite[A](value: String)(implicit writer: ValueWriter[A]) = {
writer.write(value)
}
}
case class WriterOfKeepers[T <: Keeper](value: String) {
def run: T = {
Write.toWrite[T](value)
}
}
import DefaultValueWriters._
val writerLengthKeeper = WriterOfKeepers[StringLengthKeeper]("TestString")
writerLengthKeeper.run
There is no implicit ValueWriter in scope when you call Write.toWrite.
You need to have the implicit parameter in the constructor
case class WriterOfKeepers[T <: Keeper : ValueWriter](value: String) {
def run: T = {
Write.toWrite[T](value)
}
}
or in the method
case class WriterOfKeepers[T <: Keeper](value: String) {
def run(implicit writer: ValueWriter[T]): T = {
Write.toWrite(value)
}
}
or find some other way compatible with your requirements (and I don't know those).