Created

Embed URL

HTTPS clone URL

SSH clone URL

You can clone with HTTPS or SSH.

Download Gist
View olympics.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.
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.