Accessing fields of trait of an object instance - scala

I created this little Scala worksheet about inheritance and traits in Scala. As far as I understood, one is able to add traits to single object instances and access the methods of said trait in through that object.
So I mixed in Parents with the f4 object of sister, yet I can't access the methods of Parents. The same goes for f1 and Grandparents.
abstract class FamilyMember(name: String, birthDate: String, placeOfBirth: String) {
override def toString: String = this.name;
}
trait Parents extends FamilyMember{
var children: Array[FamilyMember] = new Array[FamilyMember](0);
def printChildren(): Unit = for (child <- children ) println(child.toString)
def isParent(): Boolean = true
}
trait GrandParents extends Parents {
def printGrandChildren(): Unit = for (child <- children; child2 <- child.asInstanceOf[Parents].children) println(child2.toString)
}
class Mother(name: String, birthDate: String, placeOfBirth: String) extends FamilyMember(name, birthDate, placeOfBirth) with Parents {
}
class Sister(name: String, birthDate: String, placeOfBirth: String) extends FamilyMember(name, birthDate, placeOfBirth) {
}
val f1: Mother = new Mother("Hildegard", "23-7-1952", "Berlin") with GrandParents
val f4: Sister = new Sister("Moni", "19-12-1993", "Frankfurt am Main") with Parents
val f5: Sister = new Sister("Anne", "10-12-2012","Berlin")
//works fine:
f1.isParent
f1.children = f1.children :+ f4
//doesn't work:
f4.isParent
f4.children = f5.children :+ f5
f4.printChildren()
f1.printGrandChildren()
The error I get:
Error:(30, 4) value isParent is not a member of Sister
f4.isParent
But I added it, with the trait.

To be somewhat more explicit: by writing
val f4: Sister = ...
you specify the static type of f4 is Sister and so the compiler will only let you call Sister's methods, whatever ... is (provided it can have type Sister, of course).

As #Luis Miguel Mejía Suárez has pointed out in the comments:
Sister is not a Parent... Try with val f4: Sister with Parent, or
leave type inference do its job. - BTW, a var in a trait is a really
bad idea.
Using the following code it works:
val f1: Mother with GrandParents= new Mother("Hildegard", "23-7-1952", "Berlin") with GrandParents
val f4: Sister with Parents = new Sister ("Moni", "19-12-1993", "Frankfurt am Main") with Parents
val f5: Sister = new Sister("Anne", "10-12-2012","Berlin")

Related

Generic function for case class creation with tuple does not compile

I shall explain my question with example as shown below.
import scala.reflect.ClassTag
trait LivingBeing extends Product { def name:String; def age:Int}
case class Person (name:String, age:Int) extends LivingBeing
case class Cat(name: String, age:Int) extends LivingBeing
// usual way of creating a case class instance
val john = Person("john", 23)
// Creating a case class instance with tuples
val garfield = Cat tupled ("Garfield", 8)
// create a generic function
def createLivingBeing[T<: LivingBeing](name:String, age:Int)(implicit evidence: ClassTag[T]): T = {
T tupled (name, age) // Does not compile; why?
}
How can one elegantly construct different case classes (that are of a certain trait) generically, given a type and values for its fields?
Consider type class solution
trait LivingBeingFactory[T <: LivingBeing] {
def apply(name: String, age: Int): T
}
object LivingBeingFactory {
implicit val personFactory: LivingBeingFactory[Person] =
(name: String, age: Int) => Person(name, age)
implicit val catFactory: LivingBeingFactory[Cat] =
(name: String, age: Int) => Cat(name, age)
}
def createLivingBeing[T <: LivingBeing](name:String, age:Int)(implicit evidence: LivingBeingFactory[T]): T = {
evidence(name, age)
}
createLivingBeing[Person]("Picard", 70)
// res0: Person = Person(Picard,70)
createLivingBeing[Cat]("Bob", 5)
// res1: Cat = Cat(Bob,5)
// Creating a case class instance with tuples
val garfield = Cat tupled ("Garfield", 8)
...which is the equivalent of...
val garfield = (Cat.apply _).tupled(("Garfield", 8))
This, on the other hand...
T tupled (name, age) // Does not compile; why?
...produces Error: not found: value T because T is a type, not a value. Cat is both a type and a value. It is the type specified for the class, but it is also the companion object to the class Cat. All case classes have a companion object with an apply() method. The compiler knows the difference between them and it knows where one can be used/referenced but not the other.

OptionalT fromOptional and liftF in ScalaZ

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.

Scala: handle different constructor when extending a parameterised Class

I am writing a scala code, and want to handle different constructor when extending a parameterised Class. For example:
class Person (val name:String, val age: Int)
class Employee(name:String, age:Int, val position:String)
extends Person(name, age)
However, what I want is that Employee can have two constructors one of which takes information of Person to construct, one takes another Employee to construct:
val emply1 = new Employee(yzh, 30, CEO)
val emply2 = new Employee(emply1)
If I want both constructors works, how can I do?
Thanks!
If you want two constructors, you simply write two constructors:
class Person(val name: String, val age: Int)
class Employee(name: String, age: Int, val position: String) extends
Person(name, age) {
def this(e: Employee) = this(e.name, e.age, e.position)
}
val emply1 = new Employee("yzh", 30, "CEO")
val emply2 = new Employee(emply1)
You need to add an axillary constructor.
class Employee(name:String, age:Int, val position:String)
extends Person(name, age) {
def this(emp: Employee) = this(emp.name, emp.age, emp.position)
}
If your Employee were a case class then you could do this instead.
val emply1 = Employee("yzh", 30, "CEO")
val emply2 = emply1.copy()

Can we use the singleton .type as a type parameter?

I was cobbling together an answer to this question: Scala mixin to class instance, where I showed a way of "mixing-in" another trait or class instance to an existing instance:
case class Person(name: String)
val dave = Person("Dave")
val joe = Person("Joe")
trait Dog { val dogName: String }
val spot = new Dog { val dogName = "Spot" }
implicit def daveHasDog(p: dave.type) = spot
dave.dogName //"Spot"
joe.dogName //error: value dogName is not a member of Person
So after the local implicit def, dave can effectively be used as a Person with Dog. My question is, if we wanted to define a method that takes a Person instance only where the Person has a Dog, how do we do it?
I can define a method such as
def showDog(pd: Person with Dog) = pd.name + " shows " + pd.dogName
however this is no good for dave since he is still just a Person, despite his implicit transformation abilities.
I tried defining
trait Dog [T] { val dogName: String }
val spot = new Dog [dave.type] { val dogName = "Spot" }
def showDog(p: Person)(implicit dog: Dog[p.type]) = ...
but this is not legal, giving error: illegal dependent method type. Any ideas?
If you compile with -Ydependent-method-types, your original code will work with this definition of showDog:
scala> def showDog(p: Person)(implicit ev: p.type => Dog) = p.name + " shows " + p.dogName
showDog: (p: Person)(implicit ev: p.type => Dog)java.lang.String
scala> showDog(dave)
res1: java.lang.String = Dave shows Spot

Combinatorial Subtyping (in Scala)

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.