Scala immutable container class extended with mixins - scala

I'd like a container class that I can extend with some number of traits to contain groups of default vals that can later be changed in an immutable way. The traits will hold certain simple pieces of data that go together so that creating the class with a couple of traits will create an object with several collections of default values.
Then I'd like to be able to modify any of the vals immutably by copying the object while changing one new value at a time.
The class might have something like the following:
class Defaults(val string: String = "string", val int: Int = "int")
Then other traits like this
trait MoreDefaults{
val long: Long = 1l
}
Then I'd like to mix them when instantiated to build my the particular needed set of defaults
var d = new Defaults with MoreDefaults
and later to something like:
if (someFlag) d = d.copy( long = 1412341234l )
You can do something like this with a single case class but I run out of params at 22. But I'll have a bunch of groupings of defaults I'd like to mixin depending on the need, then allow changes to any of them (class defined or trait defined) in an immutable way.
I can stick a copy method in the Defaults class like this:
def copy(
string: String = string,
int: Int = int): Defaults = {
new Defaults(string, int)
}
then do something like
var d = new Defaults
if (someFlag) d = d.copy(int = 234234)
Question ====> This works for values in the base class but I can't figure how to extend this to the mixin traits. Ideally the d.copy would work on all vals defined by all of the class + traits. Overloading is trouble too since the vals are mainly Strings but all of the val names will be unique in any mix of class and traits or it is an error.
Using only classes I can get some of this functionality by having a base Defaults class then extending it with another class that has it's own non-overloaded copyMoreDefault function. This is really ugly and I hope a Scala expert will see it and have a good laugh before setting me straight--it does work though.
class Defaults(
val string: String = "one",
val boolean: Boolean = true,
val int: Int = 1,
val double: Double = 1.0d,
val long: Long = 1l) {
def copy(
string: String = string,
boolean: Boolean = boolean,
int: Int = int,
double: Double = double,
long: Long = long): Defaults = {
new Defaults(string, boolean, int, double, long)
}
}
class MoreDefaults(
string: String = "one",
boolean: Boolean = true,
int: Int = 1,
double: Double = 1.0d,
long: Long = 1l,
val string2: String = "string2") extends Defaults (
string,
boolean,
int,
double,
long) {
def copyMoreDefaults(
string: String = string,
boolean: Boolean = boolean,
int: Int = int,
double: Double = double,
long: Long = long,
string2: String = string2): MoreDefaults = {
new MoreDefaults(string, boolean, int, double, long, string2)
}
}
Then the following works:
var d = new MoreDefualts
if (someFlag) d = d.copyMoreDefaults(string2 = "new string2")
This method will be a mess if Defaults get's changed parameters! All the derived classes will have to be updated--ugh. There must be a better way.

I don't think I'm strictly speaking answering your question, rather suggesting an alternative solution. So your having problems with large case classes, e.g.
case class Fred(a: Int = 1, b: Int = 2, ... too many params ... )
What I would do is organize the params into more case classes:
case class Bar(a: Int = 1, b: Int = 2)
case class Foo(c: Int = 99, d: Int = 200)
// etc
case class Fred(bar: Bar = Bar(), foo: Foo = Foo(), ... etc)
Then when you want to do a copy and change, say one of the values of Foo you do:
val myFred: Fred = Fred()
val fredCopy: Fred = myFred.copy(foo = myFred.foo.copy(d = 300))
and you need not even define the copy functions, you get them for free.

Related

How to print a Monocle Lens as a property accessor style string

Using Monocle I can define a Lens to read a case class member without issue,
val md5Lens = GenLens[Message](_.md5)
This can used to compare the value of md5 between two objects and fail with an error message that includes the field name when the values differ.
Is there a way to produce a user-friendly string from the Lens alone that identifies the field being read by the lens? I want to avoid providing the field name explicitly
val md5LensAndName = (GenLens[Message](_.md5), "md5")
If there is a solution that also works with lenses with more than one component then even better. For me it would be good even if the solution only worked to a depth of one.
This is fundamentally impossible. Conceptually, lens is nothing more than a pair of functions: one to get a value from object and one to obtain new object using a given value. That functions can be implemented by the means of accessing the source object's fields or not. In fact, even GenLens macro can use a chain field accessors like _.field1.field2 to generate composite lenses to the fields of nested objects. That can be confusing at first, but this feature have its uses. For example, you can decouple the format of data storage and representation:
import monocle._
case class Person private(value: String) {
import Person._
private def replace(
array: Array[String], index: Int, item: String
): Array[String] = {
val copy = Array.ofDim[String](array.length)
array.copyToArray(copy)
copy(index) = item
copy
}
def replaceItem(index: Int, item: String): Person = {
val array = value.split(delimiter)
val newArray = replace(array, index, item)
val newValue = newArray.mkString(delimiter)
Person(newValue)
}
def getItem(index: Int): String = {
val array = value.split(delimiter)
array(index)
}
}
object Person {
private val delimiter: String = ";"
val nameIndex: Int = 0
val cityIndex: Int = 1
def apply(name: String, address: String): Person =
Person(Array(name, address).mkString(delimiter))
}
val name: Lens[Person, String] =
Lens[Person, String](
_.getItem(Person.nameIndex)
)(
name => person => person.replaceItem(Person.nameIndex, name)
)
val city: Lens[Person, String] =
Lens[Person, String](
_.getItem(Person.cityIndex)
)(
city => person => person.replaceItem(Person.cityIndex, city)
)
val person = Person("John", "London")
val personAfterMove = city.set("New York")(person)
println(name.get(personAfterMove)) // John
println(city.get(personAfterMove)) // New York
While not very performant, that example illustrates the idea: Person class don't have city or address fields, but by wrapping data extractor and a string rebuild function into Lens, we can pretend it have them. For more complex objects, lens composition works as usual: inner lens just operates on extracted object, relying on outer one to pack it back.

Scala: How to define an enum with extra attributes?

I have a use-case where I need to define a new enum type LongShort but I need it in a way to also carry the sign so it can be directly used in mathematical expressions e.g.
object LongShortType extends Enumeration {
type Type = Value
val Long = Value(+1)
val Short = Value(-1)
}
I'd then like to use it like this:
val longShort = LongShortType.Short
val numberOfContracts: Int = 10
val vanillaOptionNotional: Double = longShort*numberOfContracts
but this leads to compiler error cannot resolve symbol * ... is there a way to extract the value of the enum? Or am I not understanding how enum types work?
The type of LongShortType.Short isn't Int, it's Value. You can either extract the underlying id of the value:
val longShort = LongShortType.Short.id
Which is a little ugly. Or you could not use an enum type at all:
object LongShortType {
val Long = 1
val Short = -1
}
And then your equation would work as is.
OK I worked out a solution to accomplish what I wanted without any compromisse and by that I mean that this solution has all the advantages of using Scala enum e.g. the withName and still allows me to define extra features on it:
object LongShortType extends Enumeration {
type Type = LongShortVal
val Long = Value("Long", +1)
val Short = Value("Short", -1)
case class LongShortVal(name: String, sign: Int) extends Val(nextId, name)
protected final def Value(name: String, sign: Int) = new LongShortVal(name, sign)
}
and now can do:
val longShort = LongShortType.Short
val numberOfContracts: Int = 10
val vanillaOptionNotional: Double = longShort.sign*numberOfContracts
and can also do:
val longShort = LongShort.withName("Long") // returns LongShort.Long

scala using a class argument a placeholder (or input variable)

Lets say i have a data structure that holds a parameterised type of data:
case class Terminal[A](value: A, name: String ="")
I can easily create a Terminal[Double] if i pass it a materialised constant:
val terminal = Terminal(2.0)
However, i want it to also be able to receive a (not materialised) input so that i can evaluate the terminal multiple times with different contexts. I can achieve a simple solution by calling value by name, i.e.
class Terminal[A](value: => A, name: String ="") {
def getValue = this.value
}
var x = 1.0
val terminal = new Terminal(x)
terminal.getValue // 1.0
x = 100.0
terminal.getValue // 100.0
However the user of this program would have to initialise the input with something like var input_x = None, which is not nice, and then change its state, which in turn would have to make me turn value into a Option[A]
Is this the best way to deal with this situation? Isn't any design pattern or scala feature that i could use?
i can also create a class Input to represent these context-dependent inputs, but then i would need to change a lot of things.
You can use immutable objects as below:
scala> case class Terminal[A](value: A, name: String ="") {
| def update(newValue: A): Terminal[A] = this.copy(value = newValue)
| def getValue: A = this.value
| }
defined class Terminal
scala> val terminal = Terminal(1.0)
terminal: Terminal[Double] = Terminal(1.0,)
scala> val updatedTerminal = terminal.update(100.0)
updatedTerminal: Terminal[Double] = Terminal(100.0,)
scala> val oldValue = terminal.getValue
oldValue: Double = 1.0
scala> val newValue = updatedTerminal.getValue
newValue: Double = 100.0
The getValue method is actually redundant here because getters come free with case classes. I just had it in there to demonstrate the example.
scala> oldValue == terminal.value
res0: Boolean = true
scala> newValue == updatedTerminal.value
res1: Boolean = true
In general, prefer case-classes if you want to create objects that don't have mutable state (For example, all singleton components are better-off as non-case classes).

how can I initialize an enum from an Int or Byte?

I have an enum like this:
object Ops extends Enumeration {
val one = Value(0x01)
val two = Value(0x02)
val three = Value(0x03)
val four = Value(0x04)
}
I want to say
Byte someByte = functionThatReturnsAByte
val op = Ops.valueOf(someByte)
The only method close is withName which only takes a String.
Ops(someByte)
will do the trick.

Problem with Scala's getter/setters

I'm currently learning Scala, and just discovered the way to create custom field getters/setters. I have a simple example working:
class Thing(private val a:Int){
override def toString = "Thing[" + a + "]"
private var _value = a
def value = _value
def value_= (newVal:Int) = _value = newVal
}
On the console I can do:
scala> var t = new Thing(2)
t: dylan.code.Thing = Thing[2]
scala> t.value
res1: Int = 2
scala> t.value = 3
scala> t.value
res2: Int = 3
Now I'm trying to bring this concept to a slightly more complicated example; I'll try to whittle the code down to what's relevant:
abstract class CellExpression[Type] extends Publisher[CellUpdateEvent[Type]] with Subscriber[CellUpdateEvent[Type], CellExpression[Type]]{
protected var cachedValue: Type = recalculateValue()
protected def recalculateValue(): Type
protected def changeValue(newValue: Type):Unit = {
val oldValue = value()
if(newValue != oldValue){
cachedValue = newValue
publish(new CellUpdateEvent(this, oldValue, newValue))
}
}
def value() = cachedValue
def notify(pub: CellExpression[Type], event: CellUpdateEvent[Type]) = changeValue(recalculateValue())
}
//....
class CellVariable[Type](private val initialValue:Type) extends CellExpression[Type]{
cachedValue = initialValue
protected def recalculateValue() = { cachedValue }
override def toString = "CellVariable[" + value + "]"
def value_= (newValue:Type) = {changeValue(newValue)}
}
As far as I can tell, I've done what I need to in order to be able to treate value as a field via its getter and setter. But when I try it out in the console, I get:
scala> var i = new CellVariable(2)
i: dylan.code.CellVariable[Int] = CellVariable[2]
scala> i.value = 3
<console>:11: error: reassignment to val
i.value = 3
^
What have I done wrong, and how can I fix it?
I actually stumbled onto the solution.
The line where I declare my value function: def value() = cachedValue is the culprit.
If I remove the parentheses to make the line def value = cachedValue everything seems to work as I expected.
You cannot change values in Scala. A value is assigned once and only once. If you want to do this then you need to use variables instead of values. In other words, change the declaration from val to var.
The problem is inside one of your class definitions and may be on a line without val because I believe that if you neglect to declare a name, then Scala assumes that it is a value and therefore immutable.
Not sure what you want getters and setters for though. Scala enables you to ignore all of that Java overhead.
It is probably the line that says cachedValue = initialValue because it is not declared with var anywhere in that class. The definition in the other class is a different name because it is in a different scope. You would have to say something like class.varname to change a variable defined in another class.