Scala REPL实现原理与Spark Shell简介
版权声明:本文为博主原创文章,未经博主允许不得转载。
手动码字不易,请大家尊重劳动成果,谢谢
Scala是我最喜欢的语言之一,与Java语言不同,Scala语言十分简洁,自带了很多的算子可以帮助我以最快的时间去实现我要实现的功能,因此可以留出更多的时间来进行思考。还有一点就是Scala提供了Scala REPL,在我不确定一个算子的功能时,我可以很快打开REPL进行验证,并直观得到答案。
由于Scala提供了REPL(ReadEvalPrint Loop),但是它并不是一门解释性语言,下面我们进入Scala REPL源码中来一探究竟。
Scala REPL
打开Scala REPL很简单,在安装好Scala的机器的命令行里输入Scala即可打开Scala REPL:
这里系统打开的是path中的scala命令,windows中为scala.bat。打开scala安装目录下的bin文件夹找到这两个文件。在文件中,我们都可以看到它的启动类:scala.tools.nsc.MainGenericRunner
。因此我们从这个类入手,对Scala REPL源码进行简单的分析。
val howToRun = targetAndArguments match {
case Nil => AsRepl
case hd :: _ => waysToRun find (_.name == settings.howtorun.value) getOrElse guessHowToRun(hd)
}
def runTarget(): Either[Throwable, Boolean] = howToRun match {
case AsObject =>
ObjectRunner.runAndCatch(settings.classpathURLs, thingToRun, command.arguments)
case AsScript =>
ScriptRunner.runScriptAndCatch(settings, thingToRun, command.arguments)
case AsJar =>
JarRunner.runJar(settings, thingToRun, command.arguments)
case Error =>
Right(false)
case _ =>
// We start the repl when no arguments are given.
Right(new interpreter.ILoop process settings)
}
我们可以看到,在scala命令为无参的情况下会打开Right(new interpreter.ILoop process settings)
,这就是Scala REPL的入口了,我们继续进入到process
方法中一探究竟:
// start an interpreter with the given settings
def process(settings: Settings): Boolean = savingContextLoader {
this.settings = settings
createInterpreter()
// sets in to some kind of reader depending on environmental cues
in = in0.fold(chooseReader(settings))(r => SimpleReader(r, out, interactive = true))
globalFuture = future {
intp.initializeSynchronous()
loopPostInit()
!intp.reporter.hasErrors
}
loadFiles(settings)
printWelcome()
try loop() match {
case LineResults.EOF => out print Properties.shellInterruptedString
case _ =>
}
catch AbstractOrMissingHandler()
finally closeInterpreter()
true
}
/** Create a new interpreter. */
def createInterpreter() {
if (addedClasspath != "")
settings.classpath append addedClasspath
intp = new ILoopInterpreter
}
private def loopPostInit() {
// Bind intp somewhere out of the regular namespace where
// we can get at it in generated code.
intp.quietBind(NamedParam[IMain]("$intp", intp)(tagOfIMain, classTag[IMain]))
// Auto-run code via some setting.
( replProps.replAutorunCode.option
flatMap (f => io.File(f).safeSlurp())
foreach (intp quietRun _)
)
// classloader and power mode setup
intp.setContextClassLoader()