score:4
you can define a few internablen[arg1, arg2, ..., resulttype]
traits for n being the number of arguments to apply()
: internable1[a,z]
, internable2[a,b,z]
, etc. these traits define the cache itself, the intern()
method and the apply
method we want to hijack.
we'll have to define a trait (or an abstract class) to assure your internablen
traits that there is indeed an apply method to be overriden, let's call it applyable
.
trait applyable1[a, z] {
def apply(a: a): z
}
trait internable1[a, z] extends applyable1[a, z] {
private[this] val cache = weakhashmap[(a), z]()
private[this] def intern(args: (a))(builder: => z) = {
cache.getorelse(args, {
val newobj = builder
cache(args) = newobj
newobj
})
}
abstract override def apply(arg: a) = {
println("internable1: hijacking apply")
intern(arg) { super.apply(arg) }
}
}
the companion object of your class will have to be a mixin of a concrete class implementing applyablen
with internablen
. it would not work to have apply directly defined in your companion object.
// class with one apply arg
abstract class someclasscompanion extends applyable1[int, someclass] {
def apply(value: int): someclass = {
println("original apply")
new someclass(value)
}
}
class someclass(val value: int)
object someclass extends someclasscompanion with internable1[int, someclass]
one good thing about this is that the original apply need not be modified to cater for interning. it only creates instances and is only called when they need to be created.
the whole thing can (and should) also be defined for classes with more than one argument. for the two-argument case:
trait applyable2[a, b, z] {
def apply(a: a, b: b): z
}
trait internable2[a, b, z] extends applyable2[a, b, z] {
private[this] val cache = weakhashmap[(a, b), z]()
private[this] def intern(args: (a, b))(builder: => z) = {
cache.getorelse(args, {
val newobj = builder
cache(args) = newobj
newobj
})
}
abstract override def apply(a: a, b: b) = {
println("internable2: hijacking apply")
intern((a, b)) { super.apply(a, b) }
}
}
// class with two apply arg
abstract class anotherclasscompanion extends applyable2[string, string, anotherclass] {
def apply(one: string, two: string): anotherclass = {
println("original apply")
new anotherclass(one, two)
}
}
class anotherclass(val one: string, val two: string)
object anotherclass extends anotherclasscompanion with internable2[string, string, anotherclass]
the interaction shows that the internables' apply method executes prior to the original apply()
which gets executed only if needed.
scala> import someclass._
import someclass._
scala> someclass(1)
internable1: hijacking apply
original apply
res0: someclass = someclass@2e239525
scala> import anotherclass._
import anotherclass._
scala> anotherclass("earthling", "greetings")
internable2: hijacking apply
original apply
res1: anotherclass = anotherclass@329b5c95
scala> anotherclass("earthling", "greetings")
internable2: hijacking apply
res2: anotherclass = anotherclass@329b5c95
i chose to use a weakhashmap so that the interning cache does not prevent garbage collection of interned instances once they're no longer referenced elsewhere.
code neatly available as a github gist.
score:1
maybe a little hacky, but you could try defining your own intern()
method, like java's string
has:
import scala.collection.mutable.{map=>mutablemap}
object hashconsed {
val cache: mutablemap[(class[_],int), hashconsed] = mutablemap.empty
}
trait hashconsed {
def intern(): hashconsed =
hashconsed.cache.getorelse((getclass, hashcode), {
hashconsed.cache((getclass, hashcode)) = this
this
})
}
case class foo(bar: int, baz: string) extends hashconsed
val foo1 = foo(1, "one").intern()
val foo2 = foo(1, "one").intern()
println(foo1 == foo2) // true
println(foo1 eq foo2) // true
Source: stackoverflow.com
Related Query
- Automatically Hash Consed Case Classes
- Automatically wrapping/converting JavaBeans into case classes
- Scala case classes and Hash Collision
- hashCode in case classes in Scala
- Scala - how to print case classes like (pretty printed) tree
- Why were the case classes without a parameter list deprecated?
- 22 fields limit in Scala 2.11 + Play Framework 2.3 Case classes and functions
- Case Classes with optional fields in Scala
- Constructing simple Scala case classes from Strings, strictly without boiler-plate
- Where case classes should NOT be used in Scala?
- Weird behavior trying to convert case classes to heterogeneous lists recursively with Shapeless
- how to serialize case classes with traits with jsonspray
- Converting nested case classes to nested Maps using Shapeless
- Using generic case classes in Scala
- How to use a case classes when hierarchy is needed?
- when to use Classes vs Objects vs Case Classes vs Traits
- Play JSON Reads/Writes with single-parameter case classes
- Fastest serialization/deserialization of Scala case classes
- Case classes inheriting from abstract class
- Json Serialization for Trait with Multiple Case Classes (Sum Types) in Scala's Play
- Scala case classes with Mixin traits
- Why do case classes extend only Product and not Product1, Product2, ..., ProductN?
- Matching (and binding) two exception classes in one case statement in Scala 2.7?
- How to serialize/deserialize case classes to/from Json in Play 2.1
- "dynamically" creating case classes with macros
- Automatically make getters for class parameters (to avoid case classes)?
- Merge two case classes in Scala, but with deeply nested types, without lens boilerplate
- Why are case objects serializable and case classes not?
- How to create Spark Dataset or Dataframe from case classes that contains Enums
- Is there any time or space overhead to case classes over regular classes in Scala?
More Query from same tag
- Spark Cassandra Connector IN statement in where clause
- How to sum a List[(Char,Int)] into a Map[Char,Int] in Scala?
- How to use a Scala Secure Trait in PlayFramework?
- Play Framework 2.1.1 not resolving dependencies
- Efficient way to return Array[String] from a spark dataframe without using collect()
- scala-meta: parse vararg in class constructor
- Where to put calculation executed regularly that updates actor's internal state?
- VarArgs A* vs Seq[A] parameters to function
- How can i visualize and see the execution in intellij for any scala recursion question?
- How to change the datatype of a column in StructField of a StructType?
- scala error : ';' expected but 'import' found
- How to recreate if else statements in Scala using objects?
- Intersperse is missing in cats?
- In databricks how to automate notebook runs
- Idiomatic way to possibly compose a function
- How to write (String): Int function?
- How to get current project name in sbt build.scala
- how to fetch the last row's 1st column value of spark scala dataframe inside the for and if loop
- How to fit multiple Spark ml models to each partitioned subset of single Dataset/DataFrame?
- How to groupby and aggregate multiple fields using RDD?
- Shapeless define lens for base trait
- Can a Scala compiler plugin transform the autogenerated accessor methods of scala case classes?
- Inserting multiple values and fields into table with anorm
- Publishing my FAT Jar to local sbt repo
- Serializing/Deserializing case objects with Gson in Scala
- Specifying Unit Test Location in Gradle
- Scala - Why can't a context bound case class be used as a default constructor parameter of another case class?
- How do I set up a multi-stage test pipeline in sbt?
- How to generate all weeks between two week numbers of same/different year?
- Akka-cluster discovering other machines in local network