public
Last active

  • Download Gist
olympics.scala
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 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
import scala.collection.mutable._
 
// I want these std functions to be implicit:
implicit def tupled[a1, a2, b] = Function.tupled[a1, a2, b](_)
implicit def untupled[a1, a2, b] = Function.untupled[a1, a2, b](_)
 
class Connection
 
class ConnectionPool(size: Int) {
def withConnection[A](f: Connection => A) = f(new Connection)
}
 
trait Query {
def select: Seq[Int]
def execute: Int
}
 
type QueryFactory = (Connection, String) => Query
 
case class SimpleQuery(connection: Connection, queryString: String) extends Query {
def select = {
Thread.sleep(1000)
println("Selecting " + queryString + " on " + connection)
Array(1, 2, 3)
}
 
def execute() = {
Thread.sleep(1000)
println("Executing " + queryString + " on " + connection)
1
}
def use(thing: Int) = thing
}
 
trait QueryEvaluator {
def select(queryString: String): Seq[Int]
def execute(queryString: String): Int
def transaction[A](f: QueryEvaluator => A): A
}
 
class InTransaction(Query: QueryFactory)(connection: Connection) extends QueryEvaluator {
def select(queryString: String) = Query(connection, queryString).select
def execute(queryString: String) = Query(connection, queryString).execute
 
def transaction[A](f: QueryEvaluator => A) = f(this)
}
 
class SimpleQueryEvaluator(Query: QueryFactory)(pool: ConnectionPool) {
def select(queryString: String) = pool.withConnection(Query(_, queryString).select)
def execute(queryString: String) = pool.withConnection(Query(_, queryString).execute)
 
def transaction[A](f: QueryEvaluator => A) = {
pool.withConnection { c => f(new InTransaction(Query)(c)) }
}
}
 
abstract class QueryProxy(protected var query: Query) extends Query {
def select = delegate("select") { query.select }
def execute() = delegate("execute") { query.execute }
 
protected def delegate[A](name: String)(f: => A): A
 
def reverse: QueryProxy = query match {
case query: QueryProxy =>
val reverse = query.reverse
val inner = reverse.query
this.query = inner
reverse.query = this
reverse
case _ => this
}
}
 
case class TimingOut(timeout: Int)(query: Query) extends QueryProxy(query) {
def delegate[A](name: String)(f: => A) = {
val result = f // Timeouts are more complicated in Java!
println("Did not timeout! Yay fast database!")
result
}
}
 
class Stats {
def measure[A](name: String)(f: => A) = {
val start = System.currentTimeMillis
val result = f
val end = System.currentTimeMillis
println("Measured " + name + " at " + (end - start) / 1000 + " seconds")
result
}
}
 
case class StatsCollecting(stats: Stats)(query: Query) extends QueryProxy(query) {
def delegate[A](name: String)(f: => A) = stats.measure(name)(f)
}
 
case class Memoizing(Query: QueryFactory) extends QueryFactory {
val memo = Map[(Connection, String), Query]()
 
def apply(connection: Connection, queryString: String) = {
memo.getOrElseUpdate((connection, queryString), Query(connection, queryString))
}
}
 
case class Reversing(Query: QueryFactory) extends QueryFactory {
def apply(connection: Connection, queryString: String) = Query(connection, queryString) match {
case query: QueryProxy => query.reverse
case query: Query => query
}
}
 
val Query = Memoizing apply (SimpleQuery andThen TimingOut(1000) andThen StatsCollecting(new Stats))
for (Query <- List(Query, Reversing(Query))) {
val queryEvaluator = new SimpleQueryEvaluator(Query)(new ConnectionPool(20))
 
queryEvaluator.transaction { t =>
t.select("SELECT ... FROM ... FOR UPDATE ...")
t.execute("INSERT ...")
t.execute("INSERT ...")
}
println()
}
 
// I don't really love the side-effecty reverse. But this actually has one fewer bug than the Ruby code
// Which I found thanks to the type-checker. This also took me about twice as long to write, though.

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.