Given the following code example
case class Thing(
value: String,
parents: Seq[Thing],
children: Seq[Thing],
)
val x = Thing(
value = "foo",
parents = Seq(y),
children = Seq(),
)
val y = Thing(
value = "bar",
parents = Seq(x),
children = Seq(),
)
errors out because y is not initialized when creating x, is there any way of performing this "circular referencing" without using a secondary data structure such as a hash map to manually create pointers to each object?
One way to fix the compile code and the runtime exception is:
class Thing(
_value: => String,
_parents: => Seq[Thing],
_children: => Seq[Thing]
) {
lazy val value = _value
lazy val parents = _parents
lazy val children = _children
}
object Thing {
def apply(
value: => String,
parents: => Seq[Thing],
children: => Seq[Thing]
): Thing = {
new Thing(value, parents, children)
}
}
val x: Thing = Thing(
value = "foo",
parents = Seq(y),
children = Seq()
)
val y: Thing = Thing(
value = "bar",
parents = Seq(x),
children = Seq()
)
Fields used in Thing class must be evaluated lazily but since Scala prohibits using call-by-name for public class parameters I stored them in private class parameters and made them publicly accessible through public lazy vals.
Related
I intend to create the simple polymorphic function expression below using the Quotes.reflect API:
new PolyFunction {
def apply[X](a: X): X = a
}
What I have attempted is shown below with parts that I could not implement replaced by ???:
val name: String = "$anon"
val parents = List(TypeTree.of[Object], TypeTree.of[PolyFunction])
def decls(cls: Symbol): List[Symbol] =
List(
Symbol.newMethod(
cls,
"apply",
MethodType(List("a"))(
{ mt =>
???
},
mt => ???
)
)
)
val cls = Symbol.newClass(Symbol.spliceOwner, name, parents = parents.map(_.tpe), decls, selfType = None)
val applySym = cls.declaredMethod("apply").head
val applyDef = DefDef(applySym, argss => Some(???))
val clsDef = ClassDef(cls, parents, body = List(applyDef))
val closure = Block(List(clsDef),Apply(Select(New(TypeIdent(cls)), cls.primaryConstructor), Nil))
closure.asExpr
The problem, mainly, is I am unable to declare the type parameter X, and its reference in the first value parameter and the functions return type. I have noticed some reflection functions are annotated as experimental.
Try
import scala.annotation.experimental
import scala.quoted.*
inline def newPoly: PolyFunction = ${newPolyImpl}
#experimental
def newPolyImpl(using Quotes): Expr[PolyFunction] = {
import quotes.reflect.*
val name: String = "$anon"
val parents = List(TypeTree.of[Object], TypeTree.of[PolyFunction])
def decls(cls: Symbol): List[Symbol] =
List(
Symbol.newMethod(
cls,
"apply",
PolyType(List("X"))(_ => List(TypeBounds.empty), polyType => {
val typeParam = polyType.param(0)
MethodType(List("a"))(_ => List(typeParam), _ => typeParam)
})
)
)
val cls = Symbol.newClass(Symbol.spliceOwner, name, parents = parents.map(_.tpe), decls, selfType = None)
val applySym = cls.declaredMethod("apply").head
// argss=List(List(TypeTree[TypeRef(NoPrefix,type X)]), List(Ident(a)))
val applyDef = DefDef(applySym, argss => Some(argss(1)(0).asInstanceOf[Term]))
val clsDef = ClassDef(cls, parents, body = List(applyDef))
val closure = Block(List(clsDef), Apply(Select(New(TypeIdent(cls)), cls.primaryConstructor), Nil))
closure.asExprOf[PolyFunction]
}
Usage:
newPoly
//scalac: {
// class $anon extends java.lang.Object with scala.PolyFunction {
// def apply[X](a: X): X = a
// }
// new $anon()
//}
Scala 3.2.1
Method Override with Scala 3 Macros
Scala3: Crafting Types Through Metaprogramming?
`tq` equivalent in Scala 3 macros
For simplicities sake I removed all the types from what I intend to do and using a simple list here. Imagine I have a function which takes two parameters: a collection and a function and uses this to produce a new collection.
I know! this looks like reinventing map but the actual use case is a lot more different and complex. What I tried so far is something like:
trait SomeHelper {
class CoolFunction( right: String => Boolean ) extends Function1[List[String], List[Any]] {
inst =>
override def apply( left: List[ String ] ): List[ Any ] = {
left match {
case a: List[String] => a.filter( right )
case _ => List()
}
}
}
def coolFunction( right: String => Boolean ) = new CoolFunction( right )
}
object SomeHelper extends SomeHelper
import SomeHelper._
val myList = List("apple", "orange", "banana")
myList coolFunction {
a => a == "orange"
}
produces me a "value coolFunction is not a member of List[String]"
feels like I am so close but can't figure it out
I guess you could use an implicit class. They allow to add a method onto existing types.
implicit class MyListHelper(list: List[String]) {
def doStuff(function: String => Any): List[Any] = {
list.map(function)
}
}
And use it like this:
import MyListHelper
val list = List("a", "b", "c")
val result = list.doStuff(s => s + s) // List("aa", "bb", "cc")
Following is my ADT. Main thing to notice is that Blocks can be nested (look at the children property.
trait Cda {
def format: String = this match {
case f: Field => f.value
case Block(fields, children) => fields.map(f => f.format).mkString("|") + "|" + children.map(b => b.format).mkString("|")
case Record(keys, blocks) => blocks.map(b => b.format).mkString("|")
}
}
trait Field extends Cda {
val name: String
val value: String
}
case class StringField(name: String, value: String) extends Field
case class DateField(name: String, value: String) extends Field
case class TimeField(name: String, value: String) extends Field
case class MatchKey(keyFields: Seq[Field]) extends Cda
case class Block(fields: Seq[Field], children: Seq[Block] = Seq()) extends Cda
case class Record(key: MatchKey, blocks: Seq[Block]) extends Cda
Following is an example instantiation of that ADT
//Block - AI
val aiBlockId = StringField("blockId", "AI")
val addlFieldPos = StringField("AdditionalFieldPosition", "addlFieldPos")
val addlFieldName = StringField("AdditionalFieldName", "addlFieldName")
val AI = Block(Seq(aiBlockId, addlFieldPos, addlFieldName))
//Block - RPS
val rpsBlockId = StringField("blockId", "RPS")
val dateOfStatus = DateField("DateOfStatus", "19240811")
val timeOfStatus = TimeField("TimeOfStatus", "023829")
val rpsBlocks = Seq(rpsBlockId, dateOfStatus, timeOfStatus)
val rpsNestedBlocks = Seq(AI)
val RPS = Block(rpsBlocks, rpsNestedBlocks)
I am expecting to format to return RPS|19240811|023829|AI|addlFieldPos|addlFieldName but I am getting an additional pipe | at the end: RPS|19240811|023829|AI|addlFieldPos|addlFieldName|.
How to change the recursive function format (specifically case Block(fields,children)) to correct this?
Combine the seqs first. It's cheaper to use an iterator, which won't create an intermediate collection.
scala> val as = Seq(1,2,3) ; val bs = Seq.empty[Int]
as: Seq[Int] = List(1, 2, 3)
bs: Seq[Int] = List()
scala> (as ++ bs).mkString("|")
res0: String = 1|2|3
scala> (as.iterator ++ bs).mkString("|")
res1: String = 1|2|3
That is,
case Block(fields, children) => (fields.iterator ++ children).map(_.format).mkString("|")
trait Cda {
def format: String = this match {
case f: Field => f.value
case Block(fields, children) => fields.map(f => f.format).mkString("|") + {if (!children.isEmpty) {"|" + children.map(b => b.format).mkString("|")} else ""}
case Record(keys, blocks) => blocks.map(b => b.format).mkString("|")
}
}
I have three class's
class A (
param1:Int,
param2:Int
)
class B (
param3:Int,
param4:Int
)
class C(
param1:A,
param2:B
)
And i need map between name of field to object, like
param1->1
param2->2
param3->3
param4->4
So i tired to did it as below but field its not type of Product
def func1(c: C): Map[String, Object] = {
var map = Map[String, Object]()
for (field <- c.getClass.getDeclaredFields) yield {
map ++ getFieldMap(field))
}
}
def getFieldMap(p: Product): Map[String, Object] = {
var values = p.productIterator
p.getClass.getDeclaredFields.map(_.getName -> {
values.next.asInstanceOf[AnyRef]
}).toMap
}
The are multiple problems with your code. The class A and class B do not implement the Product trait. You either have to do with Product manually and implement all the methods, or you declare them as case classes.
case class A (
val param1: Int,
val param2: Int
)
case class B (
val param3: Int,
val param4: Int
)
To ensure that Scala actually generates fields for the constructor parameters, add the val keyword in front.
Furthermore, you are passing the field (java.lang.reflect.Field), not it's value, which you have to get first.
def func1(c: C): Map[String, Object] = {
var map = Map[String, Object]()
for (field <- c.getClass.getDeclaredFields) yield {
field.setAccessible(true) // <- to avoid IllegalAccessException
map ++ getFieldMap(field.get(c)) // <- inserted .get(c)
}
}
If you can make your classes case classes, you can do it using shapeless:
import shapeless._
import shapeless.record._
case class A (param1:Int, param2:Int)
val a = A(12, 34)
val AGen = LabelledGeneric[A]
val map = AGen.to(a).toMap.map { case (k, v) => (k.name, v) }
println(map("param1")) // prints "12"
If you have a case class like:
case class Foo(x: String, y: String, z: String)
And you have two instances like:
Foo("x1","y1","z1")
Foo("x2","y2","z2")
Is it possible to merge instance 1 in instance 2, except for field z, so that the result would be:
Foo("x1","y1","z2")
My usecase is just that I give JSON objects to a Backbone app through a Scala API, and the Backbone app gives me back a JSON of the same structure so that I can save/update it. These JSON objects are parsed as case class for easy Scala manipulation. But some fields should never be updated by the client side (like creationDate). For now I'm doing a manual merge but I'd like a more generic solution, a bit like an enhanced copy function.
What I'd like is something like this:
instanceFromDB.updateWith(instanceFromBackbone, excludeFields = "creationDate" )
But I'd like it to be typesafe :)
Edit:
My case class have a lot more fields and I'd like the default bevavior to merge fields unless I explicitly say to not merge them.
What you want is already there; you just need to approach the problem the other way.
case class Bar(x: String, y: String)
val b1 = Bar("old", "tired")
val b2 = Bar("new", "fresh")
If you want everything in b2 not specifically mentioned, you should copy from b2; anything from b1 you want to keep you can mention explicitly:
def keepY(b1: Bar, b2: Bar) = b2.copy(y = b1.y)
scala> keepY(b1, b2)
res1: Bar = Bar(new,tired)
As long as you are copying between two instances of the same case class, and the fields are immutable like they are by default, this will do what you want.
case class Foo(x: String, y: String, z: String)
Foo("old_x", "old_y", "old_z")
// res0: Foo = Foo(old_x,old_y,old_z)
Foo("new_x", "new_y", "new_z")
// res1: Foo = Foo(new_x,new_y,new_z)
// use copy() ...
res0.copy(res1.x, res1.y)
// res2: Foo = Foo(new_x,new_y,old_z)
// ... with by-name parameters
res0.copy(y = res1.y)
// res3: Foo = Foo(old_x,new_y,old_z)
You can exclude class params from automatic copying by the copy method by currying:
case class Person(name: String, age: Int)(val create: Long, val id: Int)
This makes it clear which are ordinary value fields which the client sets and which are special fields. You can't accidentally forget to supply a special field.
For the use case of taking the value fields from one instance and the special fields from another, by reflectively invoking copy with either default args or the special members of the original:
import scala.reflect._
import scala.reflect.runtime.{ currentMirror => cm }
import scala.reflect.runtime.universe._
import System.{ currentTimeMillis => now }
case class Person(name: String, age: Int = 18)(val create: Long = now, val id: Int = Person.nextId) {
require(name != null)
require(age >= 18)
}
object Person {
private val ns = new java.util.concurrent.atomic.AtomicInteger
def nextId = ns.getAndIncrement()
}
object Test extends App {
/** Copy of value with non-defaulting args from model. */
implicit class Copier[A: ClassTag : TypeTag](val value: A) {
def copyFrom(model: A): A = {
val valueMirror = cm reflect value
val modelMirror = cm reflect model
val name = "copy"
val copy = (typeOf[A] member TermName(name)).asMethod
// either defarg or default val for type of p
def valueFor(p: Symbol, i: Int): Any = {
val defarg = typeOf[A] member TermName(s"$name$$default$$${i+1}")
if (defarg != NoSymbol) {
println(s"default $defarg")
(valueMirror reflectMethod defarg.asMethod)()
} else {
println(s"def val for $p")
val pmethod = typeOf[A] member p.name
if (pmethod != NoSymbol) (modelMirror reflectMethod pmethod.asMethod)()
else throw new RuntimeException("No $p on model")
}
}
val args = (for (ps <- copy.paramss; p <- ps) yield p).zipWithIndex map (p => valueFor(p._1,p._2))
(valueMirror reflectMethod copy)(args: _*).asInstanceOf[A]
}
}
val customer = Person("Bob")()
val updated = Person("Bobby", 37)(id = -1)
val merged = updated.copyFrom(customer)
assert(merged.create == customer.create)
assert(merged.id == customer.id)
}
case class Foo(x: String, y: String, z: String)
val foo1 = Foo("x1", "y1", "z1")
val foo2 = Foo("x2", "y2", "z2")
val mergedFoo = foo1.copy(z = foo2.z) // Foo("x1", "y1", "z2")
If you change Foo later to:
case class Foo(w: String, x: String, y: String, z: String)
No modification will have to be done. Explicitly:
val foo1 = Foo("w1", "x1", "y1", "z1")
val foo2 = Foo("w2", "x2", "y2", "z2")
val mergedFoo = foo1.copy(z = foo2.z) // Foo("w1", "x1", "y1", "z2")