Maybe I'm missing something, but isn't there only one invocation of
foo(pos: Position) being wrapped, namely the one in,
@inline final def foo = withPosition(foo)
^^^
So wouldn't we only ever see that one source position?
That said, I'm very much in favour or trying to unify these mechanisms
if it's at all possible.
Cheers,
Miles
--
Miles Sabin
tel: +44 7813 944 528
gtalk: mi...@milessabin.com
skype: milessabin
g+: http://www.milessabin.com
http://twitter.com/milessabin
http://underscoreconsulting.com
http://www.chuusai.com
I would love a means to issue a casting vote in case of implicit
ambiguity. Scalaz has a ton of boilerplate (example [1]) that would
melt away with something more refined than LowPriorityImplicits.
This would work for me, although I understand that using annotations
to influence type checking isn't to everyone's taste.
@weight(+1) implicit def sourceLocation = ...
-jason
[1] https://github.com/scalaz/scalaz/blob/scalaz-seven/core/src/main/scala/scalaz/OptionT.scala
Okay, here we go:
[1] Location-generating macro:
https://github.com/scalamacros/kepler/blob/0cabc96861c4d7b0e9dcdb8ea1394f401ceba5d5/test/files/run/macro-sip19/Impls_Macros_1.scala
[2] Location-aware function:
https://github.com/scalamacros/kepler/blob/0cabc96861c4d7b0e9dcdb8ea1394f401ceba5d5/test/files/run/macro-sip19/Test_2.scala
Good news:
+ It's totally possible to implement SIP-19 without any changes to the
compiler.
Bad news:
- Chaining of source locations becomes ugly (see [2] for the example),
because implicit parameter of type SourceLocation and implicit macro
of type => SourceLocation conflict. Any ideas how to fix that?
On Mar 30, 1:10 pm, Miles Sabin <mi...@milessabin.com> wrote:
> On Fri, Mar 30, 2012 at 11:02 AM, Eugene Burmako <eugene.burm...@epfl.ch> wrote:> gtalk: mi...@milessabin.com
> > Okay, the boilerplate can be reduced to just one method:
>
> > @inline final def foo = withPosition(foo)
>
> > def foo(pos: Position) = <original definition of foo>
>
> > Where withPosition is a macro that goes from T to T and injects the position
> > into the invocation it wraps.
>
> Maybe I'm missing something, but isn't there only one invocation of
> foo(pos: Position) being wrapped, namely the one in,
>
> @inline final def foo = withPosition(foo)
> ^^^
>
> So wouldn't we only ever see that one source position?
>
> That said, I'm very much in favour or trying to unify these mechanisms
> if it's at all possible.
>
> Cheers,
>
> Miles
>
> --
> Miles Sabin
> tel: +44 7813 944 528
> skype: milessabin
> g+:http://www.milessabin.comhttp://twitter.com/milessabinhttp://underscoreconsulting.comhttp://www.chuusai.com
On Sun, Apr 1, 2012 at 7:14 PM, Eugene Burmako <eugene....@epfl.ch> wrote:
Okay, here we go:
[1] Location-generating macro:
https://github.com/scalamacros/kepler/blob/0cabc96861c4d7b0e9dcdb8ea1394f401ceba5d5/test/files/run/macro-sip19/Impls_Macros_1.scala
[2] Location-aware function:
https://github.com/scalamacros/kepler/blob/0cabc96861c4d7b0e9dcdb8ea1394f401ceba5d5/test/files/run/macro-sip19/Test_2.scala
Good news:
+ It's totally possible to implement SIP-19 without any changes to the
compiler.
Bad news:
- Chaining of source locations becomes ugly (see [2] for the example),
because implicit parameter of type SourceLocation and implicit macro
of type => SourceLocation conflict. Any ideas how to fix that?
But as far as I can see SIP 19 does not specify chaining?
In my previous email, I was talking about the language import statements, I didn't mention compiler flags. The email was about how this new proposition looked to a user. It's not clear how one should use it, or where it should go. A clearer answer would've been that it could go in the SourceLocation object (whose source would of course import macros as proposed in SIP-18), which could be imported (normally) by the user. Is that what you intend to do with it?
====
It's not really clear from the chain of emails where or really in depth *how* SourceLocations would be implemented (yes, there's a sketch linked to, but it's preliminary and it's not clear where it'd go in the library) and thus how they'd appear (and how they could misbehave) before users.Before settling on macros for this one, maybe it's a good idea to have it fully implemented and tested first? Or at least, an organized plan would be nice before this one's voted on…So, the one big thing going for the current approach is that SourceLocations have been in Delite/scala-virtualized for a while, they're understood, used, and tested, they're fully-implemented and sitting as a pull-request on scala/master: https://github.com/scala/scala/pull/343The new macro-based approach would have to be better-baked, integrated, and more thoroughly tested pretty soon…
With respect to chaining, why isn't it a linked list? As long as it's
getting special support it may as well support that much.
trait SourceLocation extends Serializable {
def fileName: String = ""
def line: Int
def charOffset: Int = 0
// the missing piece
def previous: SourceLocation = NoSourceLocation
}
// From extempore's giant collection of abandoned git branches
package scala.tools
package reflect
import scala.tools.nsc.util.{ JavaStackFrame, FrameContext, StackSlice }
import scala.collection.{ mutable, immutable, SeqLike }
import mutable.{ WrappedArray, ArrayBuilder }
import Caller._
class CallerOps(override val repr: Caller) extends
SeqLike[StackTraceElement, Caller] {
override protected[this] def thisCollection:
WrappedArray[StackTraceElement] = repr.stack
override protected[this] def toCollection(repr: Caller):
WrappedArray[StackTraceElement] = repr.stack
override protected[this] def newBuilder = new
ArrayBuilder.ofRef[StackTraceElement] mapResult { xs => Caller(xs, 0)
}
def apply(idx: Int) = repr.stack(idx)
def length = repr.stack.length
def iterator = repr.stack.iterator
def seq = repr.stack.seq
}
/** A class for recording from whence a certain call originated.
* This trick is accomplished by having an implicit value of type Caller
* available, and when said implicit is invoked, a stack trace is recorded
* and trimmed to start with the original caller.
*/
class Caller private (val complete: StackSlice, val index: Int) {
def stack = complete drop index
def top = JavaStackFrame(stack(index))
def context() = stack map (x => FrameContext(x))
def frames() = stack map (x => JavaStackFrame(x))
def dropConstructors = dropWhile(_.isConstructor)
def dropWhile(pred: StackTraceElement => Boolean): Caller =
dropUntil(x => !pred(x))
def dropUntil(pred: StackTraceElement => Boolean): Caller = {
stack indexWhere pred match {
case -1 => Caller.Empty
case idx => drop(idx)
}
}
def drop(numFrames: Int): Caller = Caller(complete, index + numFrames)
def intersect(other: Caller): Caller = {
if (stack.length == 0 || other.stack.length == 0)
Caller.Empty
else {
val idx1 = stack indexWhere (_ == other.stack.head)
lazy val idx2 = other.stack indexWhere (_ == stack.head)
if (idx1 > 0)
this drop idx1
else if (idx2 > 0)
other drop idx2
else
(this drop 1) intersect (other drop 1)
}
}
def combine(implicit other: Caller) = intersect(other)
def show() { show(complete.length) }
def show(num: Int) { context() take num foreach println }
private def methodString = dropConstructors match {
case Empty => ""
case frame => " (" + frame.top.tag + ")"
}
override def toString =
if (top.isConstructor) "constructor for class " + top.className +
methodString
else if (top.isStaticConstructor) "static initializer for " + top.className
else top.tag
}
trait LowPriorityCaller {
self: Caller.type =>
private val ThreadName = classOf[Thread].getName
private val CallerName = classOf[Caller].getName
private val CallerFile = nonThreadFrames.head.getFileName
private def nonThreadFrames = Thread.currentThread.getStackTrace
dropWhile (_.getClassName == ThreadName)
private def skipFrame(ste: StackTraceElement) = ste.getFileName == CallerFile
def trace() = apply(nonThreadFrames, 0) dropWhile skipFrame
implicit def reifyCallerStack: Caller = apply(nonThreadFrames, 0)
dropWhile skipFrame
}
object Caller extends LowPriorityCaller {
object Empty extends Caller(Array(), -1) {
override def top = null
override def intersect(other: Caller) = this
override def toString = "NoCaller"
}
def apply(complete: StackSlice, index: Int): Caller = {
if (index < 0 || index >= complete.length) Caller.Empty
else new Caller(complete, index)
}
def caller[T](implicit c: Caller) = c
}