Upload
marius-soutier
View
643
Download
6
Embed Size (px)
Citation preview
Intro to Scala.js
Marius SoutierFreelance Software Engineer
@mariussoutier
Write JavaScript in Scala
What is Scala.js?Scala.js compiles Scala to JavaScript
object HelloUg extends JSApp { override def main(): Unit = { // FP for the win var res, i = 0 val values = js.Array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) while (i < values.length) { res = res + values(i) i += 1 } println(res) }}
$c_Lug_HelloUg$.prototype.main__V = (function() { var res = 0; var i = 0; var values = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; while ((i < $uI(values["length"]))) { res = ((res + $uI(values[i])) | 0); i = ((1 + i) | 0) }; var x = res; var this$2 = $m_s_Console$(); var this$3 = this$2.outVar$2; $as_Ljava_io_PrintStream(this$3.tl$1.get__O()).println__O__V(x) });
$e["ug"] = ($e["ug"] || {}); $e["ug"]["HelloUg"] = $m_Lug_HelloUg$;
But Why?
• Web development is moving to client-side apps,JavaScript is the browser’s only language
• JavaScript is beyond broken
• Maintaining a large JavaScript project can be difficult
• Keeping up with JavaScript’s ecosystem is nigh-impossible(See JavaScript Drinking Game)
Obligatory JavaScript: The Good Parts Reference
But that’s enough JavaScript bashing for today
Scala.js to the Rescue• We all love Scala
• Type-safety, packages, traits, …
• Rich standard library
• Share code between backend and front-end
• Full editor support (same file suffix)
• Access to all JS libs and a lot of the Scala ecosystem
Scala.js’ Future
• Size of compiled JavaScript
• Compile Speed
• JS libraries must be wrapped
• Scala is developed with JVM in mind, some things just don't work in JS land, some JDK parts missing
• Front-end developers might not want to learn Scala (slackers!)
Drawbacks
• Scala libraries available for Scala.js
• Scalatags, ScalaCSS
• Shapeless, Scalaz, Cats, Monocle
• Sharing code inside a project via common cross-compiled sbt sub-project
• Data exchange via JSON
• upickle
• JavaScript doesn't necessarily understand your JSON, e.g. Longs aren't that long in JavaScript
Sharing Code & Data
How Does It Work?
.scala
.class
.sjsir
run/test
~fastOptJS
fullOptJS Google Closure -opt.js
-fastopt.js
Rhino / Node.js
Rhino / PhantomJS
No DOM
DOM
-jsdeps.min.js
jsDependencies ++= Seq( "org.webjars" % "jquery" % "1.10.2" / "jquery.js", "org.webjars" % "angularjs" % "1.4.8" / "angular.js" dependsOn "jquery.js")
Targeting JavaScriptScala / JVM JavaScript
Byte, Short, Int, Float, Double Number
Unit Undefined
Char, Long Scala classes
Custom Scala classes JavaScript class with @JSExport
NullPointerException, ArrayIndexOutOfBoundsException,
ClassCastException, StackOverflowError, …Undefined
Reflection -
String.split JS RegEx is different
Pattern Matching on Byte, Short, Int, Float, Double Determined by runtime value, not type
JavaScript Native TypesJS-native Type Maps to Example JS
js.FunctionN scala.FunctioN val fn: js.Function1[Int, Int] = (i: Int) => i * 2
var fn = function(i) { return i * 2; };
js.Array[T] Seq[T] js.Array(1, 2, 3) [1, 2, 3]
js.Dictionary[T] mutable.Map[String, T] js.Dictionary("a" -> 1, "b" -> 2) {"a": 1, "b": 2}
js.UndefOr[T] Option[T]val some: js.UndefOr[Int] = 1 val none: js.UndefOr[Int] =
js.undefinedOption(1).orUndefined
1 undefined
1
js.TupleN TupleN js.Tuple2(42, “Scala UG") [42, "Scala UG"]
Dynamic JSWhen interacting with JavaScript libraries, its dynamic nature can’t always be ignored
val guest = js.Dynamic.literal( name = "Scala User Group")
But we can give it a nicer interfacetrait Guest extends js.Object { val name: String = js.native} object Guest { def apply(name: String): Guest = js.Dynamic.literal(name = name).asInstanceOf[Guest] }
import js.Dynamic.{global => g} g.console.log(guest) val dom = g.documentval p = dom.createElement("p") p.innerHTML = s"Hi ${guest.name}!"dom.getElementById("main").appendChild(p)
Wrapping JS Libs (1)Read documentation and infer types
Wrapping JS Libs (2)
import scala.scalajs.jsimport scala.scalajs.js.annotation.JSNameobject Numeral { def apply(n: Double): Numeral = new Numeral(n) def apply(): Numeral = new Numeral(0) } @JSName("numeral") @js.nativeclass Numeral(n: Double) extends js.Object { def format(formatString: String): String = js.native def unformat(formatString: String): Double = js.native def add(number: Double): Double = js.native def subtract(number: Double): Double = js.native def multiply(number: Double): Double = js.native def divide(number: Double): Double = js.native def value(): Double = js.native def difference(number: Double): Double = js.native}
jsDependencies ++= Seq( "org.webjars" % "numeral-js" % "1.5.3-1" / "numeral.js" minified "min/numeral.min.js")
Numeral(totalValue).format("0,0.00")
Manipulating the DOMlibraryDependencies ++= Seq( "org.scala-js" %%% "scalajs-dom" % "0.8.2")
import org.scalajs.dom._
val main = document.getElementById("main") val p = document.createElement("p") val text = document.createTextNode("Hi Scala User Group!") p.appendChild(text) main.appendChild(p)
AJAXimport org.scalajs.dom.ext.Ajax import scala.scalajs.concurrent.JSExecutionContext.Implicits.queue val eventualUsers = Ajax .get(url = "/users/") .map { xmlHttp => xmlHttp.status match { case 200 => import upickle.default._ read[Seq[User]](xmlHttp.responseText) case other => Seq.empty[User] } }
• Export classes to JS for use from other JS code
• Can even publish as NPM module
Using Scala.js from JS
@JSExportcase class ExportedUser( @(JSExport@field) name: String, @(JSExport@field) email: String )
@JSExport@ScalaJSDefinedclass Foo extends js.Object { val x: Int = 4 def bar(x: Int): Int = x + 1 }
Questions?