60
1 Modernizing Scala The Journey to Java 8 Jason Zaugg

Modernizing Scala

Embed Size (px)

Citation preview

Page 1: Modernizing Scala

1

ModernizingScalaTheJourneytoJava8

JasonZaugg

Page 2: Modernizing Scala

2

Page 3: Modernizing Scala

3

OverviewScala2.12TraitEncodinginvokedynamicLambdaEncoding

Page 4: Modernizing Scala

4 .1

Scala2.12

Page 5: Modernizing Scala

4 .2

Scala2.12DropJava6/7supportExploitnewfeaturesinJVMImproveJavainteropNewcodegeneratorand-optimiseimplementation

NonGoals:newlanguagefeatures,majorlibrarychanges

Page 6: Modernizing Scala

4 .3

JavaSupport/TargetMatrixJava6 Java8 Java9

Scala2.11 ST S

Scala2.12.0 ST

Scala2.12.N ST S

Page 7: Modernizing Scala

4 .4

CodeGeneratorpreviously:AST->IR->optimizedIR->bytecodenow:AST->bytecode->optimizedbytecodeAOToptimization(ifenabled):

standardlocaloptimizations:jumpchain,dce,nullness,push-pop,redundantstoreetc.Scalaawarebox/unboxelimination(tuples,options)inlining(w/ScalaawareCHA)

Page 8: Modernizing Scala

4 .5

InteropScalalambdasnowfulfilanyfunctionalinterface(FI)ScalatraitswithconcretemethodscanbeimplementedbyJavaclasses

hence,FunctionNarenowFIs

Page 9: Modernizing Scala

4 .6

ConsumeJavaFunctionalAPIsUsefunctionalAPIsfromJavalibraries,e.g.j.u.streamscala> import java.util.stream._import java.util.stream._

scala> Stream.of("a", "b", "c").map[String](_.toUpperCase).findFirst.getres0: String = A

Page 10: Modernizing Scala

4 .7

ConsumeJavaFunctionalAPIsUsesitevarianceinStreamAPIdoesn'tplaynicelywithinference.scala> Stream.of("a", "b", "c").map(_.toUpperCase).findFirst.getres8: ?0 = A

Retrofitdeclarationsitevariance?scala> :powerscala> val List(i, o) = symbolOf[Function[_, _]].typeParamsi: $r.intp.global.Symbol = type To: $r.intp.global.Symbol = type R

scala> i.setFlag(Flags.CONTRAVARIANT); o.setFlag(Flags.COVARIANT)res6: o.type = type R

scala> Stream.of("a", "b", "c").map(_.toUpperCase).findFirst.getres8: String = A

Page 11: Modernizing Scala

4 .8

ExposeScalafunctionalAPIsJavalambdascanfulfilscala.FunctionNjshell> new scala.Some<String>("42").map(x -> x + "!!!").get()$1 ==> "42!!!"

Page 12: Modernizing Scala

4 .9

SmallerJARslambdaloversprofitthemostYMMV

Page 13: Modernizing Scala

5 .1

EncodingTraitswithoutthebloat

Page 14: Modernizing Scala

5 .2

TraitsCheatSheetclassesmayextendoneclass+manytraitstraitsmaycontainanythingthataclassdoes:

initialization,fields,methodbodiesnoconstructorparams(yet)

Page 15: Modernizing Scala

5 .3

2.11traitsTrait:

interfaceTincludingabstractaccessorsforfields

classT$classwithmethodbodies

Subclass:

implementinterfacemethodsbyforwardingtoimplclassfields+concreteaccessors

Page 16: Modernizing Scala

5 .4

2.11traitstrait T { def foo = 42; var v = 1 }class C extends T

Compileto:public interface T { int foo(); int v(); void v_$eq(int); }class T$class { static int foo(T $this) { return 42; } static void $init$(T) { T.v_eq(1); }}class C implements T { C() { T.$init$(this); } private int v; int v() { return v; } void v_eq(int v) { this.v = v } int foo() { return T$class.foo(this); }}

Page 17: Modernizing Scala

5 .5

2.12traits(naive)fieldencodingunchangedleavetraitmethodbodiesintheinterfacetraitconstructorasastaticmethodomitforwardersinclasses

Page 18: Modernizing Scala

5 .6

2.12traits(naive)trait T { def foo = 42 }class C extends T

Compileto:interface T { default int foo() { return 42; } }class C implements T

Page 19: Modernizing Scala

5 .7

Challenge:runtimedispatchclass C { def foo = "C" }trait T extends C { override def foo = "T" }class D extends Tnew D().foo // prints?

Page 20: Modernizing Scala

5 .8

Challenge:runtimedispatchJVMandScaladisagreeondispatchhereWeneedtoadda"traitforwarder"toDclass D extends T {/*synthetic*/ def foo = super[T].foo}

onlyaddincaseofdisagreement

Page 21: Modernizing Scala

5 .9

TraitforwarderstraitforwardersneedtonamearbitraryancestortraitJVMonlyallowsnamedparentsAddredundantparents?

Page 22: Modernizing Scala

5 .10

theparentofmyparentismyparentAddallancestorstoparentinterfacelistWhatcouldpossiblygowrong?!

Page 23: Modernizing Scala

5 .11

theparentofmyparentismyparentStartuptimeballoonedHierarchyVisitorinvm/classfile/defaultMethods.cppuncooperative

Page 24: Modernizing Scala

5 .12

Challenge:supercallswhataboutexplicitsupercalls?supercallsinclassesfollowJava'srestrictionsbutintraits,supercallscantargetanyancestor

Page 25: Modernizing Scala

5 .13

Challenge:supercallsSimplecase:trait T { def foo = 42 }trait U extends T { override def foo = -super[T].foo }class C extends U

Compileto:interface T { default int foo() { return 42; }}interface U extends T { default int foo() { return super.foo(); }}class C implements U

Page 26: Modernizing Scala

5 .14

superdispatchScalasuperrequiresstaticdispatchinvokespecialdispatchesvirtuallyLatentbuginScala2.11SD-143

Page 27: Modernizing Scala

5 .15

class A { def m = 1}

class B extends A { override def m = 2}

trait T extends A

class C extends B with T { override def m = super[T].m}

Page 28: Modernizing Scala

5 .16

class A { def m = 1}

class B extends A { override def m = 2}

trait T extends A

class C extends B with T { override def m = invokespecial T.m}

Page 29: Modernizing Scala

5 .17

class A { def m = 1}

class B extends A { override def m = 2}

trait T extends A

class C extends B with T { override def m = invokeplease A.m // }

Page 30: Modernizing Scala

5 .18

class A { def m = 1}

class B extends A { override def m = 2}

trait T extends A

class C extends B with T { override def m = invokeactually B.m // }

Page 31: Modernizing Scala

5 .19

super-dilemmaIndytotherescue?

bootstrapcouldunreflectSpecialwouldrequireelevatedpermissions

Or,addstaticaccessorsforalltraitmethods

wecanthenuseinvokestaticasbefore

can'ttargetJavadefaultsorclassmethods

introducelanguagerestrictionforsuchcases

Page 32: Modernizing Scala

5 .20

Cleanerstacks2.11:scala> trait T { def foo = throw null }; new T {}.foojava.lang.NullPointerException at T$class.foo(<console>:11) at $anon$1.foo(<console>:13) ... 32 elided

2.12:scala> trait T { def foo = throw null }; new T {}.foojava.lang.NullPointerException at T.foo(<console>:11) ... 30 elided

Page 33: Modernizing Scala

5 .21

TraitsRecapwe'veshedtraitimplclassesandmosttraitforwardersshallower,lesscruftycallstacksnon-privatemethodsstillrequirestaticaccessorsreconsidertheindy-supersolutiondownthetrack

Page 34: Modernizing Scala

6 .1

arriving(fashionablylate)at

theindyparty

Page 35: Modernizing Scala

6 .2

@PolymorphicSignaturescala> import java.lang.invoke._, MethodType._scala> val lookup = MethodHandles.lookup(); import lookup._

scala> findVirtual(classOf[Object], "hashCode", methodType(classOf[Int]))res0: java.lang.invoke.MethodHandle = MethodHandle(Object)int

scala> val hc: Int = res0.invoke(new Object)hc: Int = 1251285265

supportaddedin2.11series

Page 36: Modernizing Scala

6 .3

SymbolLiterals'foo // shorthand for scala.Symbol("foo")def x = f('blah)x eq q

Weusedtocacheinsyntheticstaticfieldprivatestaticsnotsupportedininterfacespublicise?indify!

Page 37: Modernizing Scala

6 .4

Structuralcalls(c: {def close(): Unit}) => c.close()

Compiledtoreflectivecalls+reflectioncacheagain,indifythecacheintothecallsitefuturework:dynalink

first,needtoteachitaboutInt.+etc

Page 38: Modernizing Scala

6 .5

indyliberationscalacbackendemitsindyfornewASTshape

notspecialcasedtousecasesabovemacros:transformapplication=>arbitraryAST(undocumented,experimental)librarylevelsupportforindy

Page 39: Modernizing Scala

6 .6

ScalamacroprimerExperimentalfeatureofScala2.{10,11,12}Clientcodelooks,feels,typecheckslikeanmethodcallMacroauthorwritesapairofmethods:

adeclarationwiththetypesignatureanimplthattransformstheAST

stdlibmacros:stringformatting,quasiquotesquasiquotemacroshorthandsASTde-/con-struction

Page 40: Modernizing Scala

6 .7

Example:linktimepatterncompilation

Page 41: Modernizing Scala

6 .8

patterns:ClientCode// pattern is compiled and cached when call site is linkedcompilePattern("foo.bar").matcher("foo!bar").matches

// bonus static error: Unclosed character classcompilePattern("abc[..")

Page 42: Modernizing Scala

6 .9

patterns:Macroclass impl(val c: Context) extends IndySupport { import ...

val bootstrapSym = typeOf[test.Bootstrap].companion .member(TermName("bootstrap")) val invokedType = NullaryMethodType(typeOf[Pattern])

def compilePattern(s: Tree): Tree = s match { case l@Literal(Constant(s: String)) => checkPattern(l.pos, s) Indy(bootstrapSym, List(l), invokedType) case _ => q"_root_.java.util.regex.Pattern.compile($s)" }}def compilePattern(s: String): Pattern = macro impl.compilePattern

Page 43: Modernizing Scala

6 .10

patterns:BootstrapCallSite bootstrap(Lookup lookup, String invokedName, MethodType invokedType, String value) { return new ConstantCallSite( MethodHandles.constant(Pattern.class, Pattern.compile(value)));}

Page 44: Modernizing Scala

6 .11

indyliberationSeeJohnRose'sideas:Scala'smacrosystemcanhelpexploretheseideas

mlvm-dev/2016-July/006661.html

Page 45: Modernizing Scala

6 .12

Example:Java8/9shimsCouldweusethistohelppeoplecrosstargetJava8/9?Link-timechoiceofUnsafevsVarHandle

Page 46: Modernizing Scala

6 .13

shims:ClientCodeclass Foo { var blah = 42 }

import Macro._

val f = new Foo()compareAndSetInt(classOf[Foo], "blah", f, -1, 0)assert(!compareAndSetInt(classOf[Foo], "blah", f, -1, 0))assert(compareAndSetInt(classOf[Foo], "blah", f, 42, 0))assert(f.blah == 0)

Page 47: Modernizing Scala

6 .14

autostaging? def foo(x: String) = { partiallyEvaluate { List("1", "2", "3").contains(x)) } }

Idea:macropartiallyEvaluatecouldmechanicallyturnthisinto:def foo(x: String) = { invokedynamic[LinkTimeInterpreter, List("1", "2", "3")].contains(x)}

Page 48: Modernizing Scala

6 .15

autostaging?LotsofrelatedresearchinScalaworld,although

"LightweightModularStaging",Rompf,Odersky"Yin-Yang:ConcealingtheDeepEmbeddingofDSLs",Jovanovicetal

integrationwithlinktimeevaldeservesmoreresearch

Page 49: Modernizing Scala

7 .1

indyλ

Page 50: Modernizing Scala

7 .2

Lambdas:BeforeandafterEssentiallythesameasJava7=>8

Page 51: Modernizing Scala

7 .3

Challenge:LMFassumptionsCan'tdefinetoStringUnboxingofnullsinScalais0,notNPEScalaValueClassboxingvoidreturntypeneedstobeboxed

Page 52: Modernizing Scala

7 .4

Challenge:LMFassumptionsscala> (() => ()).toStringres1: String = [function0]

scala> ((x: Int) => x).asInstanceOf[Any => Int].apply(null)res2: Int = 0

scala> ((() => ()) : (() => Any)).apply().getClassres3: Class[_] = class scala.runtime.BoxedUnit

Page 53: Modernizing Scala

7 .5

Challenge:LMFassumptionsSolution:

livewiththenewtoStringselectivelyuseadaptermethodsatthelambdatarget

Page 54: Modernizing Scala

7 .6

Challenge:ScalaSpecializationSpecializedtraitsleavethewrongmethodabstractShouldchangespecializationtofixthisbutitisacomplexbeast,andwe'vestartedoutwithintermediateFIstoflipthemethods.

Page 55: Modernizing Scala

7 .7

Challenge:Serializationjavactreatsserializablefuncinterfacesastheexceptionscalalambdasaretypicallyserializablejavac-style$deserializeLambda$wouldgrowlarge

hardlimitonnumberoflambdas(JDK-8008413)useageneric insteadlambdadeserializer

Page 56: Modernizing Scala

7 .8

Challenge:SerializationSolution:genericlambdadeserializer

bodyof$deserializeLambda$isanindycallsiteforacachepasseslookupalongtogenericcodethatcallsLMFmanuallytradeoff:can'tfailfastforsubtleserializationincompatibilties

Page 57: Modernizing Scala

7 .9

Challenge:lockscopinglazyvalsinlambdastranslateintothis.synchronizedthisusedtobetheanonclassnowitisthelambdahost,whichcontainsthelambdabodyinasyntheticmethod

Page 58: Modernizing Scala

7 .10

Challenge:unwantedcapturelocalmethodsinlambdastranslatetoprivatemethods......intheanonclass(before)...inthelambdahostclass(now)lambdanowretainsareferencetothelambdahostsolution:transitiveanalysistomakeliftedmethodsstatic

Page 59: Modernizing Scala

8

What'snext?2.12.0-RC1endofthemonthDeliverJava9supportincrementallyInvestinbenchmarking,performanceofcompiler/library

Page 60: Modernizing Scala

9

Thanks!Questions?