Created
May 11, 2013 17:55
-
-
Save paulp/5560802 to your computer and use it in GitHub Desktop.
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
/* A better way to tag types? | |
* | |
* 1) object Time: here we are distinguishing between different uses of a Long, | |
* yet there is no boxing whatsoever. | |
* | |
* main calls start: ()J | |
* main calls timed: (Function0, J) | |
* Function0 gives up the result: ()J | |
* timed calls now: ()J | |
* timed calls elapsed$extension: (JJ)J | |
* main calls _2: ()J | |
* | |
* 2) object Bounds: Enumerate the acceptable uses. | |
* Only Usage subclasses which appear in the lower bound can appear. | |
*/ | |
import Time._ | |
object Time { | |
// Markers - these can have any structure or no structure. | |
sealed trait Usage | |
sealed trait StartTime extends Usage | |
sealed trait CurrentTime extends Usage | |
sealed trait CalendarTime extends Usage | |
type Current = Timestamp[CurrentTime] | |
type Start = Timestamp[StartTime] | |
type Calendar = Timestamp[CalendarTime] | |
// We could make the constructor private if we wanted to tightly | |
// control instantiation. | |
final class Timestamp[T <: Usage](val nanos: Long) extends AnyVal { | |
// Originally I had this return a Nanos value class, but unfortunately | |
// specialization doesn't work with value classes and it was boxed | |
// in the return value of timed. | |
def elapsed(implicit other: Current): Long = math.abs(other.nanos - nanos) | |
override def toString = s"Timestamp($nanos)" | |
} | |
object Timestamp { | |
implicit def start: Start = new Timestamp[StartTime](System.nanoTime) | |
implicit def now: Current = new Timestamp[CurrentTime](System.nanoTime) | |
def apply(date: java.util.Date): Calendar = new Timestamp[CalendarTime](date.getTime * 1000000L) | |
} | |
// If there were only one "Timestamp" class, the implicit parameter with the | |
// start time stamp would be used as the implicit argument to elapsed rather | |
// than the one in the Timestamp companion. | |
def timed[@specialized T](body: => T)(implicit stamp: Start): (T, Long) = (body, stamp.elapsed) | |
// public scala.Tuple2<java.lang.Object, java.lang.Object> timed$mDc$sp(scala.Function0<java.lang.Object>, long); | |
// 0: new #81 // class scala/Tuple2$mcDJ$sp | |
// 5: invokeinterface #85, 1 // InterfaceMethod scala/Function0.apply$mcD$sp:()D | |
// 17: invokevirtual #31 // Method Time$Timestamp$.now:()J | |
// 20: invokevirtual #35 // Method Time$Timestamp$.elapsed$extension:(JJ)J | |
@annotation.tailrec def busywork(x: Double, reps: Long): Double = { | |
if (reps <= 0) x | |
else busywork((x + util.Random.nextInt) / 3, reps - 1) | |
} | |
def main(args: Array[String]): Unit = { | |
val reps = args(0).toLong | |
// 34: invokevirtual #66 // Method Time$Timestamp$.start:()J | |
// 37: invokevirtual #70 // Method Time$.timed$mDc$sp:(Lscala/Function0;J)Lscala/Tuple2; | |
// 40: invokevirtual #75 // Method scala/Tuple2._2$mcJ$sp:()J | |
val ms = timed(busywork(0, reps))._2 / 1e6 | |
println(f"$reps reps of busywork required $ms%.3f") | |
} | |
} | |
object Bounds { | |
def limited[T >: CurrentTime with StartTime <: Usage](implicit stamp: Timestamp[T]) = println(stamp) | |
def f1 = limited(Timestamp.start) // compiles | |
def f2 = limited(Timestamp.now) // compiles | |
// def f3 = limited(Timestamp(new java.util.Date)) // fails | |
// ./a.scala:65: error: type mismatch; | |
// found : Time.Calendar | |
// (which expands to) Time.Timestamp[Time.CalendarTime] | |
// required: Time.Timestamp[Time.Usage] | |
// Note: Time.CalendarTime <: Time.Usage, but class Timestamp is invariant in type T. | |
// You may wish to define T as +T instead. (SLS 4.5) | |
// def f3 = limited(Timestamp(new java.util.Date)) | |
// ^ | |
// one error found | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment