Created

Embed URL

HTTPS clone URL

SSH clone URL

You can clone with HTTPS or SSH.

Download Gist

Fix My Scala meetup 1/2014

View EpidemySimulator.scala
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
/* IMPROVE:
* We started with a given (no-arg) EpidemySimulator with an inner Person class but we wanted to
* be able to set its world (# rows, # columns, how many and what people are there) to be able to
* create focused unit tests. So we created class World for that however there was something ugly.
* F.ex. we couldn't use the EpidemySimulator.Person class to set the population because it is an inner
* class and before / outside of its E.S. instance.
*/
// FIXME 1 ugly hack since EpidemySimulator#Person cannot be create w/o an instance of EpidemySimulator
class StaticPerson (val id: Int,
val row: Int = 0,
val col: Int = 0,
val health: Health = Healthy) {}
 
case class World(val roomRows: Int, val roomColumns: Int, val persons: List[StaticPerson]) {}
 
// FIXME 2 ugly hack to be able to construct a new world from an aux. constructor
// - we can't do it there for it must contain call to this(..) as the first thing
// - also, Scala hasn't static methods so we must put it into a dummy object
// (though perhaps I could create a package object)
object Methods {
def createWorld(config: MySimConfig) = {
val people = List.fill(config.population) { create random StaticPerson ... }
new World(config.roomRows, config.roomColumns, people)
}
}
 
/** New construct allowing us to specify config and the world so that we can
* test different (corner) cases more easily */
class EpidemySimulator(protected val config: MySimConfig, world: World) extends Simulator {
 
/** The original no-arg constructor */
def this() {
this(DefaultSimConfig, Methods.createWorld(DefaultSimConfig))
}
 
/** The original Person class as an inner class */
class Person (val id: Int, var row: Int = 0, var col: Int = 0) {
...
}
}
View EpidemySimulator.scala
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
/* ImproveD:
* Is there anything that List and IndexedSeq have in common so
* that we would not need to call .toList on the result of the for
* comprehension?
* I cannot just change the param type to IndexedSeq because in the factory
* method I use List.fill to create a list of persons, which returns list and
* is thus incompatible with IndexedSeq (which hasn't fill).
*
* ANSWER: YES - Seq
*/
val personsIn = for(
r <- 0 to 2;
c <- 1 to 2
) yield new StaticPerson(id = r*10 + c, row = r, col = c, health = if((r,c) == center) HEALTHY else DEAD)
val world = new World(3, 3, personsIn.toList) // TODO any common type of List and IndexedSeq?
View EpidemySimulator.scala
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/* EXPLAIN
* Having FutureResponse in a file of its own works but including it in
* in the kvstore package object in package.scala leads to the following failure:
* found : kvstore.kvstore.FutureResponse, required: kvstore.(some other)kvstore.FutureResponse
* f.ex. when used inside the class kvstore.Replicator.
* WHY?
*/
package kvstore
import ...
case class FutureResponse(source: ActorRef, message: AnyRef, cancellationToken: Cancellable)
 
// This failes:
// // File src/main/scala/kvstore/package.scala:
// package object kvstore {
// case class FutureResponse(...)
// }
View EpidemySimulator.scala
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
/* IMPROVE
* Is there a nicer way to check for failure of a Future than .value.get.isFailure?
*/
val futureList: Future[List[Int]] = ...
Await.ready(futureList, ...);
assert(futureList.value.get.isFailure, "Should have failed") // IMPROVE
 
/* EXPLAIN
* What is this self thing, why do we need it?
*/
class DummyListener(val port: Int, val relativePath: String) extends NodeScala.Listener {
self =>
 
def removeContext() = this.synchronized {
...
}
 
def start() = self.synchronized {
started = true
new Subscription {
def unsubscribe() = self.synchronized {
started = false
}
}
}
 
...
}
View EpidemySimulator.scala
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
/* IMPROVE 1:
* We need to send a message with a unique ID to
* each replicator Actor for each key-value pair
* we have. Is there a nicer yet functional way
* to create a unique ID than using zipWithIndex
* where we use the index as the ID?
*/
for (
replicator <- replicators;
((key, value), idx) <- keyValueStoreMap.toList.zipWithIndex // IMPROVE smarter way of generating IDs for each pair?
) yield replicator ! Replicate(key, Some(value), idx)
 
// -----------------------
/* IMPROVE 2
* Nicer/more effective way to create set of replication message IDs and a persistence message ID
* then turning range to list then prepending pers. id then converting to set?
* Perhaps st. like range.toSet.add(persistMsgId)?
*/
val replicatorMsgIds = 0L until replicators.size // range
val awaitedIds = (persistMsgId :: replicatorMsgIds.toList).toSet // TODO nicer set-ification?
 
//------------------------
/* IMPROVE 3
* Can I remove and get as one step, without seatching the map twice?
*/
val value = myMap.get("key")
myMap ~= "key"
 
// -----------------------
/* IMPROVE 4
* Can I reuse a part of a partial function,
* namely share part of actor message handling
* between two states of the actor (leader and replica)?
*/
def receive = {
case JoinedPrimary => context.become(leader)
case JoinedSecondary => context.become(replica(0L))
}
 
val leader: Receive = {
case Get(key, id) => { // IMPROVE Same for leader and replica state
sender ! GetResult(key, kv.get(key), id)
}
...
}
 
def replica(_: Long): Receive = {
case Snapshot => ...
...
 
case Get(key, id) => {
sender ! GetResult(key, kv.get(key), id)
}
}
View EpidemySimulator.scala
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
/* IMPROVE 1
* Simplify the following - get rid of typed parameter and explicit match of it
* as suggested below it (which does not compile due to type mismatch).
* Failure:
* missing parameter type for expanded function
* The argument types of an anonymous function must be fully known. (SLS 8.5)
* Expected type was: ?
* val pageSubscription: Subscription = pages.observeOn(eventScheduler) subscribe {
* ^
*/
val suggestionSubscription: Subscription = suggestions.observeOn(eventScheduler) subscribe {
value: Try[List[String]] => value match { // TODO simplify this, possibly w/o explicit match
case Success(suggs) => suggestionList.listData = suggs
case Failure(t) => status.text = "ERROR: " + t.toString
}
}
// I want st. like this, which does not compile:
//val suggestionSubscription: Subscription = suggestions.observeOn(eventScheduler) subscribe {
// case Success(suggs) => suggestionList.listData = suggs
// case Failure(t) => status.text = "ERROR: " + t.toString
//}
 
//-------------------------
/* IMPROVE 2
* Is there a nicer way than filtering out undefined options and
* then calling get on the (defined) options to turn clicks into
* values?
*/
// Observable list of selections produced by user selecting a term from the suggstionList and clicking a button;
// if nothing is selected than no event should be generated
val selections: Observable[String] =
button.
clicks.
map(_ => getSelectedTermOption()).
filter(_.isDefined).
map(_.get) // IMPROVE st. nicer than isDefined + get ?
 
//-------------------------
// ImproveD: use @ to capture the whole object matched by case:
case Contains(requester, id, needle) => {
subtreeFor(needle) match {
case Some(node) => node ! Contains(requester, id, needle)
case None => ...
}
}
// =>
case msg @ Contains(requester, id, needle) => {
subtreeFor(needle) match {
case Some(node) => node ! msg
case None => ...
}
}
View EpidemySimulator.scala
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
// IMPROVE 1 a better way then 2 vars and a while loop?
// IMPROVE 2 possible to transform the list without needing to call expensive reverse an the end?
// TODO how to implement with futures, without async+await, is it possible ???
 
def _all[T](fs: List[Future[T]]): Future[List[T]] = async {
var result: List[T] = Nil
var rest = fs
while (rest != Nil) {
result = await {rest.head} :: result
rest = rest.tail
}
result.reverse // IMPROVE get rid of reverse
}
 
//---------------------------------------
/* IMPROVE 3
* Is future.get.get really the rigth/best way?
*/
implicit class FutureOps[T](val f: Future[T]) extends AnyVal {
 
/** Returns the result of this future if it is completed now.
* Otherwise, throws a `NoSuchElementException`. */
def now: T = {
if (!f.isCompleted) throw new NoSuchElementException()
f.value.get.get // IMPROVE; value is Option[Try[T]]
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.