2

This question isn't programming language specific (the more general the better), but I'm working in Scala (not necessarily on the JVM). Is there a means to reference count by call location, not the number of total calls? In particular, it would be great to be able to detect if a given method is called from more than one call location.

I think I can fake it to some extent by doing a reference equality check with a function, but this could be abused easily by having a global-ish token, or even calling the function multiple times in the same scope:

sealed case class Token();
class MyClass[A] {
  var tokenOpt: Option[Token] = None
  def callMeFromOnePlace(x: A)(implicit tk: Token) = {
    tokenOpt match {
      case Some(priorTk) => if (priorTk ne tk) throw new IllegalStateException("")
      case None => tokenOpt = Some(tk)
    }
    // Do some work ...
  }
}

Then this should work fine:

val myObj = new MyClass[Int]
val myIntList = List(1,2,3)
implicit val token = Token()
myIntList.map(ii => myObj.callMeFromOnePlace(ii)) 

But unfortunately, so would this:

val myObj = new MyClass[Int]
implicit val token = Token()
myObj.callMeFromOnePlace(1)
myObj.callMeFromOnePlace(1) //oops, want this to fail
bbarker
  • 11,636
  • 9
  • 38
  • 62

3 Answers3

1

When you are talking about call location, it can be represented by a call stack trace. Here is a simple example:

// keep track of calls here (you can use immutable style if you want)
var callCounts = Map.empty[Int, Int]

def f(): Unit = {
  // calculate call stack trace hashCode for more efficient storage
  // .toSeq makes WrappedArray, that knows how to properly calculate .hashCode()
  val hashCode = new RuntimeException().getStackTrace.toSeq.hashCode()
  val callLocation = hashCode
  callCounts += (callLocation -> (callCounts.getOrElse(callLocation, 0) + 1))
}

List(1,2,3).foreach(_ =>
  f()
)
f()
f()
println(callCounts) // Map(75070239 -> 3, 900408638 -> 1, -1658734417 -> 1)
Oleksandr.Bezhan
  • 2,028
  • 3
  • 22
  • 31
  • Thanks! I suspected stacktraces might be the answer, and it is compatible with Scala.js. Amazingly, or not (http://stackoverflow.com/questions/9406775/why-does-string-hashcode-in-java-have-many-conflicts), I had hash collisions from the get-go, so I might need to play around with how I generate hash codes. For now, I'm just looking at the sequence of line numbers, but this isn't ideal: `RuntimeException().getStackTrace.toSeq.map(stelem => stelem.getLineNumber).mkString("\n").hashCode` – bbarker May 12 '17 at 21:41
  • Hashing along two dimensions (positional - line #, and semantic - file/class/method name) and combining the results into a single hash code seems like a good general approach. – bbarker May 13 '17 at 00:14
1

I am not completely clear what you want to do but for your //oops.. example to fail you need just check the PriorTk is not None. (do note that it is not a thread safe solution )

Arnon Rotem-Gal-Oz
  • 25,469
  • 3
  • 45
  • 68
  • Yes, I need to look into how to make this thread safe as well, thanks for confirming my suspicion. Regarding PriorTk not being none: that's not good enough; if it is a different token from a different location, it should also fail. – bbarker May 12 '17 at 21:44
  • That would also fail if the token is already set (again except the race condition in multiple threads situation) – Arnon Rotem-Gal-Oz May 13 '17 at 03:33
1

For completeness, enforcing these kind of constraints from a type system perspective requires linear types.

OlivierBlanvillain
  • 7,701
  • 4
  • 32
  • 51