I am working on a side project, trying to implement immutable aggregates in Scala. My idea is to have base trait AggregateRoot with some common behaviors. Child classes would be actual aggregates modeled as a case classes. For now, there is one thing that I don't like, and that is that I cannot call copy method from the base trait for two reasons:
I don't have access to copy method inside of the base trait
I have no idea how many parameters copy method will have
I have some basic knowledge of shapeless library and I thought that it might help in this case. My idea is pass list of tagged fields to the base method in the trait which will replace them and return new instance of the case class.
As a step in that direction I am trying to create method that will copy one single field for the beginning using shapeless, but I am getting the same error all the time, that compiler cannot find the implicit for updater.
Here is the simplified code fragment that I am trying to use:
import shapeless._
import shapeless.labelled.FieldType
import shapeless.ops.record.Updater
import shapeless.record._
import shapeless.syntax.singleton._
trait AggregateRoot[T <: AggregateRoot[T]] {
self: T =>
def makeCopy[L <: HList, K, V](ft: FieldType[K, V])(
implicit
labeledGen: LabelledGeneric.Aux[T, L],
updater: Updater.Aux[L, FieldType[K, V], L],
witness: Witness.Aux[K]): T = {
val labeledHList = labeledGen.to(this)
val result = labeledHList.updated(witness, ft)
labeledGen.from(result)
}
}
case class User(id: String, age: Int) extends AggregateRoot[User]()
val user1 = User("123", 10)
val ageChange = "age" ->> 22
val user2 = user1.makeCopy(ageChange)
Since I am not experienced shapeless user, I am not sure why it cannot find requested implicit. Version of shapeless is 2.3.3.
As far as I understood from this great answer: How to generically update a case class field using LabelledGeneric? - you can't have Updater in general case, because Shapeless need to derive it for each particular field in case class, which means instead of having general purpose makeCopy you will need to have method for each particular field, like:
import shapeless._, record._, ops.record._, labelled._, syntax.singleton._, tag._
trait AggregateRoot[T <: AggregateRoot[T]] {
self: T =>
import AggregateRoot._
def withAge[L <: HList, K, V](age: Int)(implicit
gen: LabelledGeneric.Aux[T, L],
upd: Updater.Aux[L, F, L]
): T = {
val ageField = AgeField(age)
gen.from(upd(gen.to(this), ageField))
}
}
object AggregateRoot {
type AgeField = Symbol with Tagged[Witness.`"age"`.T]
val AgeField = field[AgeField]
type F = FieldType[AgeField, Int]
}
import AggregateRoot._
case class User(id: String, age: Int) extends AggregateRoot[User]
object User {
implicit val gen = LabelledGeneric[User]
}
val user1 = User("123", 10)
val ageChange = "age" ->> 22
val user2 = user1.withAge(ageChange)
val test = User("test-id", 20)
println("test.withAge(42) = " + test.withAge(20))
println("test.withAge(12).withAge(42) = " + test.withAge(12).withAge(42))
Scatie: https://scastie.scala-lang.org/kDnL6HTQSEeSqVduW3EOnQ
Related
I'm trying to generalize some sort of isIntanceOf based on a sealed hierarchy, but haven't been successful. The example below demonstrates what I want to achieve:
sealed abstract class Context
object Context {
case object Context1 extends Context
case class Context2(someInfo: String) extends Context
case object Context3 extends Context
}
case class ContextHolder(id: String, contexts: Set[Context])
import Context._
val holder = ContextHolder("1", Set(Context1, Context2("Other info")))
val contains1Or2 = holder.contexts.contains(Context1) || holder.contexts.exists(_.isInstanceOf[Context2])
val contains3 = holder.contexts.contains(Context3)
println(s"contains1Or2: $contains1Or2")
println(s"contains3: $contains3")
Basically I want to generalize the code of contains1Or2 and contains3 in some sort of:
def containsAnyOf[T <: Context: ClassTag](holder: ContextHolder, contexts: T*): Boolean
And then be able to use it like:
val contains1Or2 = containsAnyOf(holder, Context1, Context2)
val contains3 = containsAnyOf(holder, Context3)
I've already tried a few different approaches, but couldn't get it to work so far. Any ideas? Thanks.
The best I could come up was this:
def containsAnyOf(holder: ContextHolder)(contexts: PartialFunction[Context, Unit]): Boolean = {
val p = contexts.lift.andThen(_.isDefined)
holder.contexts.exists(p)
}
Which you can use like this:
val contains1Or2 = containsAnyOf(holder) {
case Context1 =>
case Context2(_) => // or case _: Contaxt2 =>
}
val contains3 = containsAnyOf(holder) {
case Context3 =>
}
Code running here.
Shapeless's LiftAll type class is very handy here.
import shapeless.ops.hlist.{LiftAll, ToTraversable}
import shapeless.{Generic, HList}
final class ContainsMulti[T <: Product](private val dummy: Boolean = true) extends AnyVal {
def apply[H <: HList, O <: HList](holder: ContextHolder)(implicit
toHList: Generic.Aux[T, H],
classTags: LiftAll.Aux[ClassTag, H, O],
allCtxts: LiftAll[({type E[T] = T <:< Context})#E, H],
toList: ToTraversable.Aux[O, List, ClassTag[_]]
) = classTags.instances.toList
.map(_.runtimeClass)
.forall(klazz => holder.contexts.exists(klazz.isInstance))
}
def containsMulti[T <: Product] = new ContainsMulti[T]
Usage:
val contains1Or2 = containsMulti[(Context1.type, Context2)](holder)
val contains3 = containsMulti[Tuple1[Context3.type]](holder)
val contains1and3 = containsMulti[(Context1.type, Context3.type)](holder)
val check = containsMulti[(Int, String)](holder) //doesn't compile
println(s"contains1Or2: $contains1Or2") //true
println(s"contains3: $contains3") //false
println(s"contains1and3: $contains1and3")//false
See it run.
All the implicits may seem daunting, but it's actually very simple. The extra class is so we can take multiple type parameter lists, where the first is explicitly given, and the second is inferred. Generic turns the inputted tuple into an HList, LiftAll finds the ClassTag instances for each of the types in the tuple, and ToTraversable turns the HList containing those class tags back into a List.
As Luis Miguel Mejía Suárez suggested, I have made ContainsMulti a value class, using the partially applied type trick to avoid object creation. They also pointed out that this approach initially allowed (Int, String) as input, so now allCtxts checks that all the types in the HList extend Context.
Using context bounds in scala you can do stuff like
trait HasBuild[T] {
def build(buildable: T): Something
}
object Builders {
implict object IntBuilder extends HasBuild[Int] {
override def build(i: Int) = ??? // Construct a Something however appropriate
}
}
import Builders._
def foo[T: HasBuild](input: T): Something = implicitly[HasBuild[T]].build(1)
val somethingFormInt = foo(1)
Or simply
val somethingFromInt = implicitly[HasBuild[Int]].build(1)
How could I express the type of a Seq of any elements that have an appropriate implicit HasBuild object in scope? Is this possible without too much magic and external libraries?
Seq[WhatTypeGoesHere] - I should be able to find the appropriate HasBuild for each element
This obviously doesn't compile:
val buildables: Seq[_: HasBuild] = ???
Basically I'd like to be able to handle unrelated types in a common way (e.g.: build), without the user wrapping them in some kind of adapter manually - and enforce by the compiler, that the types actually can be handled. Not sure if the purpose is clear.
Something you can do:
case class HasHasBuild[A](value: A)(implicit val ev: HasBuild[A])
object HasHasBuild {
implicit def removeEvidence[A](x: HasHasBuild[A]): A = x.value
implicit def addEvidence[A: HasBuild](x: A): HasHasBuild[A] = HasHasBuild(x)
}
and now (assuming you add a HasBuild[String] for demonstration):
val buildables: Seq[HasHasBuild[_]] = Seq(1, "a")
compiles, but
val buildables1: Seq[HasHasBuild[_]] = Seq(1, "a", 1.0)
doesn't. You can use methods with implicit HasBuild parameters when you have only a HasHasBuild:
def foo1[A](x: HasHasBuild[A]) = {
import x.ev // now you have an implicit HasBuild[A] in scope
foo(x.value)
}
val somethings: Seq[Something] = buildables.map(foo1(_))
First things first, contrary to some of the comments, you are relying on context bounds. Requesting an implicit type class instance for a T is what you call a "context bound".
What you want is achievable, but not trivial and certainly not without other libraries.
import shapeless.ops.hlist.ToList
import shapeless._
import shapeless.poly_
object builder extends Poly1 {
implicit def caseGeneric[T : HasBuilder] = {
at[T](obj => implicitly[HasBuilder[T]].build(obj))
}
}
class Builder[L <: HList](mappings: L) {
def build[HL <: HList]()(
implicit fn: Mapper.Aux[builder.type, L, HL],
lister: ToList[Something]
) = lister(mappings map fn)
def and[T : HasBuilder](el: T) = new Builder[T :: L](el :: mappings)
}
object Builder {
def apply[T : HasBuilder](el: T) = new Builder(el :: HNil)
}
Now you might be able to do stuff like:
Builder(5).and("string").build()
This will call out the build methods from all the individual implcit type class instances and give you a list of the results, where every result has type Something. It relies on the fact that all the build methods have a lower upper bound of Something, e.g as per your example:
trait HasBuild[T] {
def build(buildable: T): Something
}
An example case class: Was trying to create a generic query builder
case class BaseQuery(operand: String, value: String)
case class ContactQuery(phone: BaseQuery, address: BaseQuery)
case class UserQuery(id: BaseQuery, name: BaseQuery, contact: ContactQuery)
val user = UserQuery(BaseQuery("equal","1"), BaseQuery("like","Foo"), ContactQuery(BaseQuery("eq","007-0000"),BaseQuery("like", "Foo City")))
//case class Contact(phone: String, address: String)
//case class User(id: Long, name: String, contact: Contact)
//val user = User(1, "Foo Dev", Contact("007-0000","Foo City"))
How can we retrieve the field names and respective values in scala,
On further research,
Solutions using Scala Reflection:
def classAccessors[T: TypeTag]: List[MethodSymbol] = typeOf[T].members.collect {
case m: MethodSymbol if m.isCaseAccessor => m
}.toList
// The above snippet returns the field names, and as input we have to
//feed the 'TypeTag' of the case class. And since we are only feeding a
//TypeTag we will not have access to any object values. Is there any Scala
// Reflection variant of the solution.
Another solution,
def getMapFromCaseClass(cc: AnyRef) =
(scala.collection.mutable.Map[String, Any]() /: cc.getClass.getDeclaredFields)
{
(a, f) =>
f.setAccessible(true)
a + (f.getName -> f.get(cc))
}
// The above snippet returns a Map[String, Any] if we feed the case class
//object. And we will need to match the map value to any of our other case
//classes. If the class structure were simple, the above solution would be
//helpful, but in case of complex case class this would not be the most efficient solution.
Trying to:
I am actually trying to retrieve the list of field and their values during runtime.
One of the use cases,
val list: Seq[Somehow(Field+Value)] = listingFieldWithValue(obj: UserQuery)
for(o <- list){
o.field.type match{
case typeOne: FooType =>{
//Do something
}
case typeTwo: FooBarType =>{
//Do something else
}
}
}
Not really scala-reflect solution, but shapeless one. Shapeless is built on top of implicit macro, so it way faster than any reflection, if you have type information at compile time.
For implementation of your updated requirements you'll need some time to carry needed type information
sealed trait TypeInfo[T]{
//include here thing you like to handle at runtime
}
Next you can define such info for different types and provide implicit resolution. In my example such implementation are also suitable for later matching
implicit case object StringField extends TypeInfo[String]
case class NumericField[N](implicit val num: Numeric[N]) extends TypeInfo[N]
implicit def numericField[N](implicit num: Numeric[N]): TypeInfo[N] = new NumericField[N]
case class ListField[E]() extends TypeInfo[List[E]]
implicit def listField[E] = new ListField[E]
Then we define more suitable for pattern matching result structure
sealed trait FieldDef
case class PlainField[T](value: Any, info: TypeInfo[T]) extends FieldDef
case class EmbeddedType(values: Map[Symbol, FieldDef]) extends FieldDef
So any result will be either value container with needed type info or container for deeper look.
Finally we can define concept implementation
import shapeless._
import shapeless.labelled.FieldType
import shapeless.ops.hlist.{ToTraversable, Mapper}
sealed abstract class ClassAccessors[C] {
def apply(c: C): Map[Symbol, FieldDef]
}
trait LowPriorClassInfo extends Poly1 {
implicit def fieldInfo[K <: Symbol, V](implicit witness: Witness.Aux[K], info: TypeInfo[V]) =
at[FieldType[K, V]](f => (witness.value: Symbol, PlainField(f, info)))
}
object classInfo extends LowPriorClassInfo {
implicit def recurseInfo[K <: Symbol, V](implicit witness: Witness.Aux[K], acc: ClassAccessors[V]) =
at[FieldType[K, V]](f => (witness.value: Symbol, EmbeddedType(acc(f))))
}
implicit def provideClassAccessors[C, L <: HList, ML <: HList]
(implicit lgen: LabelledGeneric.Aux[C, L],
map: Mapper.Aux[classInfo.type, L, ML],
toList: ToTraversable.Aux[ML, List, (Symbol, FieldDef)]) =
new ClassAccessors[C] {
def apply(c: C) = toList(map(lgen.to(c))).toMap
}
def classAccessors[C](c: C)(implicit acc: ClassAccessors[C]) = acc(c)
now
classAccessors(User(100, "Miles Sabin", Contact("+1 234 567 890", "Earth, TypeLevel Inc., 10")))
will result to
Map(
'id -> PlainField(100, NumericField( scala.math.Numeric$LongIsIntegral$#...)),
'name -> PlainField("Miles Sabin", StringField),
'contact -> EmbeddedType( Map(
'phone -> PlainField( "+1 234 567 890", StringField),
'address -> PlainField("Earth, TypeLevel Inc., 10", StringField))))
Given this case class case class Location(id: BigInt, lat: Double, lng: Double)
and a list of strings List("87222", "42.9912987", "-93.9557953")
I would like to do something like Location.fromString(listOfString) in a way Location instance is created with string types converted into appropriate types.
Only way I can think of is define fromString method in each case class but I'm looking for a more generic solution each class can inherit. For example, I would have case class Location(...) extends Record, where Record implements fromString that does conversion based on argument types defined in Location class.
Normally for best performance in such you need to create some macro.
Luckily there is beautiful shapeless library with which you could create such generic reader with near-macro performance in no time using it's generic case class representation
First define typeclass and some instances for reading your fields:
trait Read[T] {
def fromString(s: String): T
}
implicit object readBigInt extends Read[BigInt] {
def fromString(s: String): BigInt = BigInt(s)
}
implicit object readDouble extends Read[Double] {
def fromString(s: String): Double = s.toDouble
}
Next define your main typeclass:
trait ReadSeq[T] {
def fromStrings(ss: Seq[String]): T
}
Now it's shapeless time (thinking about Dalí?) . We create reader for powerful Heterogenous List, which is almost like List but with of statically known length and element types.
It's no harder than to match simple List. Just define cases for empty list and cons:
import shapeless._
implicit object readHNil extends ReadSeq[HNil] {
def fromStrings(ss: Seq[String]) = HNil
}
implicit def readHList[X, XS <: HList](implicit head: Read[X], tail: ReadSeq[XS]) =
new ReadSeq[X :: XS] {
def fromStrings(ss: Seq[String]) = ss match {
case s +: rest => (head fromString s) :: (tail fromStrings rest)
}
}
Now we can use out-of-the-box macro mapping of HLists and case classes via Generic:
implicit def readCase[C, L <: HList](implicit gen: Generic.Aux[C, L], read: ReadSeq[L]) =
new ReadSeq[C] {
def fromStrings(ss: Seq[String]) = gen.from(read.fromStrings(ss))
}
Finally we could build our typeclass user:
def fromStringSeq[T](ss: Seq[String])(implicit read: ReadSeq[T]) = read.fromStrings(ss)
from this point having
case class Location(id: BigInt, lat: Double, lng: Double)
val repr = List("87222", "42.9912987", "-93.9557953")
you can ensure that
fromStringSeq[Location](repr) == Location(BigInt("87222"), 42.9912987, 93.9557953)
Thank you in advance for your help
I have 2 functions that I am trying to compose via Kleisli arrows. The functions accept String and produce FreeC. The kleisli arrows are created without an issue but the compiler is complaining that it cannot find. I will cut out some of the code for simplicity:
import scalaz._
import Scalaz._
import Free.FreeC
import Free._
import Kleisli._
trait AppCompose {
def lift[F[_], G[_], A](fa: F[A])(implicit I: Inject[F, G]): FreeC[G, A] =
Free.liftFC(I.inj(fa))
}
object BigBrother {
sealed trait Sensor[A]
case class Log(log: String) extends Sensor[Unit]
case class Filter(log: String) extends Sensor[String]
case class Secure(log: String) extends Sensor[String]
}
import BigBrother.Sensor
class BigBrother[F[_]](implicit I: Inject[Sensor,F]) extends AppCompose {
import BigBrother._
type FreeString[A] = FreeC[F,String]
def log(log: String) = lift(Log(log))
def filter(log: String) = lift(Filter(log))
def secure(log: String) = lift(Secure(log))
def filterAndSecure(phrase: String) = for {
f <- filter(phrase)
s <- secure(f)
} yield s
// kleisli composition attempt - alternative to filterAndSecure
val fk = kleisli[FreeString, String, String](filter _)
val sk = kleisli[FreeString, String, String](secure _)
val fAndS = fk >=> sk // this is where we have a compilation issue
}
For some reason what i get is this compilation error:
could not find implicit value for parameter b: scalaz.Bind[FreeString]
[error] val fAndS = sk >=> fk
feels like the implicit should be resolved since FreeC in a monad instance that implements a Bind trait and i am importing all of the Free implicit instances via import Free._
what am I missing here?
Thank you in advance!
Thank you Travis for your help. Bad type declaration was actually one of the culprits. With some help from the scalaz community via google groups and some tinkering here is the answer:
class BigBrother[F[_]](implicit I: Inject[Sensor,F]) extends AppCompose {
import BigBrother._
def log(log: String) = lift(Log(log))
def filter(log: String) = lift(Filter(log))
def secure(log: String) = lift(Secure(log))
def filterAndSecure(phrase: String) = for {
f <- filter(phrase)
s <- secure(f)
} yield s
type CoyoF[A] = Coyoneda[F, A]
type FreeCoF[A] = Free[CoyoF,A]
implicit val MonadFreeSensor = Free.freeMonad[FreeCoF]
// kleisli composition attempt - alternative to filterAndSecure
val fk = kleisli[FreeCoF, String, String](filter _)
val sk = kleisli[FreeCoF, String, String](secure _)
val fAndS = fk >=> sk
}
key is the correct type declaration and providing the type class monad instance for FreeCoF implicit val MonadFreeSensor = Free.freeMonad[FreeCoF]