From 0ca754864c76f546be651d1e4279d5b73883300c Mon Sep 17 00:00:00 2001 From: lihaoyi Date: Sun, 23 Nov 2014 19:42:32 -0800 Subject: First read-through --- book/src/main/scala/book/Utils.scala | 8 +- book/src/main/scalatex/book/Index.scalatex | 7 +- book/src/main/scalatex/book/Intro.scalatex | 10 ++- .../main/scalatex/book/handson/CanvasApp.scalatex | 6 +- .../scalatex/book/handson/ClientServer.scalatex | 13 ++-- .../scalatex/book/handson/CommandLine.scalatex | 16 ++-- .../scalatex/book/handson/GettingStarted.scalatex | 10 +-- .../book/handson/PublishingModules.scalatex | 6 +- .../main/scalatex/book/handson/WebPage.scalatex | 21 +++--- .../book/indepth/AdvancedTechniques.scalatex | 2 +- .../book/indepth/CompilationPipeline.scalatex | 86 ++++++---------------- .../scalatex/book/indepth/DesignSpace.scalatex | 53 +++++++++++-- .../book/indepth/SemanticDifferences.scalatex | 47 +++++++----- .../client/shared/main/scala/simple/FileData.scala | 2 +- .../client/src/main/scala/simple/Client.scala | 1 - .../client/src/main/scala/simple/Client.scala | 1 - .../src/main/scala/canvasapp/FlappyLine.scala | 2 +- 17 files changed, 146 insertions(+), 145 deletions(-) diff --git a/book/src/main/scala/book/Utils.scala b/book/src/main/scala/book/Utils.scala index 171d0ad..91d76a7 100644 --- a/book/src/main/scala/book/Utils.scala +++ b/book/src/main/scala/book/Utils.scala @@ -109,7 +109,7 @@ object lnk{ val IntelliJ = lnk("IntelliJ", "http://blog.jetbrains.com/scala/") val Eclipse = lnk("Eclipse", "http://scala-ide.org/") val Rhino = lnk("Rhino", "https://developer.mozilla.org/en-US/docs/Mozilla/Projects/Rhino") - val Nodejs = lnk("Nodejs", "http://nodejs.org/") + val Nodejs = lnk("Node.js", "http://nodejs.org/") val PhantomJS = lnk("PhantomJS", "http://phantomjs.org/") val Play = lnk("Play", "https://www.playframework.com/") val Scalatra = lnk("Scalatra", "http://www.scalatra.org/") @@ -118,7 +118,7 @@ object lnk{ } object github{ val Scalatags = lnk("Scalatags", "https://github.com/lihaoyi/scalatags") - val uPickle= lnk("Scalatags", "https://github.com/lihaoyi/upickle") + val uPickle= lnk("uPickle", "https://github.com/lihaoyi/upickle") val scalaPickling = lnk("scala-pickling", "https://github.com/scala/pickling") } } @@ -137,7 +137,7 @@ object hl{ .dropWhile(_ == "") .mkString("\n") - pre(code(cls:=lang + " highlight-me", stripped)) + pre(code(cls:=lang + " highlight-me hljs", stripped)) } } @@ -181,7 +181,7 @@ object hl{ pre( - code(cls:=lang + " highlight-me", blob), + code(cls:=lang + " highlight-me hljs", blob), a( cls:="header-link", i(cls:="fa fa-link "), diff --git a/book/src/main/scalatex/book/Index.scalatex b/book/src/main/scalatex/book/Index.scalatex index 1e6600f..7a7e2b8 100644 --- a/book/src/main/scalatex/book/Index.scalatex +++ b/book/src/main/scalatex/book/Index.scalatex @@ -18,13 +18,13 @@ is a set of detailed expositions on various parts of the Scala.js platform. Noth @lnk("Scala.js", "http://www.scala-js.org/") is a compiler that compiles Scala source code to equivalent Javascript code. That lets you write Scala code that you can run in a web browser, or other environments (Chrome plugins, Node.js, etc.) where Javascript is supported. This book is an introduction to Scala.js, which aims to get you from knowing-nothing about it to being relatively proficient. @p - This book contains something for all levels of experience with Scala.js: absolute beginners can get started with the @sect.ref{Intro to Scala.js} and @sect.ref{Hands On} tutorial, people who have used it before can skip ahead to the later parts of the tutorial: @sect.ref{Making a Canvas App} or @sect.ref{Interactive Web Pages}. Intermediate users will find interest in the chapters on @sect.ref{Cross Publishing Libraries} with Scala.js or @sect.ref{Integrating Client-Server}, and even experienced users will find the @sect.ref{In Depth} documention useful. + This book contains something for all levels of experience with Scala.js: absolute beginners can get started with the @sect.ref{Intro to Scala.js} and @sect.ref{Hands On} tutorial, people who have used it before can skip ahead to the later parts of the tutorial: @sect.ref{Making a Canvas App} or @sect.ref{Interactive Web Pages}. Intermediate users will find interest in the chapters on @sect.ref{Cross Publishing Libraries} with Scala.js or @sect.ref{Integrating Client-Server}, and even experienced users will find the @sect.ref{In Depth} documention useful. Feel free to explore the navigation bar on the left to find chapters of interest. @p Even if we do not require any familiarity of Scala.js, this book nonetheless assumes a good amount of background knowledge: of Scala, of Javascript, and of web development as a whole. In general, you will not need deep knowledge of any of these subjects, though if you are coming in entirely without knowledge of any one of them, you'll have to be willing to spend time Google-ing things and picking things up as we go along. Someone who comes in without previous web-dev experience may miss or not-notice many of the nice touches and benefits that Scala.js brings to the table, having never done web-dev any other way, @p - Many of the code samples are taken from examples available on the book's @lnk("Github Page", "https://github.com/lihaoyi/scala-js-book"); for those code samples (e.g. the animation above), there is a link in the bottom-right corner of the snippet that you can click on to go to the original code. These come in handy if you find you need additional context around the snippet, e.g. what imports you need for the code to work, or what the complete executable example looks like. + Many of the code samples are taken from examples available on the book's @lnk("Github Page", "https://github.com/lihaoyi/scala-js-book"); for those code samples (e.g. the animation above), there is a @i(cls:="fa fa-link ") link in the bottom-right corner of the snippet that you can click on to go to the original code. These come in handy if you find you need additional context around the snippet, e.g. what imports you need for the code to work, or what the complete executable example looks like. @p This book is roughly divided into two sections: @@ -70,9 +70,6 @@ is a set of detailed expositions on various parts of the Scala.js platform. Noth @sect{Advanced Techniques} @indepth.AdvancedTechniques() - @sect{Javascript Interoperability} - @indepth.JavascriptInterop() - @sect{Deviations from Scala-JVM} @indepth.SemanticDifferences() diff --git a/book/src/main/scalatex/book/Intro.scalatex b/book/src/main/scalatex/book/Intro.scalatex index 8c51032..10a2638 100644 --- a/book/src/main/scalatex/book/Intro.scalatex +++ b/book/src/main/scalatex/book/Intro.scalatex @@ -9,7 +9,8 @@ def main() = { var x = 0 while(x < 10) x += 3 - println(x) // 12 + println(x) + // 12 } } @@ -20,7 +21,8 @@ while ((x < 10)) { x = ((x + 3) | 0) }; - ScalaJS.m.s_Predef().println__O__V(x) // 12 + ScalaJS.m.s_Predef().println__O__V(x) + // 12 }); @p @@ -60,7 +62,7 @@ In a large code-base, finding out what methods or properties a variable has is often a long chase through dozens of files to see how it ended up being passed to the current function. Refactorings, which are OK when you can just test the code see if it works, become dangerous when your code base is large enough that "just test all the code" would take hours. Language-warts which are slightly annoying in small programs become a minefield in large ones: it's only a matter of time before you hit one, often in code you did-not/cannot test, resulting in breakages in production. @p - Apart from the inherent danger of the language, Javascript has another major problem: the language has left so many things unspecified, yet at the same time provides the ability to emulate these things in a variety of ways. This means that rather than having a single way of e.g. defining a class and instantiating an object, there is a decade-long debate between a dozen different and equally-bad, hand-crafted alternatives. Large code-bases use third-party libraries, and most are guaranteed (purely due to how stastistics work) to do these basic things differently from your own code, making understanding these disparate code-bases (e.g. when something goes wrong) very difficult. + Apart from the inherent danger of the language, Javascript has another major problem: the language has left many things unspecified, yet at the same time provides the ability to emulate these things in a variety of ways. This means that rather than having a single way of e.g. defining a class and instantiating an object, there is a decade-long debate between a dozen different and equally-bad, hand-crafted alternatives. Large code-bases use third-party libraries, and most are guaranteed (purely due to how stastistics work) to do these basic things differently from your own code, making understanding these disparate code-bases (e.g. when something goes wrong) very difficult. @p To work in Javascript, you need the discipline to limit yourself to the sane subset of the language, avoiding all the pitfalls along the way: @@ -118,7 +120,7 @@ @p - Not only do you have an expressive language with static types, you also have great tooling with IDEs like IntelliJ and Eclipse, a rich library of standard collections, and many other modern conveniences that we take for granted but are curiously missing when working in the wild west of web development: the browser! You get all of the upside of developing for the web platform, + Not only do you have an expressive language with static types, you also have great tooling with IDEs like IntelliJ and Eclipse, a rich library of standard collections, and many other modern conveniences that we take for granted but are curiously missing when working in the wild west of web development: the browser! You get all of the upside of developing for the web platform. @p While not useful for small applications, where most of the logic is gluing together external APIs, this comes in very useful in large applications where a lot of the complexity and room-for-error is entirely internal. With larger apps, you can no longer blame browser vendors for confusing APIs that make your code terrible: these confusing APIs only lurk in the peripherals around a larger, complex application. One thing you learn working in large-ish web client-side code-bases is that the bulk of the confusion and complexity is no-one's fault but your own, as a team. diff --git a/book/src/main/scalatex/book/handson/CanvasApp.scalatex b/book/src/main/scalatex/book/handson/CanvasApp.scalatex index 41be815..e68adb7 100644 --- a/book/src/main/scalatex/book/handson/CanvasApp.scalatex +++ b/book/src/main/scalatex/book/handson/CanvasApp.scalatex @@ -45,7 +45,7 @@ @img(src:="images/Dropdown.png", maxWidth:="100%") @p - Apart from mouse events, keyboard events, scroll events, input events, etc. are all usable from Scala.js as you'd expect + Apart from mouse events, keyboard events, scroll events, input events, etc. are all usable from Scala.js as you'd expect. If you have problems getting this to work, feel free to click on the link @i(cls:="fa fa-link ") icon below the code snippet to see what the full code for the example looks like @sect{Making a Clock using setInterval} @@ -71,7 +71,7 @@ @BookData.example(canvas, "Clock().main") @p - As you can see, we're using more @lnk("Canvas APIs", "https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D"), in this case dealing with rendering text on the canvas. Another thing we're using is the Javascript @lnk("Date", "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date") class, in Scala.js under the full name @hl.scala{scala.scalajs.js.Date}, here imported as @hl.scala{js.Date}. + As you can see, we're using more @lnk("Canvas APIs", "https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D"), in this case dealing with rendering text on the canvas. Another thing we're using is the Javascript @lnk("Date", "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date") class, in Scala.js under the full name @hl.scala{scala.scalajs.js.Date}, here imported as @hl.scala{js.Date}. Again, click on the link @i(cls:="fa fa-link ") icon to view the full-code if you're having trouble here. @sect{Tying it together: Flappy Box} @@ -103,7 +103,7 @@ This section of the code is peripherally necessary, but not core to the implementation or logic of Flappy Box. We see the same @hl.scala{canvas}/@hl.scala{renderer} logic we've seen in all our examples, along with some logic to make the canvas a reasonable size, and some configuration of how we will render text to the canvas. @p - In general, code like this will usually end up being necessary in a Scala.js program: the Javascript APIs that the browser provides to do things often ends up being somewhat roundabout and verbose. It's somewhat annoying to have to do for a small program such as this one, but in a larger application, the cost is both spread out over thousands of lines of code and also typically hidden away in some helpers, so you don't have to mess with this stuff unless you really want to. + In general, code like this will usually end up being necessary in a Scala.js program: the Javascript APIs that the browser provides to do things often ends up being somewhat roundabout and verbose. It's somewhat annoying to have to do for a small program such as this one, but in a larger application, the cost is both spread out over thousands of lines of code and also typically hidden away in helper functions, so the verbosity and non-idiomatic-scala-ness doesn't bother you much. @sect{Defining our State} @hl.ref("examples/demos/src/main/scala/canvasapp/FlappyLine.scala", "/*variables*/", end="def runLive") diff --git a/book/src/main/scalatex/book/handson/ClientServer.scalatex b/book/src/main/scalatex/book/handson/ClientServer.scalatex index ca8db07..0a41dae 100644 --- a/book/src/main/scalatex/book/handson/ClientServer.scalatex +++ b/book/src/main/scalatex/book/handson/ClientServer.scalatex @@ -40,7 +40,7 @@ @p We have two projects: @code{client} and @code{server}, one of which is a Scala.js project (indicated by the presence of @hl.scala{scalaJSSettings}). Both projects share a number of settings: the presence of the @code{shared/} folder, which shared code can live in (similar to what we saw in @sect.ref{Cross Publishing Libraries}) and the settings to add @lnk.github.Scalatags and @lnk.github.uPickle to the build. Note that those two dependencies use the triple @code{%%%} instead of the double @code{%%} to declare: this means that for each dependency, we will pull in the Scala-JVM or Scala.js version depending on whether it's being used in a Scala.js project. @p - The @code{client} subproject is uneventful, with a dependency on the by-now-familiar @code{scalajs-dom} library. The @code{server} project, on the other hand, is interesting: it contains the dependencies required for us to set up out Spray server, and one additioanl thing: we add the output of @code{fastOptJS} from the client to the @code{resources} on the server. This will allow the @code{server} to serve the compiled-javascript from our @code{client} project from its resources. + The @code{client} subproject is uneventful, with a dependency on the by-now-familiar @code{scalajs-dom} library. The @code{server} project, on the other hand, is interesting: it contains the dependencies required for us to set up out Spray server, and one additional thing: we add the output of @code{fastOptJS} from the client to the @code{resources} on the server. This will allow the @code{server} to serve the compiled-javascript from our @code{client} project from its resources. @p Next, let's kick off the Spray server in our Scala-JVM main method: @@ -110,15 +110,15 @@ @hl.ref("examples/crossBuilds/clientserver/client/src/main/scala/simple/Client.scala", "ajax/list", "") @p - Three times on the server and once on the client! What's worse, two of the appearances of @i{list} are in string literals, which are not checked by the compiler to match up with themselves or the name of the method @hl.scala{list}. Apart from this, there is one other piece of duplication that is unchecked: the type being returned from @hl.scala{list} (@hl.scala{Seq[FileData]} is being repeated on the client in @hl.scala{upickle.read[Seq[FileData]]} in order to de-serialize the serialized data. This leaves three wide-open opportunities for error: + Three times on the server and once on the client! What's worse, two of the appearances of @hl.scala{"list"} are in string literals, which are not checked by the compiler to match up with themselves or the name of the method @hl.scala{list}. Apart from this, there is one other piece of duplication that is unchecked: the type being returned from @hl.scala{list} (@hl.scala{Seq[FileData]}) is being repeated on the client in @hl.scala{upickle.read[Seq[FileData]]} in order to de-serialize the serialized data. This leaves three opportunities for error wide-open: @ul @li - You could change the string literals "list" and forget to change the method-name @hl.scala{list}, thus confusing future maintainers of the code. + You could change the string literals @hl.scala{"list"} and forget to change the method-name @hl.scala{list}, thus confusing future maintainers of the code. @li - You could change one of literal "list"s but forget to change the other, thus causing an error at run-time (e.g. a 404 NOT FOUND response) + You could change one of literal @hl.scala{"list"}s but forget to change the other, thus causing an error at run-time (e.g. a 404 NOT FOUND response) @li - You could update the return type of @hl.scala{list} and forget to update the deserialization call on the client, resulting in a deserialization failure at runtime. + You could update the return type of the @hl.scala{list} method and forget to update the @hl.scala{upickle.read} deserialization call on the client, resulting in a deserialization failure at runtime. @p @@ -130,11 +130,10 @@ @p @lnk("Autowire", "https://github.com/lihaoyi/autowire") is a library that turns your request routing layer from a fragile, hand-crafted mess into a solid, type-checked, boilerplate-free experience. Autowire basically turns what was previously a stringly-typed, hand-crafted Ajax call and route: - @hl.ref("examples/crossBuilds/clientserver/server/src/main/scala/simple/Server.scala", """path("ajax" / "list")""", "") @hl.ref("examples/crossBuilds/clientserver/client/src/main/scala/simple/Client.scala", "ajax/list", "") @p - Into a single, type-checked function call: + Into a safe, type-checked function call: @hl.ref("examples/crossBuilds/clientserver2/client/src/main/scala/simple/Client.scala", ".call()", "") diff --git a/book/src/main/scalatex/book/handson/CommandLine.scalatex b/book/src/main/scalatex/book/handson/CommandLine.scalatex index 29bd77f..0663378 100644 --- a/book/src/main/scalatex/book/handson/CommandLine.scalatex +++ b/book/src/main/scalatex/book/handson/CommandLine.scalatex @@ -81,7 +81,7 @@ @hl.bash > fastOptJS @p - @code{fastOptJS} is a command we've used in earlier chapters. It basically runs the Fast Optimization stage of the compilation pipeline. This results in a moderately-sized executable, which you can then load in the browser with a @hl.html{