Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Functional Object-Oriented Imperative Scala / 関数型オブジェクト指向命令型 Scala by Sébastien Doeraene

22 views

Published on

on June 28th at ScalaMatsuri 2019.
http://2019.scalamatsuri.org/index_en.html

Published in: Software
  • DOWNLOAD FULL BOOKS, INTO AVAILABLE Format, ......................................................................................................................... ......................................................................................................................... 1.DOWNLOAD FULL. PDF EBOOK here { https://tinyurl.com/y6a5rkg5 } ......................................................................................................................... 1.DOWNLOAD FULL. EPUB Ebook here { https://tinyurl.com/y6a5rkg5 } ......................................................................................................................... 1.DOWNLOAD FULL. doc Ebook here { https://tinyurl.com/y6a5rkg5 } ......................................................................................................................... 1.DOWNLOAD FULL. PDF EBOOK here { https://tinyurl.com/y6a5rkg5 } ......................................................................................................................... 1.DOWNLOAD FULL. EPUB Ebook here { https://tinyurl.com/y6a5rkg5 } ......................................................................................................................... 1.DOWNLOAD FULL. doc Ebook here { https://tinyurl.com/y6a5rkg5 } ......................................................................................................................... ......................................................................................................................... ......................................................................................................................... .............. Browse by Genre Available eBooks ......................................................................................................................... Art, Biography, Business, Chick Lit, Children's, Christian, Classics, Comics, Contemporary, Cookbooks, Crime, Ebooks, Fantasy, Fiction, Graphic Novels, Historical Fiction, History, Horror, Humor And Comedy, Manga, Memoir, Music, Mystery, Non Fiction, Paranormal, Philosophy, Poetry, Psychology, Religion, Romance, Science, Science Fiction, Self Help, Suspense, Spirituality, Sports, Thriller, Travel, Young Adult,
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • DOWNLOAD FULL BOOKS, INTO AVAILABLE Format, ......................................................................................................................... ......................................................................................................................... 1.DOWNLOAD FULL. PDF EBOOK here { https://tinyurl.com/y6a5rkg5 } ......................................................................................................................... 1.DOWNLOAD FULL. EPUB Ebook here { https://tinyurl.com/y6a5rkg5 } ......................................................................................................................... 1.DOWNLOAD FULL. doc Ebook here { https://tinyurl.com/y6a5rkg5 } ......................................................................................................................... 1.DOWNLOAD FULL. PDF EBOOK here { https://tinyurl.com/y6a5rkg5 } ......................................................................................................................... 1.DOWNLOAD FULL. EPUB Ebook here { https://tinyurl.com/y6a5rkg5 } ......................................................................................................................... 1.DOWNLOAD FULL. doc Ebook here { https://tinyurl.com/y6a5rkg5 } ......................................................................................................................... ......................................................................................................................... ......................................................................................................................... .............. Browse by Genre Available eBooks ......................................................................................................................... Art, Biography, Business, Chick Lit, Children's, Christian, Classics, Comics, Contemporary, Cookbooks, Crime, Ebooks, Fantasy, Fiction, Graphic Novels, Historical Fiction, History, Horror, Humor And Comedy, Manga, Memoir, Music, Mystery, Non Fiction, Paranormal, Philosophy, Poetry, Psychology, Religion, Romance, Science, Science Fiction, Self Help, Suspense, Spirituality, Sports, Thriller, Travel, Young Adult,
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • Be the first to like this

Functional Object-Oriented Imperative Scala / 関数型オブジェクト指向命令型 Scala by Sébastien Doeraene

  1. 1. Functional OO ImperativeFunctional OO Imperative ScalaScala 関数型オブジェクト指向命関数型オブジェクト指向命 令型 Scala令型 Scala Sébastien Doeraene -- セバスチャン·ドゥラン June 28, 2019 -- Scala Matsuri -- 2019年6⽉28⽇ @sjrdoeraene
  2. 2. Scala Center, École polytechnique fédérale de Lausanne               scala.epfl.ch 1
  3. 3. BasicsBasics Japanese line 1 Japanese line 2 2
  4. 4. def times2(xs: List[Int]): List[Int] = { var result: List[Int] = Nil var i = 0 while (i < xs.length) { result = result :+ (xs(i) * 2) i += 1 } result } 3
  5. 5. def times2(xs: List[Int]): List[Int] = { var result: List[Int] = Nil for (i <- 0 until xs.length) { result = result :+ (xs(i) * 2) } result } 4
  6. 6. def times2(xs: List[Int]): List[Int] = { val builder = List.newBuilder[Int] for (i <- 0 until xs.length) { builder += xs(i) * 2 } builder.result() } 5
  7. 7. def times2(xs: List[Int]): List[Int] = { val builder = List.newBuilder[Int] for (x <- xs) { builder += x * 2 } builder.result() } 6
  8. 8. def times2(xs: List[Int]): List[Int] = { for (x <- xs) yield x * 2 } def times2(xs: List[Int]): List[Int] = { xs.map(x => x * 2) } 7
  9. 9. Sometimes, builders are better Japanese line 1 Japanese line 2 8
  10. 10. def fromPathClasspath(classpath: Seq[Path]): (Seq[IRContainer], Seq[Path]) = { val containers = Seq.newBuilder[IRContainer] val paths = Seq.newBuilder[Path] for (entry <- classpath if Files.exists(entry)) { val attrs = Files.readAttributes(entry, classOf[BasicFileAttributes]) if (attrs.isDirectory()) { walkIR(entry) { (path, attrs) => containers += IRContainer.fromIRFile(...) paths += path } } else if (entry.getFileName().toString().endsWith(".jar")) { containers += new JarIRContainer(entry, attrs.lastModifiedTime()) paths += entry } else { throw new IllegalArgumentException("Illegal classpath entry " + entry) } } (containers.result(), paths.result()) } 9
  11. 11. def fromPathClasspath(classpath: Seq[Path]): (Seq[IRContainer], Seq[Path]) = { val containers = Seq.newBuilder[IRContainer] val paths = Seq.newBuilder[Path] for (entry <- classpath if Files.exists(entry)) { val attrs = Files.readAttributes(entry, classOf[BasicFileAttributes]) if (attrs.isDirectory()) { walkIR(entry) { (path, attrs) => containers += IRContainer.fromIRFile(...) paths += path } } else if (entry.getFileName().toString().endsWith(".jar")) { containers += new JarIRContainer(entry, attrs.lastModifiedTime()) paths += entry } else { throw new IllegalArgumentException("Illegal classpath entry " + entry) } } (containers.result(), paths.result()) } 9
  12. 12. Sometimes, s are better Japanese line 1 Japanese line 2 10
  13. 13. var test = { genIsScalaJSObject(obj) && genIsClassNameInAncestors(...) } def typeOfTest(typeString: String): js.Tree = ... if (isAncestorOfString) test = test || typeOfTest("string") if (isAncestorOfHijackedNumberClass) { test = test || typeOfTest("number") if (useBigIntForLongs) test = test || genCallHelper("isLong", obj) } if (isAncestorOfBoxedBooleanClass) test = test || typeOfTest("boolean") if (isAncestorOfBoxedCharacterClass) test = test || (obj instanceof envField("Char")) test 11
  14. 14. var test = { genIsScalaJSObject(obj) && genIsClassNameInAncestors(...) } def typeOfTest(typeString: String): js.Tree = ... if (isAncestorOfString) test = test || typeOfTest("string") if (isAncestorOfHijackedNumberClass) { test = test || typeOfTest("number") if (useBigIntForLongs) test = test || genCallHelper("isLong", obj) } if (isAncestorOfBoxedBooleanClass) test = test || typeOfTest("boolean") if (isAncestorOfBoxedCharacterClass) test = test || (obj instanceof envField("Char")) test 11
  15. 15. val test0 = { genIsScalaJSObject(obj) && genIsClassNameInAncestors(...) } def typeOfTest(typeString: String): js.Tree = ... val test1 = if (isAncestorOfString) test0 || typeOfTest("string") else test0 val test3 = if (isAncestorOfHijackedNumberClass) { val test2 = test1 || typeOfTest("number") if (useBigIntForLongs) test2 || genCallHelper("isLong", obj) else test2 } val test4 = if (isAncestorOfBoxedBooleanClass) test3 || typeOfTest("boolean") else test3 val test5 = if (isAncestorOfBoxedCharacterClass) test4 || (obj instanceof envField("Char")) else test4 test5 12
  16. 16. val test0 = { genIsScalaJSObject(obj) && genIsClassNameInAncestors(...) } def typeOfTest(typeString: String): js.Tree = ... val test1 = if (isAncestorOfString) test0 || typeOfTest("string") else test0 val test3 = if (isAncestorOfHijackedNumberClass) { val test2 = test1 || typeOfTest("number") if (useBigIntForLongs) test2 || genCallHelper("isLong", obj) else test2 } val test4 = if (isAncestorOfBoxedBooleanClass) test3 || typeOfTest("boolean") else test3 val test5 = if (isAncestorOfBoxedCharacterClass) test4 || (obj instanceof envField("Char")) else test4 test5 12
  17. 17. val test0 = { genIsScalaJSObject(obj) && genIsClassNameInAncestors(...) } def typeOfTest(typeString: String): js.Tree = ... val test1 = if (isAncestorOfString) test0 || typeOfTest("string") else test0 val test3 = if (isAncestorOfHijackedNumberClass) { val test2 = test1 || typeOfTest("number") if (useBigIntForLongs) test2 || genCallHelper("isLong", obj) else test2 } val test4 = if (isAncestorOfBoxedBooleanClass) test3 || typeOfTest("boolean") else test3 val test5 = if (isAncestorOfBoxedCharacterClass) test4 || (obj instanceof envField("Char")) else test4 test5 12
  18. 18. val test0 = { genIsScalaJSObject(obj) && genIsClassNameInAncestors(...) } def typeOfTest(typeString: String): js.Tree = ... val test1 = if (isAncestorOfString) test0 || typeOfTest("string") else test0 val test3 = if (isAncestorOfHijackedNumberClass) { val test2 = test1 || typeOfTest("number") if (useBigIntForLongs) test2 || genCallHelper("isLong", obj) else test2 } val test4 = if (isAncestorOfBoxedBooleanClass) test3 || typeOfTest("boolean") else test3 val test5 = if (isAncestorOfBoxedCharacterClass) test4 || (obj instanceof envField("Char")) else test4 test5 12
  19. 19. In the (super) smallIn the (super) small Immutable/functional API Locally imperative implementation (if more readable) Japanese line 1 Japanese line 2 13
  20. 20. Algorithms with mutable internal dataAlgorithms with mutable internal data structuresstructures The of Scala.js The change detection algorithm of the optimizer Japanese line 1 Japanese line 2 14
  21. 21. At the instance levelAt the instance level Japanese line 1 Japanese line 2 15
  22. 22. final class Emitter(config: CommonPhaseConfig) { def emitAll(unit: LinkingUnit, builder: JSLineBuilder, logger: Logger): Unit = { ... } } 16
  23. 23. final class Emitter(config: CommonPhaseConfig) { private class State(val lastMentionedDangerousGlobalRefs: Set[String]) { val jsGen: JSGen = new JSGen(..., lastMentionedDangerousGlobalRefs) val classEmitter: ClassEmitter = new ClassEmitter(jsGen) val coreJSLib: WithGlobals[js.Tree] = CoreJSLib.build(jsGen) } private var state: State = new State(Set.empty) private def jsGen: JSGen = state.jsGen private def classEmitter: ClassEmitter = state.classEmitter private def coreJSLib: WithGlobals[js.Tree] = state.coreJSLib private val classCaches = mutable.Map.empty[List[String], ClassCache] def emitAll(unit: LinkingUnit, builder: JSLineBuilder, logger: Logger): Unit = { ... classCaches.getOrElseUpdate(..., ...) ... val mentionedDangerousGlobalRefs = ... state = new State(mentionedDangerousGlobalRefs) ... } } 17
  24. 24. final class Emitter(config: CommonPhaseConfig) { private class State(val lastMentionedDangerousGlobalRefs: Set[String]) { val jsGen: JSGen = new JSGen(..., lastMentionedDangerousGlobalRefs) val classEmitter: ClassEmitter = new ClassEmitter(jsGen) val coreJSLib: WithGlobals[js.Tree] = CoreJSLib.build(jsGen) } private var state: State = new State(Set.empty) private def jsGen: JSGen = state.jsGen private def classEmitter: ClassEmitter = state.classEmitter private def coreJSLib: WithGlobals[js.Tree] = state.coreJSLib private val classCaches = mutable.Map.empty[List[String], ClassCache] def emitAll(unit: LinkingUnit, builder: JSLineBuilder, logger: Logger): Unit = { ... classCaches.getOrElseUpdate(..., ...) ... val mentionedDangerousGlobalRefs = ... state = new State(mentionedDangerousGlobalRefs) ... } } 17
  25. 25. final class Emitter(config: CommonPhaseConfig) { private class State(val lastMentionedDangerousGlobalRefs: Set[String]) { val jsGen: JSGen = new JSGen(..., lastMentionedDangerousGlobalRefs) val classEmitter: ClassEmitter = new ClassEmitter(jsGen) val coreJSLib: WithGlobals[js.Tree] = CoreJSLib.build(jsGen) } private var state: State = new State(Set.empty) private def jsGen: JSGen = state.jsGen private def classEmitter: ClassEmitter = state.classEmitter private def coreJSLib: WithGlobals[js.Tree] = state.coreJSLib private val classCaches = mutable.Map.empty[List[String], ClassCache] def emitAll(unit: LinkingUnit, builder: JSLineBuilder, logger: Logger): Unit = { ... classCaches.getOrElseUpdate(..., ...) ... val mentionedDangerousGlobalRefs = ... state = new State(mentionedDangerousGlobalRefs) ... } } 17
  26. 26. final class Emitter(config: CommonPhaseConfig) { private class State(val lastMentionedDangerousGlobalRefs: Set[String]) { val jsGen: JSGen = new JSGen(..., lastMentionedDangerousGlobalRefs) val classEmitter: ClassEmitter = new ClassEmitter(jsGen) val coreJSLib: WithGlobals[js.Tree] = CoreJSLib.build(jsGen) } private var state: State = new State(Set.empty) private def jsGen: JSGen = state.jsGen private def classEmitter: ClassEmitter = state.classEmitter private def coreJSLib: WithGlobals[js.Tree] = state.coreJSLib private val classCaches = mutable.Map.empty[List[String], ClassCache] def emitAll(unit: LinkingUnit, builder: JSLineBuilder, logger: Logger): Unit = { ... classCaches.getOrElseUpdate(..., ...) ... val mentionedDangerousGlobalRefs = ... state = new State(mentionedDangerousGlobalRefs) ... } } 17
  27. 27. At the instance levelAt the instance level Immutable/functional object API Mutable state carried between invocations But not observable from the outside The state is encapsulated using object-orientation Japanese line 1 Japanese line 2 18
  28. 28. At the instance levelAt the instance level Not possible using pure functional programming Difficult to reason about if the state is observable (using an imperative API) Unique power of combining functional programming, object-orientation and imperative features Japanese line 1 Japanese line 2 19
  29. 29. Used at several levels: The emitter The optimizer The caches for files The all-encompassing method etc. Japanese line 1 Japanese line 2 20
  30. 30. Open class hierarchiesOpen class hierarchies Japanese line 1 Japanese line 2 21
  31. 31. So far: traits and classes Japanese line 1 Japanese line 2 22
  32. 32. /** A backend of a standard Scala.js linker. */ abstract class LinkerBackend { /** Core specification that this linker backend implements. */ val coreSpec: CoreSpec /** Symbols this backend needs to be present in the linking unit. */ val symbolRequirements: SymbolRequirement /** Emit the given LinkingUnit to the target output. */ def emit(unit: LinkingUnit, output: LinkerOutput, logger: Logger)( implicit ec: ExecutionContext): Future[Unit] } 23
  33. 33. /** The basic backend for the Scala.js linker. */ final class BasicLinkerBackend(config: LinkerBackendImpl.Config) extends LinkerBackend { val coreSpec = config.commonConfig.coreSpec private[this] val emitter = new Emitter(config.commonConfig) val symbolRequirements: SymbolRequirement = emitter.symbolRequirements def emit(unit: LinkingUnit, output: LinkerOutput, logger: Logger)( implicit ec: ExecutionContext): Future[Unit] = { ... val builder = new JSFileBuilder emitter.emitAll(unit, builder, logger) OutputFileImpl.fromOutputFile(output.jsFile) .writeFull(builer.complete()) ... } } 24
  34. 34. /** The Closure backend of the Scala.js linker. */ final class ClosureLinkerBackend(config: LinkerBackendImpl.Config) extends LinkerBackend { val coreSpec = config.commonConfig.coreSpec private[this] val emitter = new Emitter(config.commonConfig) val symbolRequirements: SymbolRequirement = emitter.symbolRequirements def emit(unit: LinkingUnit, output: LinkerOutput, logger: Logger)( implicit ec: ExecutionContext): Future[Unit] = { ... val builer = new ClosureModuleBuilder emitter.emitAll(unit, builer, logger) val closureModules = makeClosureModules(builder.result()) val result = closureCompiler.compileModules(..., closureModules, ...) writeResult(result, ...) ... } } 25
  35. 35. Possible to write your own Scala.js backend in user-space actually does that, to collect imported modules for Webpack Japanese line 1 Japanese line 2 26
  36. 36. /** A JavaScript execution environment. * * This can run and interact with JavaScript code. * * Any implementation is expected to be fully thread-safe. */ trait JSEnv { /** Human-readable name for this [[JSEnv]] */ val name: String /** Starts a new (asynchronous) JS run. */ def start(input: Input, config: RunConfig): JSRun /** Like [[start]], but initializes a communication channel. */ def startWithCom(input: Input, config: RunConfig, onMessage: String => Unit): JSComRun } 27
  37. 37. In the main repo, one implementation: Completely unrelated but compatible implementations are in other repos: Custom environments in users' builds Japanese line 1 Japanese line 2 28
  38. 38. Custom for comprehensionsCustom for comprehensions the M word Japanese line 1 Japanese line 2 29
  39. 39. def genMethod(className: String, method: MethodDef)( implicit globalKnowledge: GlobalKnowledge): WithGlobals[js.Tree] = { val methodBody = method.body.getOrElse( throw new AssertionError("Cannot generate an abstract method")) implicit val pos = method.pos val namespace = method.flags.namespace val methodFunWithGlobals: WithGlobals[js.Function] = desugarToFunction(className, method.args, methodBody, method.resultType) methodFunWithGlobals.flatMap { methodFun => if (namespace != MemberNamespace.Public) { ... } else { if (useClasses) { for (propName <- genPropertyName(method.name)) yield { js.MethodDef(static = false, propName, methodFun.args, methodFun.body) } } else { genAddToPrototype(className, method.name, methodFun) } } } } 30
  40. 40. def genMethod(className: String, method: MethodDef)( implicit globalKnowledge: GlobalKnowledge): WithGlobals[js.Tree] = { val methodBody = method.body.getOrElse( throw new AssertionError("Cannot generate an abstract method")) implicit val pos = method.pos val namespace = method.flags.namespace val methodFunWithGlobals: WithGlobals[js.Function] = desugarToFunction(className, method.args, methodBody, method.resultType) methodFunWithGlobals.flatMap { methodFun => if (namespace != MemberNamespace.Public) { ... } else { if (useClasses) { for (propName <- genPropertyName(method.name)) yield { js.MethodDef(static = false, propName, methodFun.args, methodFun.body) } } else { genAddToPrototype(className, method.name, methodFun) } } } } 30
  41. 41. def genMethod(className: String, method: MethodDef)( implicit globalKnowledge: GlobalKnowledge): WithGlobals[js.Tree] = { val methodBody = method.body.getOrElse( throw new AssertionError("Cannot generate an abstract method")) implicit val pos = method.pos val namespace = method.flags.namespace val methodFunWithGlobals: WithGlobals[js.Function] = desugarToFunction(className, method.args, methodBody, method.resultType) methodFunWithGlobals.flatMap { methodFun => if (namespace != MemberNamespace.Public) { ... } else { if (useClasses) { for (propName <- genPropertyName(method.name)) yield { js.MethodDef(static = false, propName, methodFun.args, methodFun.body) } } else { genAddToPrototype(className, method.name, methodFun) } } } } 30
  42. 42. def genMethod(className: String, method: MethodDef)( implicit globalKnowledge: GlobalKnowledge): WithGlobals[js.Tree] = { val methodBody = method.body.getOrElse( throw new AssertionError("Cannot generate an abstract method")) implicit val pos = method.pos val namespace = method.flags.namespace val methodFunWithGlobals: WithGlobals[js.Function] = desugarToFunction(className, method.args, methodBody, method.resultType) methodFunWithGlobals.flatMap { methodFun => if (namespace != MemberNamespace.Public) { ... } else { if (useClasses) { for (propName <- genPropertyName(method.name)) yield { js.MethodDef(static = false, propName, methodFun.args, methodFun.body) } } else { genAddToPrototype(className, method.name, methodFun) } } } } 30
  43. 43. /** A monad that associates a set of global variable names to a value. */ private[emitter] final case class WithGlobals[+A]( value: A, globalVarNames: Set[String]) { def map[B](f: A => B): WithGlobals[B] = WithGlobals(f(value), globalVarNames) def flatMap[B](f: A => WithGlobals[B]): WithGlobals[B] = { val t = f(value) WithGlobals(t.value, globalVarNames ++ t.globalVarNames) } } private[emitter] object WithGlobals { /** Constructs a `WithGlobals` with an empty set `globalVarNames`. */ def apply[A](value: A): WithGlobals[A] = new WithGlobals(value, Set.empty) def list[A](xs: List[WithGlobals[A]]): WithGlobals[List[A]] = ... def option[A](xs: Option[WithGlobals[A]]): WithGlobals[Option[A]] = ... } 31
  44. 44. Further exploration of the codeFurther exploration of the code Time permitting Japanese line 1 Japanese line 2 32
  45. 45.                    scala-js.org scala.epfl.ch 33

×