I am porting my java code to scala, and have a constructor that does quite some stuff:
open hibernate session to load data
do loading (now the session is kept open! I need lazy loading further on)
perform some operations on loaded data and put in containers
tie containers with their consumers
..blah blah
close session
The constructor produces quite a number (~20) of object members, that are final (val in scala). Their creation is not independent, also note that session. Obviously I don't want the session and other temporary stuff to become constructed instance members. How do I do that in scala?
Similar questions have been already asked before:
Constructor-local variables in Scala
Scala local variable inside primary constructor
Answers given in previous topics suggest either returning a tuple of ~20 fields from initialization method, or creating a private constructor that takes those ~20 values as parameters from companion object apply method. Either seems pig ugly to me.
So far the most sensible approach seems to declare those fields as vars, not vals, and reassign them in some initialization method, which also kinda smells.
I have a technically correct solution, but it might not be what you wanted, because it introduces a lot of verbocity
trait Example {
val a1 : Int
val a2 : Int
val a3 : Int
val a4 : Int
val a5 : Int
val a6 : Int
}
object Example {
def apply(seed : Int) : Example = {
val x1 = seed + 1
val x2 = x1 * 2
val x3 = x2 / 3
val x4 = x3 + x1
val x5 = x4 * x2
val x6 = x5 - x4
new Example {
override val a1 = x1
override val a2 = x2
override val a3 = x3
override val a4 = x4
override val a5 = x5
override val a6 = x6
}
}
}
So there is no constructor neither initializing method that takes many of variables, but they are passed via trait extending
Another (complex) way would be something like this:
import Person.getParams
class Person private (t: (Int, String, String)) {
val a = t._1
val b = t._2
val c = t._3
def this(filename: String) {
this(getParams(filename))
}
}
object Person {
def getParams(filename: String): (Int, String, String) {
// do some logic with filename which results in the relevant tuple
// return the tuple
}
}
Of course this is limited to 22 parameters so if you need more you would need an actual class...
Related
I have the following code, but I want to make it more idiomatically correct. First of all I define three variables as Option[String].
var p1 = None : Option[String]
var p2 = None : Option[String]
var p3 = None : Option[String]
Then I define these parameters using the function getParameters(). Basically this function simply reads parameters from text file.
getParameters()
//...
def getParameters(): Unit = {
val p1 = Some(getP1())
val p2 = Some(getP2())
val p3 = Some(getP3())
}
Finally, right after getParameters() I run another function getRules that uses p1, p2 and p3. Now it expects them to be String instead of Some[String].
val df = getRules(p1,p2,p3)
If any of these three parameters is None, then the program should throw an error. I wonder if I am on the right way. What is the number of parameters is bigger, e.g. 10 or 15? What is the best short way to process these parameters?
val valuesOpt = for(a <- p1; b <- p2; c <- p3) yield (a,b,c)
valuesOpt.map{
case (a, b, c) => getRules(a, b, c)
}.getOrElse(throw new Exception("Nope"))
There are multiple ways to unpack an Option, but in your case I think this is the easiest to read/maintain in my opinion:
val df = (p1, p2, p3) match {
case (Some(x), Some(y), Some(z)) => getRules(x, y, z)
case _ => throw new Exception("One or more values were NONE!")
}
*Edit: Here is a small scala fiddle to demonstrate how to use this: https://scalafiddle.io/sf/YVCYBBl/1
In the following code I would like to use map with breakOut and to specify the result should be IndexedSeq, but I do not want to provide the type of the sequence elements, I want the compiler to deduce it. I can write my own helper generic function (asIndexedSeq below) which does the job, but I think I am perhaps missing some obvious more "native" way how to to this.
val m = Map("a"->1, "b"->2)
implicit class CastAsIndexedSeq[T](s: IndexedSeq[T]) {
def asIndexedSeq = s
}
val t1 = m.map(_._1 + ":")(collection.breakOut).asIndexedSeq
val t2: IndexedSeq[String]= m.map(_._1 + ":")(collection.breakOut) // works, but I want String to be deduced
val t4: IndexedSeq[_] = m.map(_._1 + ":")(collection.breakOut) // almost works, but static type is lost
val t4: IndexedSeq[_] = m.map(_._1 + ":").toIndexedSeq // intermediate collection created, I prefer breakOut
val t3: IndexedSeq = m.map(_._1 + ":")(collection.breakOut) // compile error
Is there some more concise way?
I want to check if a specify id that contained in an Enumeration.
So I write down the contains function
object Enum extends Enumeration {
type Enum = Value
val A = Value(2, "A")
def contains(value: Int): Boolean = {
Enum.values.map(_.id).contains(value)
}
}
But the time cost is unexpected while id is a big number, such as over eight-digit
val A = Value(222222222, "A")
Then the contains function cost over 1000ms per calling.
And I also noticed the first time calling always cost hundreds millisecond whether the id is big or small.
I can't figure out why.
First, lets talk about the cost of Enum.values. This is implemented here:
See here: https://github.com/scala/scala/blob/0b47dc2f28c997aed86d6f615da00f48913dd46c/src/library/scala/Enumeration.scala#L83
The implementation is essentially setting up a mutable map. Once it is set up, it is re-used.
The cost for big numbers in your Value is because, internally Scala library uses a BitSet.
See here: https://github.com/scala/scala/blob/0b47dc2f28c997aed86d6f615da00f48913dd46c/src/library/scala/Enumeration.scala#L245
So, for larger numbers, BitSet will be bigger. That only happens when you call Enum.values.
Depending on your specific uses case you can choose between using Enumeration or Case Object:
Case objects vs Enumerations in Scala
It sure looks like the mechanics of Enumeration don't handle large ints well in that position. The Scaladocs for the class don't say anything about this, but they don't advertise using Enumeration.Value the way you do either. They say, e.g., val A = Value, where you say val A = Value(2000, "A").
If you want to keep your contains method as you have it, why don't you cache the Enum.values.map(_.id)? Much faster.
object mult extends App {
object Enum extends Enumeration {
type Enum = Value
val A1 = Value(1, "A")
val A2 = Value(2, "A")
val A222 = Enum.Value(222222222, "A")
def contains(value: Int): Boolean = {
Enum.values.map(_.id).contains(value)
}
val cache = Enum.values.map(_.id)
def contains2(value: Int): Boolean = {
cache.contains(value)
}
}
def clockit(desc: String, f: => Unit) = {
val start = System.currentTimeMillis
f
val end = System.currentTimeMillis
println(s"$desc ${end - start}")
}
clockit("initialize Enum ", Enum.A1)
clockit("contains 2 ", Enum.contains(2))
clockit("contains 222222222 ", Enum.contains(222222222))
clockit("contains 222222222 ", Enum.contains(222222222))
clockit("contains2 2 ", Enum.contains2(2))
clockit("contains2 222222222", Enum.contains2(222222222))
}
I have a class that represents sales orders:
class SalesOrder(val f01:String, val f02:Int, ..., f50:Date)
The fXX fields are of various types. I am faced with the problem of creating an audit trail of my orders. Given two instances of the class, I have to determine which fields have changed. I have come up with the following:
class SalesOrder(val f01:String, val f02:Int, ..., val f50:Date){
def auditDifferences(that:SalesOrder): List[String] = {
def diff[A](fieldName:String, getField: SalesOrder => A) =
if(getField(this) != getField(that)) Some(fieldName) else None
val diffList = diff("f01", _.f01) :: diff("f02", _.f02) :: ...
:: diff("f50", _.f50) :: Nil
diffList.flatten
}
}
I was wondering what the compiler does with all the _.fXX functions: are they instanced just once (statically), and can be shared by all instances of my class, or will they be instanced every time I create an instance of my class?
My worry is that, since I will use a lot of SalesOrder instances, it may create a lot of garbage. Should I use a different approach?
One clean way of solving this problem would be to use the standard library's Ordering type class. For example:
class SalesOrder(val f01: String, val f02: Int, val f03: Char) {
def diff(that: SalesOrder) = SalesOrder.fieldOrderings.collect {
case (name, ord) if !ord.equiv(this, that) => name
}
}
object SalesOrder {
val fieldOrderings: List[(String, Ordering[SalesOrder])] = List(
"f01" -> Ordering.by(_.f01),
"f02" -> Ordering.by(_.f02),
"f03" -> Ordering.by(_.f03)
)
}
And then:
scala> val orderA = new SalesOrder("a", 1, 'a')
orderA: SalesOrder = SalesOrder#5827384f
scala> val orderB = new SalesOrder("b", 1, 'b')
orderB: SalesOrder = SalesOrder#3bf2e1c7
scala> orderA diff orderB
res0: List[String] = List(f01, f03)
You almost certainly don't need to worry about the perfomance of your original formulation, but this version is (arguably) nicer for unrelated reasons.
Yes, that creates 50 short lived functions. I don't think you should be worried unless you have manifest evidence that that causes a performance problem in your case.
But I would define a method that transforms SalesOrder into a Map[String, Any], then you would just have
trait SalesOrder {
def fields: Map[String, Any]
}
def diff(a: SalesOrder, b: SalesOrder): Iterable[String] = {
val af = a.fields
val bf = b.fields
af.collect { case (key, value) if bf(key) != value => key }
}
If the field names are indeed just incremental numbers, you could simplify
trait SalesOrder {
def fields: Iterable[Any]
}
def diff(a: SalesOrder, b: SalesOrder): Iterable[String] =
(a.fields zip b.fields).zipWithIndex.collect {
case ((av, bv), idx) if av != bv => f"f${idx + 1}%02d"
}
I want to achieve the following:
There is a list of strings I need to process.
There are several different kinds of these processors, each of which knows which part of the string to read.
I need to work in 2 phases: first, processors need to see each input string to build processor-specific data; second, each input string is processed by each of the processors, and the resulting strings are combined into one.
It's easy to do it in a mutable way: there's a common base class for all processors, the different kinds of data they aggregate is encapsulated in the concrete implementations; the interface consists of just 2 functions --- "look at input string and build internal data" and "process input string using your internal data."
As I am writing it in Scala, I am wondering if there exists a pure functional approach. The problem is that now the base trait for these processors is parameterized by the type of their internal data, and there doesn't seem to be a way to have a list of processors of different kinds.
This problem can be demonstrated on a simpler case: say I'd stick with the mutable approach, but for some reason have parameterized the type of what the processor takes from the string:
trait F[V] {
def get(line: String) : V
def aggregate(value: V)
def process(value: V) : String
}
class F1 extends F[Int] // ...
class F2 extends F[HashMap[Int, Int]] // ...
for (s <- List("string1", "string2");
f <- List(new F1(), new F2())
{
f.aggregate(f.get(s)); // Whoops --- doesn't work
}
It doesn't work because f.get(s) returns Any. Looks like I need to express in Scala's type system that List(new F1(), new F2()) contains F[?] that are different but consistent in that if I take an element of that list, it has some concrete value of its type parameter, and f.get(s) is of that type, which should be accepted by f.aggregate().
In the end, I would like to have something like this (with omissions because I don't get how to do it):
trait F[D] {
def initData : D
def aggregate(line: String, data: D) : D
def process(line: String, data: D) : String
}
class F1 extends F[Int] // ...
class F2 extends F[HashMap[Int, Int]] // ...
// Phase 1
// datas --- List of f.initData, how to?
for (s <- List("string1", "string2")) {
for (f <- List(new F1(), new F2()) {
// let fdata be f's data
// update fdata with f.aggregate(s, fdata)
}
}
// Phase 2
for (s <- List("string1", "string2")) {
for (f <- List(new F1(), new F2()) {
// let fdata be f's data
// for all fs, concatenate f.process(s, fdata) into an output string
}
}
Questions:
Is this task solvable in pure functional way in Scala?
Is this task solvable in other functional languages?
This situation looks like quite a general one. Is there a name for it I could search?
Where is the best place to read about it, assuming little to no background on theory of types and functional programming languages?
Also, you may use abstract types instead of generics, so:
trait F {
type D
def initData: D
def aggregate(line: String, data: D): D
def process(line: String, data: D): String
}
class F1 extends F { type D = Int } // ...
class F2 extends F { type D = Map[Int, Int] } // ...
val strings = List("string1", "string2")
for (f <- List(new F1(), new F2())) {
val d = strings.foldLeft(f.initData) { (d, s) => f.aggregate(s, d) }
for (s <- strings)
f.process(s, d)
}
Don't sure, if I undrestood correct order of operation, but it may be a starting point.
Edit Just noticed, that my former solution was overly verbose, consing up a temporary data structure without any need.
I am not sure, what you mean with "purely functional". The following solution (if it is a solution to your problem) is "purely functional", as it has no side effects except the final println call in main.
Note, that the List[F[_]](...) is important, since otherwise, the compiler will infer a very specific internal type for the elements of the list, which doesn't go well with the aggregateAndProcess function.
trait F[D] {
type Data = D // Abbreviation for easier copy+paste below. Does not
// contribute to the actual solution otherwise
def initData: Data
def aggregate(line: String, data: Data) : Data
def process(line: String, aggData: Data): String
}
class F1 extends F[Int] {
def initData: Data = 1
def aggregate(line: String, data: Data) : Data = data + 1
def process(line: String, aggData: Data): String = line + "/F1" + aggData
}
class F2 extends F[Boolean] {
def initData: Data = false
def aggregate(line: String, data: Data) : Data = !data
def process(line: String, aggData: Data): String = line + "/F2" + aggData
}
object Main {
private def aggregateAndProcess[T](line: String, processor: F[T]): String =
processor.process(line, processor.aggregate(line, processor.initData))
def main(args: Array[String]) {
val r = for {
s <- List("a", "b")
d <- List[F[_]](new F1, new F2)
} yield
aggregateAndProcess(s, d)
println(r.toList)
}
}
Note, though, that I am still unsure as to what you actually want to accomplish. The F interface doesn't really specify, which information flows from which method into whatever location at what time, so: this is still a best-guess efford.