Giter VIP home page Giter VIP logo

sourcecode's Introduction

sourcecode

"com.lihaoyi" %% "sourcecode" % "0.1.0" // Scala-JVM
"com.lihaoyi" %%% "sourcecode" % "0.1.0" // Scala.js

sourcecode is a small Scala library for that provides common "source code" context to your program at runtime, similar to Python's __name__, C++'s __LINE__ or Ruby's __FILE__'. For example, you can ask for the file-name and line number of the current file:

val file = sourcecode.File()
assert(file.endsWith("/sourcecode/shared/src/test/scala/sourcecode/Tests.scala"))

val line = sourcecode.Line()
assert(line == 16)

This might not be something you want to use for "business logic", but is very helpful for debugging, logging and providing automatic diagnostics. This information is also available via an implicit, letting you write functions that automatically pull it in.

The kinds of compilation-time data that sourcecode provides are:

  • sourcecode.File: full path of the current file where the call occurs
  • sourcecode.Line: current line number
  • sourcecode.Name: the name of the nearest enclosing definition: val, class, whatever.
  • sourcecode.FullName: the name of the nearest enclosing definition: val, class, whatever, prefixed by the names of all enclosing classs, traits, objects or packages. Note that this does not include other enclosing defs, vals, vars or lazy vals`
  • sourcecode.Enclosing: the name of the nearest enclosing definition: val, class, whatever, prefixed by the names of all enclosing classs, traits, objects or packages, defs, vals, vars or lazy vals`

All these are available both via () and as implicits, e.g. sourcecode.File can be summoned via sourcecode.File() or implicitly[sourcecode.File].value. This also means you can define functions that pull in this information automatically:

def foo(arg: String)(implicit file: sourcecode.File) = {
  ... do something with arg ...
  ... do somethign with file.value ...
}

foo("hello") // the implicit sourcecode.File is filled in automatically

sourcecode does not rely on runtime reflection or stack inspection, and is done at compile-time using macros. This means that it is both orders of magnitude faster than e.g. getting file-name and line-numbers using stack inspection, and also works on Scala.js where reflection and stack inspection can't be used.

Use Cases

At first it might seem strange to make use of these source-level details in your program: shouldn't a program's meaning not change under re-formatting and re-factoring?

It turns out that there are a number of entirely valid use cases for this sort of information that is both extremely handy, and also would not be surprising at all to a developer using your API. Here are a few example use cases:

Logging

You can use sourcecode.File and sourcecode.Line to define log functions that automatically capture their line number and file-name

def log(foo: String)(implicit line: sourcecode.Line, file: sourcecode.File) = {
  println(s"${file.value}:${line.value} $foo")
}

log("Foooooo") // sourcecode/shared/src/test/scala/sourcecode/Tests.scala:86 Fooooo

This can be handy for letting you see where the log lines are coming from, without tediously tagging every log statement with a unique prefix. Furthermore, this happens at compile time, and is thus orders of magnitude faster than getting this information by generating stack traces, and works on Scala.js where stack-inspection does not.

Enums

You can use sourcecode.Name to define an enumeration-value factory function that automatically assigns names to the enum values based on the name of the val that it is assigned to

case class EnumValue(name: String){
  override def toString = name
}
class Enum{
  def value(implicit name: sourcecode.Name) = EnumValue(name.value)
}
object MyEnum extends Enum{
  val firstItem = value // No need to pass in "firstItem" as a string!
  val secondItem = value
}
assert(MyEnum.firstItem.toString == "firstItem")
assert(MyEnum.secondItem.toString == "secondItem")

This is very handy, and this functionality is used in a number of libraries such as FastParse and Scalatags to provide a boilerplate-free experience while still providing good debuggability and convenience.

Debug Prints

How many times have you written tedious code like

object Bar{
  def foo(arg: String) = {
    println("Foo.bar: " + arg)
  }
}

Where you have to prefix every print statement with the name of the enclosing classes, objects or functions to make sure you can find your print output 2-3 minutes later? With source.Enclosing, you can get this for free:

def debug(msg: String)(implicit enclosing: sourcecode.Enclosing) = {
  println(enclosing.value + ": " + msg)
}

debug("Hello!") // sourcecode.Tests.debugRun: Hello!
class Foo(arg: Int){
  def bar(param: String) = {
    debug(arg + " " + param)
  }
}
new Foo(123).bar("lol")  // sourcecode.Tests.debugRun Foo#bar: 123 lol

Version History

0.1.0

  • First release

sourcecode's People

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.