Skip to content

Instantly share code, notes, and snippets.

@aposwolsky
Created March 16, 2013 05:49
Show Gist options
  • Save aposwolsky/5175169 to your computer and use it in GitHub Desktop.
Save aposwolsky/5175169 to your computer and use it in GitHub Desktop.
deep priming version 2
package demo
/* *********** USER ************** */
class User(id: Int) { def name = "User" + id }
/* *********** END OF USER ************** */
/* *********** Page ************** */
class Page(id: Int) { def name = "Page" + id }
/* *********** END OF USER ************** */
/* *********** VENUE ************** */
sealed abstract class Venue {
def venueId: Int
def userId: Int
def pageId: Int
}
trait HasUserPrimed[UserT <: User] { def user: Option[UserT] }
trait HasPagePrimed[PageT <: Page] { def page: Option[PageT] }
case class PrivateHiddenRealVenue(venueId: Int, userId: Int, pageId: Int)
extends Venue with HasUserPrimed[User] with HasPagePrimed[Page] {
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[Page] = None
def page: Option[Page] = {
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[Page]] = {
val records = xs.asInstanceOf[List[PrivateHiddenRealVenue]]
records.foreach(x => {
if (!x.isPageCalced) {
x.isPageCalced = true
x.pageObj = Some(new Page(x.pageId))
}
})
new VenueList[VenueT with HasPagePrimed[Page]](records.asInstanceOf[List[VenueT with HasPagePrimed[Page]]])
}
}
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 CheckinVenuePrimedList[CheckinT <: Checkin, VenueT <: Venue](xs: List[CheckinT with HasVenuePrimed[VenueT]]) {
def primeInsideVenue[VenueTransformed <: VenueT](
transformer: (VenueList[VenueT] => VenueList[VenueTransformed])
) : List[CheckinT with HasVenuePrimed[VenueTransformed]] = {
val records = xs.asInstanceOf[List[PrivateHiddenRealCheckin]]
transformer(records.flatMap(_.venue).asInstanceOf[List[VenueT]])
// This is going to return a List[CheckinT with HasVenuePrimed[..]]
// but unfortunately, [Checkin
records.asInstanceOf[List[CheckinT with HasVenuePrimed[VenueTransformed]]]
}
}
/* TODO(abp):
Given xs: List[CheckinT with HasVenuePrimed[VenueT]
Current behavior:
The implicit will be called as:
wrapCheckinVenuePrimedList[CheckinT with HasVenuePrimed[VenueT], VenueT](xs)
Desired behavior:
implicit called with:
wrapCheckinVenuePrimedList[CheckinT, VenueT](xs)
*/
implicit def wrapCheckinVenuePrimedList[CheckinT <: Checkin, VenueT <: Venue](
xs: List[CheckinT with HasVenuePrimed[VenueT]]
): CheckinVenuePrimedList[CheckinT, VenueT] = new CheckinVenuePrimedList(xs)
class CheckinList[CheckinT <: Checkin](xs: List[CheckinT]) {
def primeVenue: List[CheckinT with HasVenuePrimed[Venue]] = {
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])
}
})
records.asInstanceOf[List[CheckinT with HasVenuePrimed[Venue]]]
}
}
implicit def wrapCheckinList[T <: Checkin](xs: List[T]): CheckinList[T] = new CheckinList(xs)
}
/* ******** END OF PRIMING LIBRARY ********* */
object Demos {
import Priming._
def printCheckinVenue[CheckinT <: Checkin, VenueT <: Venue]
(x: CheckinT with HasVenuePrimed[VenueT]): Unit = {
println("checkin %s at venue %s".format(x.checkinId, x.venue.get.venueId))
/* The next line will not compile since user is not primed.. yay!
println("checkin %s at venue %s with user %s".format(x.checkinId, x.venue.get.venueId, x.venue.get.user.get.name))
*/
}
def printCheckinVenueUser[CheckinT <: Checkin, VenueT <: Venue]
(x: CheckinT with HasVenuePrimed[VenueT with HasUserPrimed[User]]): Unit = {
println("checkin %s at venue %s with user %s".format(x.checkinId, x.venue.get.venueId, x.venue.get.user.get.name))
}
def printCheckinVenuePage[CheckinT <: Checkin, VenueT <: Venue]
(x: CheckinT with HasVenuePrimed[VenueT with HasPagePrimed[Page]]): Unit = {
println("checkin %s at venue %s with page %s".format(x.checkinId, x.venue.get.venueId, x.venue.get.page.get.name))
}
type FullyPrimedVenue = HasUserPrimed[User] with HasPagePrimed[Page]
def printFullCheckin[CheckinT <: Checkin, VenueT <: Venue]
(x: CheckinT with HasVenuePrimed[VenueT with FullyPrimedVenue]): Unit = {
println("checkin %s at venue %s with user %s page %s".format(
x.checkinId,
x.venue.get.venueId,
x.venue.get.user.get.name,
x.venue.get.page.get.name
))
}
def main(argv: Array[String]) = {
val rawCheckins: List[Checkin] = List(CheckinBuilder.build(101, 201), CheckinBuilder.build(102, 202))
println("Priming demo...")
/* next line fails to compile as expected since venue is not primed! yay!
rawCheckins.foreach(printCheckinVenue)
*/
val primedVenues: List[Checkin with HasVenuePrimed[Venue]] = rawCheckins.primeVenue
println("\nprimedVenues")
primedVenues.foreach(printCheckinVenue)
/* next line fails to compile as expected since primeVenue gives us a base(unprimed) Venue! yay!
val primedVenuesFail: List[Checkin with HasVenuePrimed[Venue with HasUserPrimed[User]]] = rawCheckins.primeVenue
*/
// Deep priming!
val primedVenuesWithVenueUsers: List[Checkin with HasVenuePrimed[Venue with HasUserPrimed[User]]] = {
rawCheckins.primeVenue.primeInsideVenue(_.primeUser)
}
println("\nprimedVenuesWithUsers")
primedVenuesWithVenueUsers.foreach(printCheckinVenueUser)
/* The following fails to compile as expected as page is not primed
primedVenuesWithVenueUsers.foreach(printCheckinVenuePage)
*/
val primeVenueUsersAgain = primedVenuesWithVenueUsers.primeInsideVenue(_.primeUser)
println("\nprimedVenuesWithUsersPrimedTwice")
primeVenueUsersAgain.foreach(printCheckinVenueUser)
val allPrimed: List[Checkin with HasVenuePrimed[Venue with HasUserPrimed[User] with HasPagePrimed[Page]]] = {
primedVenuesWithVenueUsers.primeInsideVenue(_.primePage)
}
println("\nallPrimed")
allPrimed.foreach(printFullCheckin)
// The fully primed type can be used in functions that are not fully primed
allPrimed.foreach(printCheckinVenuePage)
allPrimed.foreach(printCheckinVenueUser)
allPrimed.foreach(printCheckinVenue)
val allPrimedNoTypeAscriptions = rawCheckins.primeVenue.primeInsideVenue(_.primeUser).primeInsideVenue(_.primePage)
allPrimedNoTypeAscriptions.foreach(printCheckinVenuePage)
allPrimedNoTypeAscriptions.foreach(printCheckinVenueUser)
allPrimedNoTypeAscriptions.foreach(printCheckinVenue)
/* The following line does not work:
allPrimedNoTypeAscriptions.foreach(printFullCheckin)
because we want type List[Checkin with HasVenuePrimed[Venue with HasUserPrimed[User] with HasPagePrimed[Page]]
but instead we are getting an "equivalent" type
List[Checkin with HasVenuePrimed[Venue]
with HasVenuePrimed[Venue with HasUserPrimed[User]]
with HasVenuePrimed[Venue with HasPagePrimed[Page]]]
*/
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment