-
Notifications
You must be signed in to change notification settings - Fork 2
Description
I'd like to have more Contains macro generators, covering common use cases.
Few come to mind:
1. A sealed family form for GenContains
example:
sealed trait User{
def id: String
}
case class Registered(id: String, name: String, email: String) extends User
case class Anonymous(id: String, ip: String) extends UserHere GenContains[User](_.id) must go through all the direct subtypes of User and verify that GenContains(_.id) resolves to lens. Example of generated tree:
val registeredC = GenContains[Registered](_.id)
val anonymousC = GenContains[Anonymous](_.id)
new Contains[User, String] {
def set(s: User, b: String) = s match {
case r: Registered => registeredC.set(r, b)
case a: Anonymous => anonymousC.set(a, b)
}
def extract(s: User): String = s match {
case r: Registered => registeredC.extract(r)
case a: Anonymous => anonymousC.extract(a)
}
}The proposed solution should work even for the case when path is longer that one field, or one of the direct subtypes is a sealed family itself
2. Typeclass based sealed Contains derivation
Ability to derive Contains when each alternative has implicit Contains to the given type.
In the best form it should support recursion and GADTs
case class Path(str: String)
sealed trait ValidatedField[A] {
def check[B](f: A => Either[String, B]): ValidatedField[B] = Check(this, f)
}
@ClassyOptics
case class Read(path: Path) extends ValidatedField[String]
@Optics
case class Check[A, B](inner: ValidatedField[A], verify: A => Either[String, B]) extends ValidatedField[B]
implicit def checkPath[A, B]: Contains[Check[A, B], Path] = Check.inner >> validatedPath
implicit def validatedPath[A]: Contains[ValidatedField[A], Path] = DeriveContains[ValidatedField[A], Path]latter should be expanded to
implicit def validatedPath[A]: Contains[ValidatedField[A], Path] = {
new Contains[ValidatedField[A], Path] {
def set(s: ValidatedField[A], b: Path): ValidatedField[A] = s match {
case r: Read => Contains[Read, Path].set(r, b)
case c: Check[x, A] => Contains[Check[x, A], Path].set(c, b)
}
def extract(s: ValidatedField[A]): Path = s match {
case r: Read => Contains[Read, Path].extract(r)
case c: Check[x, A] => Contains[Check[x, A], Path].extract(c)
}
}
}3. Typeclass based case class to case class derivation
When two case classes have fields related to each other, sometimes we can derive Contains for them. Such case could be a partial replacement for complex data transformation libraries such as https://github.com/scalalandio/chimney
We will derive Contains[A, B] when :
- Each field in
Bhas a field with the same name in theA - For any two fields with the same name of types
AFandBFwe haveAF =:= BForContains[AF, BF]
Latter could be relaxed providing implicit `Contains[A, A] during derivation
case class RichName(name: String, searchId: Long)
object RichName {
implicit val nameString: Contains[RichName, String] = GenContains.apply(_.name)
}
case class UserData(
firstName: String,
lastName: String,
age: Int
)
case class UserInfo(
id: Long,
firstName: RichName,
lastName: RichName,
age: Int,
lastUpdated: Instant,
)
val infoData = DeriveContains[UserInfo, UserData]last line should be expanded to the following
val infoData = new Contains[UserInfo, UserData] {
implicit def sameConv[A]: A Contains A = Same.id
def set(s: UserInfo, b: UserData): UserInfo =
s.copy(
firstName = Contains[RichName, String].set(s.firstName, b.firstName),
lastName = Contains[RichName, String].set(s.lastName, b.lastName),
age = Contains[Int, Int].set(s.age, b.age),
)
def extract(s: UserInfo): UserData = UserData(
firstName = Contains[RichName, String].extract(s.firstName),
lastName = Contains[RichName, String].extract(s.lastName),
age = Contains[Int, Int].extract(s.age)
)
}