Skip to content

Instantly share code, notes, and snippets.

@paulp
Created May 11, 2013 17:55
Show Gist options
  • Save paulp/5560802 to your computer and use it in GitHub Desktop.
Save paulp/5560802 to your computer and use it in GitHub Desktop.
/* 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