Created
March 16, 2013 00:06
-
-
Save aposwolsky/5174193 to your computer and use it in GitHub Desktop.
deep priming, v1
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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