Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Immutable classes and traits?

Tags:

scala

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

like image 377
LaloInDublin Avatar asked Jun 23 '13 21:06

LaloInDublin


1 Answers

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"
  }
}
like image 167
som-snytt Avatar answered Sep 20 '22 10:09

som-snytt