Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Some sort of sourcecode.Caller[T] implicit #9

Open
lihaoyi opened this issue Mar 24, 2016 · 8 comments
Open

Some sort of sourcecode.Caller[T] implicit #9

lihaoyi opened this issue Mar 24, 2016 · 8 comments

Comments

@lihaoyi
Copy link
Member

lihaoyi commented Mar 24, 2016

Use cases:

  • Getting the caller for logging or something:
object Foo{
  def log()(implicit caller: sourcecode.Caller[Any]) = {
    println(caller.value)
  }
}
object Bar{
  Foo.log() // Bar
}
  • Restricting who you can be called from
class IKnowWhatImDoing
object Foo{
  def runDangerous()(implicit caller: sourcecode.Caller[IKnowWhatImDoing]) = {
    println(caller.value)
  }
}
object Bar {
  Foo.runDangerous() // compile error
}
object Bar2 extends IKnowWhatImDoing{
  Foo.runDangerous() // ok, prints Bar2
}
  • Getting calling class or classloader, e.g. for loading resources, without needing to worry about properly setting up and tearing down the Context ClassLoader:
object Foo{
  def getResource(path: String)(implicit caller: sourcecode.Caller[_]) = {
    caller.value.getClass.getClassLoader.getResourceAsStream(path)
  }
}
object Bar{
  Foo.getResource("/thing/file.txt") // loads resource from `Bar`s classloader, always
}
@jducoeur
Copy link

Fascinating. If that typed version of Caller can be done in a no-boilerplate kind of way, I have a suspicion that that's quite useful in a lot of different domains...

@lihaoyi
Copy link
Member Author

lihaoyi commented Mar 24, 2016

As I imagine it, it would be a pretty trivial macro expanding

implicitly[sourcecode.Caller[T]]

into

sourcecode.Caller[T](this)

And letting this this bind to whatever enclosing thingy that this normally binds to. That would hit all the use cases I've come up with, though there may be others

@lihaoyi
Copy link
Member Author

lihaoyi commented Mar 24, 2016

Something similar could be done for Caller.Class[T] or Caller.ClassLoader, to make it more fine-grained, and allow easier stubbing-out in the cases where the function only needs one of these things (rather than a whole object) and the caller can easily pass in a custom Class or ClassLoader, just like how you can currently easily pass in custom sourcecode.Names or sourcecode.Lines and it gets implicitly wrapped and all that.

I would want to use a Caller.ClassLoader implicit in Ammonite-Ops to make loading resources work without needing to futz with context classloaders. Not sure if it's worth doing but it's a possibility...

@fizzy33
Copy link

fizzy33 commented Mar 24, 2016

We have DSL's that would make great use of this, providing much richer compilation phase checks.

@lihaoyi
Copy link
Member Author

lihaoyi commented Mar 24, 2016

@fizzy33 anything you could share? I'm interested in knowing what the use cases could be before implementing it and maybe having to change things again later

@fizzy33
Copy link

fizzy33 commented Mar 24, 2016

  • finer grain access control (your IKnowWhatImDoing case)
  • better logger creation (right now we grab the stack trace and have a hardcoded number of frame to walk back, which when you start extending stuff one need to manually massage that hardcoded number)

Are the two obvious ones (that you have already mooted). I am intrigued and trying to work through a few more. One would be to augment our internal DI framework (that leverages guice and scala implicits) though I don't have a solid use case yet, though I am working on it ;-)

@Atry
Copy link

Atry commented Apr 19, 2017

Caller should be a dependent function:

trait Caller {
  type Out
}
object Caller {
  type Aux[Out0] = Caller { type Out = Out0 }
}

So that we can have other type classes that depends on Caller

def info[A](message: String)(implicit caller : Caller.Aux[A], hasLogger: HasLogger[A]) = {
  hasLogger.getLogger.info(message)
}

@Atry
Copy link

Atry commented Apr 19, 2017

See https://gigiigig.github.io/posts/2015/09/13/aux-pattern.html for the Aux pattern.

izhangzhihao added a commit to izhangzhihao/sourcecode that referenced this issue Apr 28, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants