I want to use optics in Scala to not go down a nested structure but rather traverse the other way around like going to Parent from Child.
case class Parent(param1: Int)
class Child(param2: Int)
val parent = Parent(param1)
val child = Child(param2)
// I would like something of this sort.
val parentParam1 = Lens[Child, Parent](_.parent)
I have tried creating a trait WithParent[T] that the child class extends. For example -
trait WithParent[T] extends scala.AnyRef with scala.Product {
var parent: T = ???
}
case class Parent(param1: Int)
case class Child(param2: Int) extends WithParent[Parent]
object WithParentCheck extends App {
val a = Parent(1)
val b = Child(2)
val parentParam1 = Lens[Child, Parent](_.parent)
}
I have 2 questions -
Will this method work? If so can I define parent in the trait itself?
Is there a better approach to this question?
Edit:
Edit 2: Account has an unique customer identifier.
Real World Scenario
case class Customer(id: String, account: Account)
case class Account(id: String, balance: Double, customerId: String)
val account = Account('01', 100, '1')
val cust = Customer('1', account)
// Accessing customer from account
val customerToAccount = Lens[Customer, Account](_.account)
My question: Is the reverse possible? Accessing customer from the account object?
Logic for my approach:
trait MyParent[T] would contain a object/variable parent of type T which would be accessible from the case class extending it.
Thanks!
Related
Suppose I have two classes, Person and Business, that are extended by the trait Entity.
trait Entity
case class Person(name: String) extends Entity
case class Business(id: String) extends Entity
Assuming I cannot change Entity, Person and Business (they are in a different file and not to be changed) how can I define a function, say a printEntity, that prints the field name or id, depending on the entity? For example, given instances of Person and Business, how can I do something like this:
object Main extends App {
val person1: Person = Person("Aaaa Bbbb")
val business1: Business = Business("0001")
// How can I do something like this?
person1.printEntity // would call a function that executes println(id)
business1.printEntity // would call a function that executes println(name)
}
Any ideas are appreciated! Sorry for the lack of context, I am still learning!
This is done via so called "extension methods". In scala 2 this is achieved using implicit wrapper class:
trait Entity
case class Person(name: String) extends Entity
case class Business(id: String) extends Entity
implicit class PersonWrapper(val p: Person) extends AnyVal {
def printEntity(): Unit = {
println(p.name)
}
}
implicit class BusinessWrapper(val b: Business) extends AnyVal {
def printEntity(): Unit = {
println(b.id)
}
}
val person1: Person = Person("Aaaa Bbbb")
val business1: Business = Business("0001")
person1.printEntity()
business1.printEntity()
// prints:
//Aaaa Bbbb
//0001
Note, x.printEntity can be called without parentheses, but, by convention, methods with Unit result type and side effects should be called with explicit empty parentheses.
UPD: As #DmytroMitin pointed out, you should extend implicit wrapper classes from AnyVal. This allows the compiler to avoid actually allocating wrapper class instances at runtime, improving performance.
case class Person(name: String,
override val age: Int,
override val address: String
) extends Details(age, address)
class Details(val age: Int, val address: String)
val person = Person("Alex", 33, "Europe")
val details = person.asInstanceOf[Details] // ???
println(details) // I want only Details class fields
I have these 2 classes. In reality, both have a lot of fields. Somewhere, I need only field of superclass, taken from Person class.
There is a nice way to get only super class values and not mapping them field by field?
*I'm pretty sure I'll have some problems with json writes for class Details (which is not a case class and have not a singleton object, but this is another subject)
If I get your question correctly, then you might be asking me runtime polymorphism or dynamic method dispatch from java.
If so, you may have to create both the class and not case class
class Details( val age: Int, val address: String)
class Person(name: String,
override val age: Int,
override val address: String
) extends Details(age, address) {
}
Now create the object of person and reference to superclass (Details)
val detail:Details = new Person("Alex", 33, "Europe")
println(detail.address)
println(detail.age)
This way you will be able to get the only address and age
Another way is like , why can't we create the Details a separate entity like:
case class Details( age: Int, address: String)
case class Person(name: String,
details: Details
)
val detail = Person("Alex", Details(10,"Europe") )
Output:
println(detail.details)
Details(10,Europe)
I will post a solution that leverages scala macro system (old kind, not the newest introduced with Scala 3.0). It could be an overkill for you...
BTW, if you want to access to only parent values (for example for getting key, value pair), you can:
given a type tag, get all parents;
from them, extract all the accessors (vals);
for each val, get its value;
and finally returns a list with all accessors taken
So, I try to solve each point step by step.
First of all, we have to write the macro definition as:
object Macros {
def accessors[T](element : T): String = macro MacrosImpl.accessors[T]
}
object MacrosImpl {
def accessors[T: c.WeakTypeTag](c: whitebox.Context): c.Expr[String] = ...
}
for the first point, we can leverage the reflection macroprogramming API using c.universe:
import c.universe._
val weakType = weakTypeTag[T] //thanks to the WeakTypeTag typeclass
val parents = weakType.tpe.baseClasses
for the second point, we can iterate over the parent classes and then take only the public accessors:
val accessors = parents
.map(weakType.tpe.baseType(_))
.flatMap(_.members)
.filter(_.isPublic)
.filter(_.isMethod)
.map(_.asMethod)
.filter(_.isAccessor)
.toSet
So, for example, if the we write Macros.accessors[Details](person), accessors will yield age and address.
To take the value, we can leverage quasiqouting. So, first we take only the values name:
val names = accessors
.map(_.fullName)
.map(_.split("\\."))
.map(_.reverse.head)
Then we convert them into a TermName:
val terms = names.map(TermName(_))
And finally, we convert each term to a key value tuple containing the val name and its value:
val accessorValues = terms
.map(name => c.Expr[(String, Any)](q"(${name.toString}, ${element}.${name})"))
.toSeq
The last step consist in convert a Seq[Expr[(String, Any)] into a Expr[Seq[(String, Any)]. A way to do that, could be leveraging recursion, reify, and splicing expression:
def seqToExprs(seq: Seq[Expr[(String, Any)]]): c.Expr[Seq[(String, Any)]] =
seq.headOption match {
case Some(head) =>
c.universe.reify(
Seq((head.splice._1, head.splice._2)) ++
seqToExprs(seq.tail).splice
)
case _ => c.Expr[Seq[(String, Any)]](q"Seq.empty")
}
So now I decide to return a String representation (but you can manipulate it as you want):
val elements = seqToExprs(accessorValues)
c.Expr[String](q"${elements}.mkString")
You can use it as:
import Macros._
class A(val a : Int)
class B(val b : Int) extends A(b)
class C(val c: Int) extends B(c)
//println(typeToString[List[Set[List[Double]]]])
val c = new C(10)
println(accessors[C](c)) // prints (a, 10)(b, 10)(c, 10)
println(accessors[B](c)) // prints (a, 10)(b, 10)
println(accessors[A](c)) // prints (a, 10)
And, using your example:
// Your example:
case class Person(name: String,
override val age: Int,
override val address: String
) extends Details(age, address)
class Details(val age: Int, val address: String)
val person = Person("Alex", 33, "Europe")
println(accessors[Details](person)) // prints (address,Europe)(age,33)
println(accessors[Person](person)) // prints (address,Europe)(age,33)(name,Alex)
Here there is a repository with the macro implemented.
Scala 3.0 introduce a safer and cleaner macro system, if you use it and you want to go further you can read these articles:
macros tips and tricks
short tutorial
another tutorial
Say I have 2 case classes:
case class Basic(id: String, name) extends SomeBasicTrait
case class Info (age: Int, country: String, many other fields..) extends SomeInfoTrait
and want to create a case class that has all fields from both of those case classes. This is a possible way:
case class Full(bs: Basic, meta: Info) extends SomeBasicTrait with SomeInfoTrait {
val id = bs.id
val name = bs.name
val age = meta.age
val country = meta.country
// etc
}
But it's a lot of boilerplate code. Is there any way to avoid this?
I couldn't find a way to achieve this in Shapeless but maybe there is..
[Update]
#jamborta 's comment helps and is basically this:
case class FullTwo(id: String, name: String, age:Int, country:String)
val b = Basic("myid", "byname")
val i = Info(12, "PT")
Generic[FullTwo].from(Generic[Basic].to(b) ++ Generic[Info].to(i))
The problem with this solution is that it still requires defining every field in the arguments of the FullTwo class, so that every time a change to Basic or Info is made, we also have to remember to change FullTwo as well.
Is there any way to create dynamically at compile time a case class equal to FullTwo?
Let's say I have a following case class:
case class Product(name: String, categoryId: Option[Long]/*, other fields....*/)
Here you can see that categoryId is optional.
Now let's say I have a following method in my DAO layer:
getCategoryProducts(): List[Product] = {
// query products that have categoryId defined
}
You see, that this method returns products, that are guaranteed to have categoryId defined with some value.
What I would like to do is something like this:
trait HasCategory {
def categoryId_!: Long
}
// and then specify in method signature
getCategoryProducts(): List[Product with HasCategory]
This will work, but then such a product will have two methods: categoryId_! and categoryId that smells bad.
Another way would be:
sealed trait Product {
def name: String
/*other fields*/
}
case class SimpleProduct(name: String, /*, other fields....*/) extends Product
case class ProductWithCategory(name: String, categoryId: Long/*, other fields....*/) extends Product
def getCategoryProducts: List[ProductWithCategory] = ...
This method helps to avoid duplicate methods categoryId and categoryId_!, but it requires you to create two case classes and a trait duplicating all the fields, which also smells.
My question: how can I use Scala type system to declare this specific case without these fields duplications ?
Not sure how much this will scale for your particular case, but one solution that comes to mind is to parameterize over the Option type using a higher-kinded generic type:
object Example {
import scala.language.higherKinds
type Id[A] = A
case class Product[C[_]](name: String, category: C[Long])
def productsWithoutCategories: List[Product[Option]] = ???
def productsWithCategories: List[Product[Id]] = ???
}
A way to do it is to use type classes -
import scala.language.implicitConversions
object Example {
sealed class CartId[T]
implicit object CartIdSomeWitness extends CartId[Some[Long]]
implicit object CartIdNoneWitness extends CartId[None.type]
implicit object CartIdPresentWitness extends CartId[Long]
case class Product[T: CartId](name: String, categoryId: T /*, other fields....*/)
val id: Long = 7
val withId = Product("dsds", id)
val withSomeId = Product("dsds", Some(id))
val withNoneId = Product("dsds", None)
val presentId: Long = withId.categoryId
val maybeId: Some[Long] = withSomeId.categoryId
val noneId: None.type = withNoneId.categoryId
val p = Product("sasa", true) //Error:(30, 18) could not find implicit value for evidence parameter of type com.novak.Program.CartId[Boolean]
}
This solution involves some code and dependent on implicits but does what you're trying to achieve.
Be aware that this solution is not completely sealed and can be 'hacked'. You can cheat and do something like -
val hack: Product[Boolean] = Product("a", true)(new CartId[Boolean])
val b: Boolean =hack.categoryId
For some more - advanced solutions which include
* Miles Sabin (#milessabin)’s Unboxed union types in Scala via the Curry-Howard isomorphism
* Scalaz / operator
http://eed3si9n.com/learning-scalaz/Coproducts.html
I have a Base class have some function and val, I want to inherent them in my inherent case class how to do it ?
This is my base class:
class Base(val name:String, val number:int) extends Sometrait {
def copy(name:String=this.name, number:int=this.number){
new Base(name, number)
}
}
I want to write the:
case class SomeCase(val name:String, val number:int, val id:int)extends Base(String, number){
...
}
But the compiler always told me:
value **** needs `override' modifier social.scala /scalatest/src/scalatest line 35 Scala
But I really want to is just do as inherent not override, how to do it.
(I need to put the child class as case class, as it is easy for me to use in slick. (here is my another question for how to use class as table content class in slick, someone give me really great answer, but I still mass.))
Because name and number fields in both Base and SomeCase are defined with val without any modifiers like private, they are both public members and participate in inheritance. Because these fields have same names in base and child classes, you have to add override modifier before val name and val number in the child class:
case class SomeCase(override val name: String,
override val number: Int,
val id: Int) extends Base(name, number) { ... }