Mining for granularity beyond AnyVal

54 views
Skip to first unread message

Paul Phillips

unread,
May 11, 2013, 2:01:01 PM5/11/13
to scala-l...@googlegroups.com
https://gist.github.com/paulp/5560802


/* 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
}
Reply all
Reply to author
Forward
0 new messages