Skip to content

Instantly share code, notes, and snippets.

@aposwolsky
Created March 16, 2013 00:06
Show Gist options
  • Save aposwolsky/5174193 to your computer and use it in GitHub Desktop.
Save aposwolsky/5174193 to your computer and use it in GitHub Desktop.
deep priming, v1
package demo
/* *********** USER ************** */
class User(id: Int) { def name = "User" + id }
/* *********** END OF USER ************** */
/* *********** VENUE ************** */
sealed abstract class Venue {
def venueId: Int
def userId: Int
}
trait HasUserPrimed[UserT <: User] { def user: Option[UserT] }
trait HasPagePrimed[UserT <: User] { def page: Option[UserT] }
case class PrivateHiddenRealVenue(venueId: Int, userId: Int, pageId: Int)
extends Venue with HasUserPrimed[User] with HasPagePrimed[User] {
var isUserCalced = false
var userObj: Option[User] = None
def user: Option[User] = {
if (isUserCalced) {
userObj
} else {
throw new Exception("Attempting to access unprimed resource!... impossible!")
}
}
var isPageCalced = false
var pageObj: Option[User] = None
def page: Option[User] = {
if (isPageCalced) {
pageObj
} else {
throw new Exception("Attempting to access unprimed resource!... impossible!")
}
}
}
object VenueBuilder {
def build(venueId: Int, userId: Int, pageId: Int): Venue = new PrivateHiddenRealVenue(venueId, userId, pageId)
}
/* ********** END OF VENUE ********* */
/* ********** CHECKIN ********* */
sealed abstract class Checkin {
def checkinId: Int
def venueId: Int
}
trait HasVenuePrimed[VenueT <: Venue] { def venue: Option[VenueT] }
case class PrivateHiddenRealCheckin(checkinId: Int, venueId: Int)
extends Checkin with HasVenuePrimed[PrivateHiddenRealVenue] {
var isVenueCalced = false
var venueObj: Option[PrivateHiddenRealVenue] = None
def venue: Option[PrivateHiddenRealVenue] = {
if (isVenueCalced) {
venueObj
} else {
throw new Exception("Attempting to access unprimed resource!... impossible!")
}
}
}
object CheckinBuilder {
def build(checkinId: Int, venueId: Int): Checkin = new PrivateHiddenRealCheckin(checkinId, venueId)
}
/* ********** END OF CHECKIN ********* */
/* ******** PRIMING LIBRARY ********* */
object Priming {
// The invariant requires that the only real class of Venue is PrivateHiddenRealVenue
class VenueList[VenueT <: Venue](xs: List[VenueT]) {
def primeUser: VenueList[VenueT with HasUserPrimed[User]] = {
val records = xs.asInstanceOf[List[PrivateHiddenRealVenue]]
records.foreach(x => {
if (!x.isUserCalced) {
x.isUserCalced = true
x.userObj = Some(new User(x.userId))
}
})
new VenueList[VenueT with HasUserPrimed[User]](records.asInstanceOf[List[VenueT with HasUserPrimed[User]]])
}
def primePage: VenueList[VenueT with HasPagePrimed[User]] = {
val records = xs.asInstanceOf[List[PrivateHiddenRealVenue]]
records.foreach(x => {
if (!x.isPageCalced) {
x.isPageCalced = true
x.pageObj = Some(new User(x.pageId))
}
})
new VenueList[VenueT with HasPagePrimed[User]](records.asInstanceOf[List[VenueT with HasPagePrimed[User]]])
}
}
implicit def wrapVenueList[T <: Venue](xs: List[T]): VenueList[T] = new VenueList(xs)
// The invariant requires that the only real class of Checkin is PrivateHiddenRealCheckin
class CheckinList[CheckinT <: Checkin](xs: List[CheckinT]) {
/*
def primeVenue[VenueT <: Venue](
transformer: (VenueList[Venue] => VenueList[VenueT]) = ((x:VenueList[Venue]) => x.asInstanceOf[VenueList[VenueT]])
) : List[CheckinT with HasVenuePrimed[VenueT]] = {
val records = xs.asInstanceOf[List[PrivateHiddenRealCheckin]]
records.foreach(x => {
if (!x.isVenueCalced) {
x.isVenueCalced = true
val realVenueObject = VenueBuilder.build(x.venueId, x.venueId + 100, x.venueId + 200)
x.venueObj = Some(realVenueObject.asInstanceOf[PrivateHiddenRealVenue])
}
})
transformer(records.flatMap(_.venue))
records.asInstanceOf[List[CheckinT with HasVenuePrimed[VenueT]]]
}
*/
def primeVenue[VenueU <: Venue, VenueT <: VenueU](
transformer: (VenueList[VenueU] => VenueList[VenueT])
) : List[CheckinT with HasVenuePrimed[VenueT]] = {
val records = xs.asInstanceOf[List[PrivateHiddenRealCheckin]]
records.foreach(x => {
if (!x.isVenueCalced) {
x.isVenueCalced = true
val realVenueObject = VenueBuilder.build(x.venueId, x.venueId + 100, x.venueId + 200)
x.venueObj = Some(realVenueObject.asInstanceOf[PrivateHiddenRealVenue])
}
})
transformer(new VenueList[VenueU](records.flatMap(_.venue).asInstanceOf[List[VenueU]]))
records.asInstanceOf[List[CheckinT with HasVenuePrimed[VenueT]]]
}
}
implicit def wrapCheckinList[T <: Checkin](xs: List[T]): CheckinList[T] = new CheckinList(xs)
}
/* ******** END OF PRIMING LIBRARY ********* */
object Demos {
import Priming._
def main(argv: Array[String]) = {
val xs: List[Checkin] = List(CheckinBuilder.build(101, 201), CheckinBuilder.build(102, 202))
println("Priming demo...")
//xs.foreach(x => println(x.Venue.get.name)) -- failed with compile error! yay!
val primedVenues: List[Checkin with HasVenuePrimed[Venue]] = xs.primeVenue(identity[VenueList[Venue]])
primedVenues.foreach(x => println(x.venue.get.venueId)) // works!
/* This fails with a great error message:
val primedVenuesFail: List[Checkin with HasVenuePrimed[Venue with HasUserPrimed[User]]] = xs.primeVenue
[error] found : List[demo.Checkin with demo.HasVenuePrimed[demo.Venue]]
[error] required: List[demo.Checkin with demo.HasVenuePrimed[demo.Venue with demo.HasUserPrimed[demo.User]]]
*/
// Deep priming!
val primedVenuesWithVenueUsers: List[Checkin with HasVenuePrimed[Venue with HasUserPrimed[User]]] = {
xs.primeVenue((x: VenueList[Venue]) => x.primeUser)
}
val success: List[Checkin with HasVenuePrimed[Venue with HasUserPrimed[User] with HasPagePrimed[User]]] = {
primedVenuesWithVenueUsers.primeVenue((x: VenueList[Venue with HasUserPrimed[User]]) => x.primePage)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment