Suppose I have the following code:
BroadcastMessage gets a list of groups of students in the form of iterators that can only be traversed once
On call to sendMessage it will send message to all students in the groups.
class BroadcastMessage(message:String,groups:List[Iterator[Student]]) {
def sendMessage:Unit = {
groups.foreach(group=>group.foreach(sendeMessage))
}
private def sendMessage(student:Student): Unit ={
EmailClient.sendMessage(student.email,message)
}
}
case class Student(id: String,email:String)
Say that student can exist in several groups and we don't want to send him more than one email.
The mutable solution would be to add mutable set and to add the id of the student to the set and only send the message if the id exists in the set.
class BroadcastMessage(message:String,groups:List[Iterator[Student]]) {
// evil mutable set
private var set:scala.collection.mutable.Set[String] = Set()
def sendMessage:Unit = {
groups.foreach(group=>group.foreach(sendeMessage))
}
private def sendMessage(student:Student): Unit ={
if (set.add(student.id)) {
EmailClient.sendMessage(student.email, message)
}
}
}
How do I implement it in immutable way?
Well, I think that you are making two different mutable things in your example, while you only really need one.
You either need a private val set : mutable.Set[Student] = mutable.Set.empty[Student] or a private var set : Set[Student] = Set.empty[Student]. That is, you either need to mutate the set itself or just the reference to it that your class holds. I would personally go for the latter, ending up with something like:
case class Student(id: String,email:String)
class BroadcastMessage(message:String,groups:List[Iterator[Student]]) {
private var set : Set[Student] = Set.empty // <- no mutable Set, just a mutable reference to several immutable Sets
def sendMessage:Unit = {
groups.foreach(group=>group.foreach(sendMessage))
}
private def sendMessage(student:Student): Unit = {
if (!set(student)) {
set = set + student
EmailClient.sendMessage(student.email, message)
}
}
}
Finally, you could even get rid of the sendMessage(student : Student) method all together:
case class Student(id: String,email:String)
class BroadcastMessage(message:String,groups:List[Iterator[Student]]) {
private var set : Set[Student] = Set.empty
def sendMessage:Unit = {
val students = (for{
group <- groups
student <- group
} yield student).toSet
val toBeNotified = (students -- set)
toBeNotified.foreach(student => EmailClient.sendMessage(student.email, message))
set = set ++ toBeNotified
}
}
I guess it depends on style then...
I managed to do do this. I think I lost some readability but it is mutable:
class BroadcastMessage(message: String, groups: List[Iterator[Student]]) {
def sendMessage(): Unit = {
groups.foldLeft[Set[String]](Set.empty)(sendMessage)
}
private def sendMessage(sent: Set[String], group: Iterator[Student]):Set[String] = {
group.foldLeft[Set[String]](sent)(sendMessage)
}
private def sendMessage(sent: Set[String], student: Student): Set[String] = {
if (!sent.contains(student.id)) {
EmailClient.sendMessage(student.email, message)
return sent + student.id
}
sent
}
}
If you have no memory restrictions, you can just do this:
def sendMessage:Unit = {
groups.flatten.distinct.foreach(sendMessage)
}
It looks like what you are looking for is all unique Students in the nested collection.
A very simple way to do this is by flattening the collection and converting it to a Set; here is an example with Ints:
scala> val groups = List(Iterator(1,2,3), Iterator(3,4,5))
groups: List[Iterator[Int]] = List(non-empty iterator, non-empty iterator)
scala> val unique: Set[Int] = groups.flatten.toSet
unique: Set[Int] = Set(5, 1, 2, 3, 4)
A problem here is that the toSet methods actually copies your list. To avoid this, you can use this little trick (you can read more about collection.breakOut and CanBuildFrom here):
scala> val unique: Set[Int] = groups.flatMap(identity)(collection.breakOut)
unique: Set[Int] = Set(5, 1, 2, 3, 4)
However, the source of mutability here is the usage of an Iterator which will be consumed anyway, there mutating and breaking referential transparency.
You can do it with a one-liner:
def sendMessage: Unit =
groups.reduce(_ ++ _).toStream.distinct.foreach(sendMessage)
Expanded version for learning purpose:
val students: Iterator[Student] = groups.reduce(_ ++ _)
val sStudents: Stream[Student] = students.toStream
val dStudents: Stream[Student] = sStudents.distinct
def sendMessage: Unit = sStudents.foreach(sendMessage)
Related
I'm new to Scala, I'm trying to transform and add an object while iterating a list in Scala, but struggling badly with syntax and probably concept as well.
I want to achieve something like below as we do in JAVA.
public Set<Manager> promoteEmployeesToManager(List<Employee> employees) {
Set<Manager> managers = new HashSet<Manager>();
for(employee: employees){
if(employee.getExprience() > 5) {
managers.add(promoteEmployee(employee));
}
}
return managers;
}
public Manager promoteEmployee(Employee employee){
return new Manager(employee);
}
I tried below code but it's not working. In the step of saveNewManagers(newManagers.toArray); the newManagers is still empty.
private def processEmployeePromotion(employees : List[Employee]) {
val newManagers: Set[Manager] = Set()
employees.asScala.map(employee => promoteEmployee(newManagers, employee))
saveNewManagers(newManagers.toArray);
}
Then promoteEmployee method is like:
private def promoteEmployee (newManagers : Set[Manager], employee: Employee){
if(employee.getExprience() > 5) {
managers+.(new Manager(employee));
}
}
So you have a List of employees and you want a List of new managers.
def processEmployeePromotion(employees :List[Employee]) :List[Manager] =
employees.collect{case emp if emp.getExprience() > 5 => promoteEmployee(emp)}
This will return an empty List if the employees list is empty or if no employees qualify for promotion.
val managers: Set[Manager] = employees
.iterator
.filter(_.getExperience > 5)
.map(promoteEmployee)
.toSet
In Scala, Set is immutable by default, so the + method on Set returns a new set.
private def promoteEmployee(managers: Set[Manager], employee: Employee): Set[Manager] =
if (employee.getExperience > 5) {
managers + new Manager(employee)
} else {
managers
}
private def processEmployeePromotion(employees: List[Employee]): Unit = {
val newManagers = employees.asScala.foldLeft(Set.empty[Manager]) { (managers, employee) =>
promoteEmployee(managers, employee)
}
saveNewManagers(newManagers.toArray)
}
The major change here is the foldLeft which basically abstracts away the idea of looping and building up the Set.
An alternative strategy would be to have promoteEmployee return an Option[Manager]; you can think of Option as being like a collection of at most one element.
private def promoteEmployee(employee: Employee): Option[Manager] =
if (employee.getExperience > 5) {
Option(new Manager(employee))
} else {
None
}
private def promotedEmployees(employees: Iterable[Employee]): Set[Manager] =
employees.flatMap(promoteEmployee).toSet // NB: might not work if Manager doesn't obey the equality contract...
private def processEmployeePromotion(employees: List[Employee]): Unit = {
saveNewManagers(promotedEmployees(employees.asScala).toArray)
}
When you start to work with Scala you have to get used to work with immutable data structures, foldables, expressions... etc . You can still work with mutable as you used to do with Java if you want but the first is what you should do.
Look at this example using foldLeft from List:
case class Employee(id: String, name: String, experience: Int)
case class Manager(employee: Employee)
object Your_Code extends App {
def promoteEmployee(employee: Employee) =
Manager(employee)
def promoteEmployeesToManager(list: List[Employee]) : Set[Manager] = {
list.foldLeft(Set[Manager]())((a, b) => if(b.experience > 5) a + promoteEmployee(b) else a)
}
val employess = List(Employee("id1", "name1", 6), Employee("id1", "name1", 4))
println(promoteEmployeesToManager(employess))
}
My use case has case classes something like
case class Address(name:String,pincode:String){
override def toString =name +"=" +pincode
}
case class Department(name:String){
override def toString =name
}
case class emp(address:Address,department:Department)
I want to create a DSL like below.Can anyone share the links about how to create a DSL and any suggestions to achieve the below.
emp.withAddress("abc","12222").withDepartment("HR")
Update:
Actual use case class may have more fields close to 20. I want to avoid redudancy of code
I created a DSL using reflection so that we don't need to add every field to it.
Disclamer: This DSL is extremely weakly typed and I did it just for fun. I don't really think this is a good approach in Scala.
scala> create an Employee where "homeAddress" is Address("a", "b") and "department" is Department("c") and that_s it
res0: Employee = Employee(a=b,null,c)
scala> create an Employee where "workAddress" is Address("w", "x") and "homeAddress" is Address("y", "z") and that_s it
res1: Employee = Employee(y=z,w=x,null)
scala> create a Customer where "address" is Address("a", "b") and "age" is 900 and that_s it
res0: Customer = Customer(a=b,900)
The last example is the equivalent of writing:
create.a(Customer).where("address").is(Address("a", "b")).and("age").is(900).and(that_s).it
A way of writing DSLs in Scala and avoid parentheses and the dot is by following this pattern:
object.method(parameter).method(parameter)...
Here is the source:
// DSL
object create {
def an(t: Employee.type) = new ModelDSL(Employee(null, null, null))
def a(t: Customer.type) = new ModelDSL(Customer(null, 0))
}
object that_s
class ModelDSL[T](model: T) {
def where(field: String): ValueDSL[ModelDSL2[T], Any] = new ValueDSL(value => {
val f = model.getClass.getDeclaredField(field)
f.setAccessible(true)
f.set(model, value)
new ModelDSL2[T](model)
})
def and(t: that_s.type) = new { def it = model }
}
class ModelDSL2[T](model: T) {
def and(field: String) = new ModelDSL(model).where(field)
def and(t: that_s.type) = new { def it = model }
}
class ValueDSL[T, V](callback: V => T) {
def is(value: V): T = callback(value)
}
// Models
case class Employee(homeAddress: Address, workAddress: Address, department: Department)
case class Customer(address: Address, age: Int)
case class Address(name: String, pincode: String) {
override def toString = name + "=" + pincode
}
case class Department(name: String) {
override def toString = name
}
I really don't think you need the builder pattern in Scala. Just give your case class reasonable defaults and use the copy method.
i.e.:
employee.copy(address = Address("abc","12222"),
department = Department("HR"))
You could also use an immutable builder:
case class EmployeeBuilder(address:Address = Address("", ""),department:Department = Department("")) {
def build = emp(address, department)
def withAddress(address: Address) = copy(address = address)
def withDepartment(department: Department) = copy(department = department)
}
object EmployeeBuilder {
def withAddress(address: Address) = EmployeeBuilder().copy(address = address)
def withDepartment(department: Department) = EmployeeBuilder().copy(department = department)
}
You could do
object emp {
def builder = new Builder(None, None)
case class Builder(address: Option[Address], department: Option[Department]) {
def withDepartment(name:String) = {
val dept = Department(name)
this.copy(department = Some(dept))
}
def withAddress(name:String, pincode:String) = {
val addr = Address(name, pincode)
this.copy(address = Some(addr))
}
def build = (address, department) match {
case (Some(a), Some(d)) => new emp(a, d)
case (None, _) => throw new IllegalStateException("Address not provided")
case _ => throw new IllegalStateException("Department not provided")
}
}
}
and use it as emp.builder.withAddress("abc","12222").withDepartment("HR").build().
You don't need optional fields, copy, or the builder pattern (exactly), if you are willing to have the build always take the arguments in a particular order:
case class emp(address:Address,department:Department, id: Long)
object emp {
def withAddress(name: String, pincode: String): WithDepartment =
new WithDepartment(Address(name, pincode))
final class WithDepartment(private val address: Address)
extends AnyVal {
def withDepartment(name: String): WithId =
new WithId(address, Department(name))
}
final class WithId(address: Address, department: Department) {
def withId(id: Long): emp = emp(address, department, id)
}
}
emp.withAddress("abc","12222").withDepartment("HR").withId(1)
The idea here is that each emp parameter gets its own class which provides a method to get you to the next class, until the final one gives you an emp object. It's like currying but at the type level. As you can see I've added an extra parameter just as an example of how to extend the pattern past the first two parameters.
The nice thing about this approach is that, even if you're part-way through the build, the type you have so far will guide you to the next step. So if you have a WithDepartment so far, you know that the next argument you need to supply is a department name.
If you want to avoid modifying the origin classes you can use implicit class, e.g.
implicit class EmpExtensions(emp: emp) {
def withAddress(name: String, pincode: String) {
//code omitted
}
// code omitted
}
then import EmpExtensions wherever you need these methods
Is there a way to share a variable among all objects (instantiated from the same type)? Consider the following simple program. Two objects name and name2 have the same type A. Is there way to connect the properyList inside the two instantiation name and name2?
class A {
var properyList = List[String]()
def printProperties(): Unit = {
println(properyList)
}
}
object Experiment {
def main(args: Array[String]): Unit = {
val name = new A
val name2 = new A
name.properyList = List("a property")
name.printProperties()
name2.printProperties()
}
}
The output is
List(a property)
List()
Any way to change the class definition so that by just changing the .properyList in one of the objects, it is changed in all of the instatiations?
What you seem to be looking for is a class variable. Before I get into why you should avoid this, let me explain how you can do it:
You can attach propertyList to the companion object instead of the class:
object A {
var properyList = List[String]()
}
class A {
def printProperties(): Unit = {
println(A.properyList)
}
}
Now, to the why you shouldn't:
While scala let's you do pretty much anything that the JVM is capable of, its aims are to encourage a functional programming style, which generally eschews mutable state, especially shared, mutable state. I.e. the anti-pattern in A is not only that propertyList is a var, not a val but by sharing it via the companion object, you further allow anyone, from any thread to change the state of all instances at anytime.
The benefit of declaring your data as val is that you can safely pass it around, since you can be sure that nobody can change from under you at any time in the future.
You seem to be looking for something like java static fields.
In scala you usually achieve something like that by using a companion object:
object Main extends App {
class A {
import A._
def printProperties(): Unit = {
println(properyList)
}
}
object A {
private var properyList = List[String]()
def addProperty(prop: String): Unit = {
properyList ::= prop
}
}
val name = new A
val name2 = new A
A.addProperty("a property")
name.printProperties()
name2.printProperties()
}
If you want to have something similar to java's static fields you will have to use companion objects.
object Foo {
private var counter = 0
private def increment = {
counter += 1;
counter
}
}
class Foo {
val i = Foo.increment
println(i)
}
Code copied from:
"Static" field in Scala companion object
http://daily-scala.blogspot.com/2009/09/companion-object.html
Based on Arne Claassen's answer, but using private mutable collection with the companion object, which makes it visible only to the companion classes. Very simplistic example tried out in scala 2.11.7 console:
scala> :paste
// Entering paste mode (ctrl-D to finish)
object A {
private val mp = scala.collection.mutable.Map("a"->1)
}
class A {
def addToMap(key:String, value:Int) = { A.mp += (key -> value) }
def getValue(key:String) = A.mp.get(key)
}
// Exiting paste mode, now interpreting.
defined object A
defined class A
// create a class instance, verify it can access private map in object
scala> val a = new A
a: A = A#6fddee1d
scala> a.getValue("a")
res1: Option[Int] = Some(1)
// create another instance and use it to change the map
scala> val b = new A
b: A = A#5e36f335
scala> b.addToMap("b", 2)
res2: scala.collection.mutable.Map[String,Int] = Map(b -> 2, a -> 1)
// verify that we cannot access the map directly
scala> A.mp // this will fail
<console>:12: error: value mp is not a member of object A
A.mp
^
// verify that the previously created instance sees the updated map
scala> a.getValue("b")
res4: Option[Int] = Some(2)
Consider this case class
case class sample(val s: String = { /*compiled code*/ })
{
val name:String = { /*compiled code*/ }
val no:Int = { /*compiled code*/ }
}
I need to create instance of this class with particular values set to name and no.
But I cannot use another class which extends this class. I have to pass the same class instance.
You mean someone has gone to the trouble of making sure name and no are set a particular way and cannot be changed, and you want to make them contain something else, which may violate all sorts of assumptions? That's not a good idea, and the JVM will stop you from doing something so foolish (barring absurdly difficult tricks involving custom classloaders for everything).
If you merely mean that you want to set those vals, but not usually have to worry about them in the constructor, then there are a variety of solutions. One is to use lazy vals and private vars with setters:
case class C(val s: String = "fish") {
private var myN: Option[Int] = None
def initializeN(n0: Int) {
myN = Some(n0)
if (n != n0) throw new Exception("Already initialized")
}
lazy val n: Int = myN.getOrElse(s.length) // s.length is your code block
}
Which works like so:
scala> C("salmon")
res0: C = C(salmon)
scala> res0.initializeN(2); res0.n
res1: Int = 2
scala> C("herring")
res1: C = C(herring)
scala> res1.n
res2: Int = 5
scala> res1.initializeN(2)
java.lang.Exception: Already initialized
at C.initializeN(<console>:11)
...
There are various other tricks you can play if you want compile-time safety of initialization. Later parameter blocks can refer to earlier ones, and don't appear as match arguments:
case class D(val s: String = "fish")(val n: Int = s.length) {}
You can overload constructors:
case class E(val s: String, n: Int) {
def this(s: String) = this(s, s.length)
def this() = this("fish")
}
and various mixtures of the above are possible also.
And making name and no constructor parameter is not an option?
case class Sample(s: String = { /*compiled code*/ },
name: String = { /*compiled code*/ },
no: Int = { /*compiled code*/ })
I assume that the /* compiled code */ determines the default values of the fields. You can use the code as follows:
Sample() // all default values
Sample("some value for s") // default values for name and no
Sample("s", "name", 123) // explicit values, no defaults
I'm have a parent-child relation between 2 classes, but the child objects are never stored. I do get an warning:
"org.datanucleus.store.appengine.MetaDataValidator checkForIllegalChildField: Unable to validate relation net.vermaas.kivanotify.model.UserCriteria.internalCriteria"
but it is unclear to me why this occurs. Already tried several alternatives without luck.
The parent class is "UserCriteria" which has a List of "Criteria" as children.
The classes are defined as follows (Scala):
class UserCriteria(tu: String, crit: Map[String, String]) extends LogHelper {
#PrimaryKey
#Persistent{val valueStrategy = IdGeneratorStrategy.IDENTITY}
var id = KeyFactory.createKey("UserCriteria", System.nanoTime)
#Persistent
var twitterUser = tu
#Persistent
var internalCriteria: java.util.List[Criteria] = flatten(crit)
def flatten(crits: Map[String, String]) : java.util.List[Criteria] = {
val list = new java.util.ArrayList[Criteria]
for (key <- crits.keySet) {
list.add(new Criteria(this, key, crits(key)))
}
list
}
def criteria: Map[String, String] = {
val crits = mutable.Map.empty[String, String]
for (i <- 0 to internalCriteria.size-1) {
crits(internalCriteria.get(i).name) = internalCriteria.get(i).value
}
Map.empty ++ crits
}
// Stripped the equals, canEquals, hashCode, toString code to keep the code snippet short...
}
#PersistenceCapable
#EmbeddedOnly
class Criteria(uc: UserCriteria, nm: String, vl: String) {
#Persistent
var userCriteria = uc
#Persistent
var name = nm
#Persistent
var value = vl
override def toString = {
"Criteria name: " + name + " value: " + value
}
}
Any ideas why the childs are not stored? Or why I get the error message?
Thanks,
Gero
It looks to me like you are trying to implement an owned one-to-many relationship. Also you seem to have forgotten the #Embedded annotation for the UserCriteria's internalCriteria field, though I think, that it might still not work since the field contains a list and not the embedded class itself.