I seem to run into a lot of problems learning to stay with immutable principals in Scala, one of which is the concept of Cloning (or rather deriving) from the object in question.
One of these problems is the concept of mixed in traits - example
trait helper //modifies some kind behavior.. assume we want to to continue down the line
class A (val x:int) {
def add(y:int) = new A(x + y)
}
Example extends App {
val a =new A(5) with helper
val b = a.add(10) // mixed trait disappears
}
Now this is just a really simple version of a more complex problem I had already built today around various classes, factory methods to hide class A, etc. If we're only dealing with one trait, I know I could simply test for it and send it forward as needed. But what the heck do I do if upward of 3 or more traits could exist? I would have to test for all combinations which is unrealistic.
How do you instantiate (clone) an existing object with various traits and/or modify some aspect of it while adhering to functional design principals?
Many thanks, - Tim
Collections uses implicit builders which know how to produce your target type from what you're starting with. Those two types are not always the same thing.
There are many related posts about typesafe builders that control what can be produced, e.g., http://nullary.blogspot.com/2011/10/builder-pattern-revisited-in-scala.html
A related question is what if you have a collection of mixed types: Polymorphic updates in an immutable class hierarchy
On the value axis, tracking values instead of encoding types What is the Scala equivalent to a Java builder pattern?
Updated: something similar just came up during play time. Using REs in patterns is described on the ML and here.
package object interpat {
implicit class MySContext (val sc : StringContext) {
object mys {
def apply (args : Any*) : String = sc.s (args : _*)
def unapplySeq (s : String) : Option[Seq[String]] = {
val regexp = sc.parts.mkString ("(.+)").r
regexp.unapplySeq (s)
}
}
}
implicit class SBContext (val sc : StringContext) {
def sb(args: Any*): SB = new SB(sc.s (args : _*))
}
implicit class toHasPattern(sb: SB) {
def /(pp: String) = new SB(sb.s) with HasPattern {
val p = pp
}
}
implicit class toHasRepl(hasp: SB with HasPattern) {
def /(rr: String) = new SB(hasp.s) with HasPattern with HasRepl with Updateable {
val p = hasp.p
val r = rr
}
}
// disallow it
implicit class noway(hasr: SB with HasPattern with HasRepl) {
def /(rr: String) = ???
}
implicit class noway2(hasr: SB with HasPattern with HasRepl) {
def /(rr: String) = ???
}
}
With usages and alternative of controlling the implicits with type parameters.
package interpat {
import scala.util.Try
object I { def unapply(x: String): Option[Int] = Try(x.toInt).toOption }
trait XX {
type HasIt
type Yes <: HasIt
type No <: HasIt
}
object XX extends XX {
implicit class XContext (val sc : StringContext) {
def x(args: Any*) = new X[No, No] {
val s = sc.s(args : _*)
}
}
implicit class xPat(x: X[No, No]) {
def /(pp: String) = new X[Yes, No] with HasPattern {
val s = x.s
val p = pp
}
}
implicit class xRep(x: X[Yes, No] with HasPattern) {
def /(rr: String) = new X[Yes, Yes] with HasPattern with HasRepl {
val s = x.s
val p = x.p
val r = rr
override def toString = s replaceAll (p, r )
}
}
implicit class xable(xx: X[Yes, Yes]) {
def x = xx.toString
}
}
import XX._
trait X[HasP <: HasIt, HasR <: HasIt] {
def s: String
}
trait HasPattern {
def p: String
}
trait HasRepl {
def r: String
}
trait Updateable { this: HasPattern with HasRepl =>
def update(p: String, r: String)
override def toString = {
update(p, r)
super.toString
}
}
class SB(val s: String) {
final val sb = new StringBuilder(s)
def update(p: String, r: String): Unit =
sb.replace(0, sb.length, sb.toString.replaceAll(p, r))
override def toString = sb.toString
}
object Test extends App {
val msg = "The sky is blue" match {
case mys"The $thing is $colour" => mys"A $colour thing is $thing"
case _ => "no match"
}
println (msg)
val mys"The $thing is $colour" = "The sky is blue"
Console println s"$thing / $colour"
val mys"The ${I(number)} is blue" = "The 3 is blue"
Console println s"$number"
//sb"The sky is blue".update("blue","red")
// no way to get a value out!
sb"The sky is blue"("blue") = "red"
val ugh = sb"The sky is blue"
ugh("blue") = "red"
Console println ugh
val sx = sb"The sky is $colour" / "blue" / "red"
Console println sx
//sb"The sky is $colour" / "blue" / "red" / "yellow"
Console println sx
Console println x"The sky is $colour" / "blue" / "red"
Console println (x"The sky is $colour" / "blue" / "red").x
//Console println x"The sky is $colour" / "blue" / "red" / "yellow"
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With