Created
March 16, 2013 05:49
-
-
Save aposwolsky/5175169 to your computer and use it in GitHub Desktop.
deep priming version 2
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 ************** */ | |
/* *********** 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