I am trying to learn about the cake pattern.
I am reading this blog about it.
The example code from that blog is:
case class User (name:String,email:String,supervisorId:Int,firstName:String,lastName:String)
trait UserRepository {
def get(id: Int): User
def find(username: String): User
}
trait UserRepositoryComponent {
def userRepository: UserRepository
trait UserRepository {
def get(id: Int): User
def find(username: String): User
}
}
trait Users {
this: UserRepositoryComponent =>
def getUser(id: Int): User = {
userRepository.get(id)
}
def findUser(username: String): User = {
userRepository.find(username)
}
}
trait UserInfo extends Users {
this: UserRepositoryComponent =>
def userEmail(id: Int): String = {
getUser(id).email
}
def userInfo(username: String): Map[String, String] = {
val user = findUser(username)
val boss = getUser(user.supervisorId)
Map(
"fullName" -> s"${user.firstName} ${user.lastName}",
"email" -> s"${user.email}",
"boss" -> s"${boss.firstName} ${boss.lastName}"
)
}
}
trait UserRepositoryComponentImpl extends UserRepositoryComponent {
def userRepository = new UserRepositoryImpl
class UserRepositoryImpl extends UserRepository {
def get(id: Int) = {
???
}
def find(username: String) = {
???
}
}
}
object UserInfoImpl extends
UserInfo with
UserRepositoryComponentImpl
I can simplify that code by removing Users:
package simple {
case class User(name: String, email: String, supervisorId: Int, firstName: String, lastName: String)
trait UserRepository {
def get(id: Int): User
def find(username: String): User
}
trait UserRepositoryComponent {
def userRepository: UserRepository
trait UserRepository {
def get(id: Int): User
def find(username: String): User
}
}
trait UserInfo {
this: UserRepositoryComponent =>
def userEmail(id: Int): String = {
userRepository.get(id).email
}
def userInfo(username: String): Map[String, String] = {
val user = userRepository.find(username)
val boss = userRepository.get(user.supervisorId)
Map(
"fullName" -> s"${user.firstName} ${user.lastName}",
"email" -> s"${user.email}",
"boss" -> s"${boss.firstName} ${boss.lastName}"
)
}
}
trait UserRepositoryComponentImpl extends UserRepositoryComponent {
def userRepository = new UserRepositoryImpl
class UserRepositoryImpl extends UserRepository {
def get(id: Int) = {
???
}
def find(username: String) = {
???
}
}
}
object UserInfoImpl extends
UserInfo with
UserRepositoryComponentImpl
}
and it compiles just fine.
1) Why is the code in the blog so complicated ?
2) Is that the idiomatic way to use the cake pattern ?
3) Why is the Users class needed in this example ?
4) Is that the way the cake pattern supposed to look like (with that seemingly unnecessary Users class?
5) Or is the simplified version just fine ?
At first it might look like it's complicated, but once you get familiar with that pattern it's just... boilerplaty and cumbersome. For every service of yours you have to create an accompanying component that will be wrapping that service. So in the provided example you have a UserRepository that's wrapped by a UserRepositoryComponent. And this is only the abstraction, so you need to have a concrete implementation for both the component and the service (i.e. UserRepositoryComponentImpl wrapping UserRepositoryImpl). And so far you have only one service that might be used in your modules, imagine the effort to create dozens of services ;)
Yes, this is the idiomatic way to use that pattern. However there are also other variations of that pattern, e.g. thin cake pattern or parfait (a term coined by Dick Wall)
You're asking about User, yet your code simplification was to remove Users, so I'll describe both of them. User is a simple case class, that should make that example more accessible/easier to grasp. Users however were not necessary here (it's just another intermediate level of abstraction), and in my opinion they bring some unnecessary noise to the example.
I would say that your simplified version shows exactly how cake pattern should look like. You have an abstract UserRepository wrapped inside of UserRepositoryComponent, you have a concrete implementation of both those traits, and you have some service (UserInfo) that requires the repository of users (which is "injected" using self-type annotation).
Already answered.
Related
I´ve been watching some examples of monads transformers with Cats, and I was trying to reproduce those in Scalaz
Here I have a for comprehension where I first receive an optional which I flatMap with OptionalT, and the second function return a Future of Employee.
Here my code
//Attributes for this example
sealed trait Employee {
val id: String
}
final case class EmployeeWithoutDetails(id: String) extends Employee
final case class EmployeeWithDetails(id: String, name: String, city: String, age: Int) extends Employee
case class Company(companyName: String, employees: List[EmployeeWithoutDetails])
trait HybridDBOps {
protected def getDetails(employeeId: String): Future[EmployeeWithDetails]
protected def getCompany(companyName: String): Option[Company]
}
class DbHybrid extends HybridDBOps {
override def getDetails(employeeId: String): Future[EmployeeWithDetails] = Future {
EmployeeWithDetails("1", "name", "city", 36)
}
override def getCompany(companyName: String): Option[Company] = Some(Company(companyName, List(EmployeeWithoutDetails("1"))))
}
def getEmployeeAgeScalaZHybrid(employeeId: String, companyName: String): Future[Option[Int]] = {
val db = new DbHybrid
val eventualOption = (for {
company <- OptionT.fromOption(db.getCompany(companyName)) --> Wont compile
if company.employees map (_.id) contains employeeId
details <- OptionT.liftF(db.getDetails(employeeId)) --> Wont compile
} yield details.age).run
eventualOption
}
This code is from cats version, and in scalaz the OptionT.fromOption to wrap a Option does not exist, I notice that I can do OptionT(Some(db.getCompany(companyName)) and then compile but now the signature of the method says that I returning an Optional instead of a future.
Also how can use the OptionT.liftF in ScalaZ
Here the complete example https://github.com/politrons/reactiveScala/blob/master/scala_features/src/main/scala/app/impl/scalaz/MonadTransformer.scala
Regards.
These should work as substitutes:
import scalaz.std.future._
import scalaz.syntax.monad._
// instead of OptionT.fromOption(db.getCompany(companyName))
OptionT(db.getCompany(companyName).pure[Future])
// instead of OptionT.liftF(db.getDetails(employeeId))
db.getDetails(employeeId).liftM[OptionT]
However, it would be good to have both methods also on OptionT. You could add them and open a pull request.
Any idea how to get the following to work:
trait HttpTransport {
def doGet(str: String): String
}
trait CoreGet {
def GET(str: String)(implicit imp:String): List[String]
}
trait VerbGet extends CoreGet with HttpTransport {
override def GET(str: String)(implicit imp:String): List[String]= {
println("->VerbGet.GET")
val str1 = doGet(str)
// and more biz logic calls here
List(s"\nverb (biz logic) called url $str and got '${str1}'>")
}
}
// PlayGet {
implicit class ExtendCoreGet(coreGet: CoreGet) {
def GET[A](url: String)(implicit imp:String, imp2: List[A]): List[A]= {
println(s"->ExtendCoreGet.GET($url)")
val coreGetResult = coreGet.GET(url)
coreGetResult.flatMap(_ => imp2)
}
}
trait Play extends HttpTransport {
override def doGet(str: String): String = {
println("->Play.doGet")
s"\nPlay.doGet($str)>"
}
}
val client = new VerbGet with Play
client.GET("www.go.com")("hello", List("1")) //<-- does not compile
Compiler error:
too many arguments (2) for method GET: (implicit imp:
String)List[String]
You can play with the code here:
https://scastie.scala-lang.org/arminio/tN9NfdxGQUmusrNL0lJ78w
It looks like you are trying extend functionality of VerbGet. You need to fix two things:
1. ExtendCoreGet must extend AnyVal to add more methods.
2. You can add new functionality by adding new method say GET2 but you can't overload existing method. Renmaed your GET to GET2 or something meaningful.
ExtendCoreGet definition should be
implicit class ExtendCoreGet(val coreGet: CoreGet) extends AnyVal {
def GET2[A](url: String)(implicit imp:String, imp2: List[A]): List[A]= {
coreGet.GET(url).flatMap(_ => imp2)
}
}
This question is related to this other question. There I can basically attach an interface implementation to a simple case class. The core is this bit:
case class UserRow(id: Long, username: String, firstName: String,
lastName : String, ...)
trait PluggableUserService extends be.objectify.deadbolt.scala.models.Subject {
override def roles: List[Role]
}
object PluggableUserService {
implicit class toPluggable(user: UserRow)(implicit userService: UserService)
extends PluggableUserService {
//------------------------------------------------------------------------
override def roles: List[Role] = {
userService.roles(user)
}
}
and then I can do:
val user : UserRow = UserRow(...)
user.roles
// or even nicer
val subject : Subject = UserRow(..)
subject.roles
but now I need to be able to do this but doesn't work:
val subject : Option[Subject] = Some(UserRow(...))
How can I extend the design above to cover this Option covariant assignment?
How about
implicit def toPluggableOpt(o: Option[UserRow])(implicit us: UserService) = o.map(new toPluggable(_))
Have an implicit def that wraps an existing implicit in Option:
implicit def toPluggableOpt[T](t: Option[T])(implicit ev: T => PluggableUserService): Option[PluggableUserService] = t.map(ev)
This works for all things that can be implicitly converted to PluggableUserService. It is also able to be easily generalized to any functor (List and the like). Given a Functor[F[_]] typeclass from the library of your choosing (scalaz/cats/whatever):
implicit def mapPluggable[T, F[_]: Functor](t: F[T])
(implicit ev: T => PluggableUserService): F[PluggableUserService] = implicitly[Functor[F[_]]].map(t)(ev)
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.
I am looking for a clean object-orientated way to model the following (in Scala):
A person can be:
A manager at some firm
A mathematician
A world-class tennis player
A hobbyist programmer
A volunteer at a local school
A creative painter
This suggests that we introduce a Person super-class and sub-classes:
class Manager
class Mathematician
class TennisPlayer
class HobbyistProgrammer
class Volunteer
class Painter
The Manager class has methods such as: getSalary(), workLongHours(), findNewJob(), etc. The TennisPlayer class has methods such as: getWorldRanking(), playGame(), strainAnkle(), etc. And so on. In addition there are methods in class Person such as becomeSick(). A sick manager loses his job and tennis player stops playing in the season.
Futhermore the classes are immutable. That is, for instance strainAnkle() returns a new TennisPlayer that that has a strained ankle, but where all other properties remain the same.
The question is now: How do we model the fact that a person can be both a Manager and a TennisPlayer?
It's important that the solution preserves both immutability and type-safety.
We could implement classes such as:
ManagerAndMathematician
ManagerAndTennisPlayerAndPainter
ManagerAndPainter
but this leads to a combinatorial explosion of classes.
We could also use traits (with state), but then how do we implement methods such as findNewJob(), which needs to return a new person with the same traits mixed in, but with a new state of the Manager trait. Similarly, how can we implement methods such as becomeSick()?
Question: How would you implement this in a clean OO-fashion in Scala? Remember: Immutability and type-safety are a must.
This does not look to me like an ideal case for inheritance. Maybe you're trying to force things into an inheritance pattern because it seems awkward to handle composition with immutable values. Here's one of several ways to do it.
object Example {
abstract class Person(val name: String) {
def occupation: Occupation
implicit val self = this
abstract class Occupation(implicit val practitioner: Person) {
def title: String
def advanceCareer: Person
}
class Programmer extends Occupation {
def title = "Code Monkey"
def advanceCareer = practitioner
}
class Student extends Occupation {
def title = "Undecided"
def advanceCareer = new Person(practitioner.name) {
def occupation = new Programmer
}
}
}
def main(args: Array[String]) {
val p = new Person("John Doe") { def occupation = new Student }
val q = p.occupation.advanceCareer
val r = q.occupation.advanceCareer
println(p.name + " is a " + p.occupation.title)
println(q.name + " is a " + q.occupation.title)
println(r.name + " is a " + r.occupation.title)
println("I am myself: " + (r eq r.occupation.practitioner))
}
}
Let's try it out:
scala> Example.main(Array())
John Doe is a Undecided
John Doe is a Code Monkey
John Doe is a Code Monkey
I am myself: true
So this works in a somewhat useful way.
The trick here is that you create anonymous subclasses of your person each time an occupation (which is an inner class) decides to change things up. Its job is to create a new person with the new roles intact; this is helped out by the implicit val self = this and the implicit constructor on Occupation which helpfully automatically loads the correct instance of the person.
You will probably want a list of occupations, and thus will probably want helper methods that will regenerate the list of professions. Something like
object Example {
abstract class Person(val name: String) {
def occupations: List[Occupation]
implicit val self = this
def withOccupations(others: List[Person#Occupation]) = new Person(self.name) {
def occupations = others.collect {
case p: Person#Programmer => new Programmer
case s: Person#Pirate => new Pirate
}
}
abstract class Occupation(implicit val practitioner: Person) {
def title: String
def addCareer: Person
override def toString = title
}
class Programmer extends Occupation {
def title = "Code Monkey"
def addCareer: Person = withOccupations( this :: self.occupations )
}
class Pirate extends Occupation {
def title = "Sea Monkey"
def addCareer: Person = withOccupations( this :: self.occupations )
}
}
def main(args: Array[String]) {
val p = new Person("John Doe") { def occupations = Nil }
val q = (new p.Programmer).addCareer
val r = (new q.Pirate).addCareer
println(p.name + " has jobs " + p.occupations)
println(q.name + " has jobs " + q.occupations)
println(r.name + " has jobs " + r.occupations)
println("I am myself: " + (r eq r.occupations.head.practitioner))
}
}
A clean object-oriented way of solving this does not have to be Scala-specific. One could adhere to the general object-oriented design principle of favoring composition over inheritance and use something like Strategy pattern, which is a standard way of avoiding class explosion.
I think this can be solved in a manner similar to type-safe builders.
The basic idea is to represent "state" through type parameters, and use implicits to control methods. For example:
sealed trait TBoolean
final class TTrue extends TBoolean
final class TFalse extends TBoolean
class Person[IsManager <: TBoolean, IsTennisPlayer <: TBoolean, IsSick <: TBoolean] private (val name: String) {
// Factories
def becomeSick = new Person[TFalse, IsTennisPlayer, TTrue](name)
def getBetter = new Person[IsManager, IsTennisPlayer, TFalse](name)
def getManagerJob(initialSalary: Int)(implicit restriction: IsSick =:= TFalse) = new Person[TTrue, IsTennisPlayer, IsSick](name) {
protected override val salary = initialSalary
}
def learnTennis = new Person[IsManager, TTrue, IsSick](name)
// Other methods
def playGame(implicit restriction: IsTennisPlayer =:= TTrue) { println("Playing game") }
def playSeason(implicit restriction1: IsSick =:= TFalse, restriction2: IsTennisPlayer =:= TTrue) { println("Playing season") }
def getSalary(implicit restriction: IsManager =:= TTrue) = salary
// Other stuff
protected val salary = 0
}
object Person {
def apply(name: String) = new Person[TFalse, TFalse, TFalse](name)
}
It can get very wordy, and if things get complex enough, you may need something like an HList. Here's another implementation, that separates concerns better:
class Person[IsManager <: TBoolean, IsTennisPlayer <: TBoolean, IsSick <: TBoolean] private (val name: String) {
// Factories
def becomeSick = new Person[TFalse, IsTennisPlayer, TTrue](name)
def getBetter = new Person[IsManager, IsTennisPlayer, TFalse](name)
def getManagerJob(initialSalary: Int)(implicit restriction: IsSick =:= TFalse) = new Person[TTrue, IsTennisPlayer, IsSick](name) {
protected override val salary = initialSalary
}
def learnTennis = new Person[IsManager, TTrue, IsSick](name)
// Other stuff
protected val salary = 0
}
object Person {
def apply(name: String) = new Person[TFalse, TFalse, TFalse](name)
// Helper types
type PTennisPlayer[IsSick <: TBoolean] = Person[_, TTrue, IsSick]
type PManager = Person[TTrue, _, _]
// Implicit conversions
implicit def toTennisPlayer[IsSick <: TBoolean](person: PTennisPlayer[IsSick]) = new TennisPlayer[IsSick]
implicit def toManager(person: PManager) = new Manager(person.salary)
}
class TennisPlayer[IsSick <: TBoolean] {
def playGame { println("Playing Game") }
def playSeason(implicit restriction: IsSick =:= TFalse) { println("Playing Season") }
}
class Manager(salary: Int) {
def getSalary = salary
}
To get better error messages you should use specialized versions of TBoolean (ie, HasManagerJob, PlaysTennis, etc), and the annotation implicitNotFound to go with it.