summaryrefslogtreecommitdiff
path: root/book/src/main/scalatex/book/indepth/SemanticDifferences.scalatex
blob: c87c60f371fc10d7b8ea9a8289f47bc66c9f1b5f (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
@import BookData._
@p
  Although Scala.js tries very hard to maintain compatibility with Scala-JVM, there are some parts where the two platforms differs. This can be roughly grouped into two things: differences in the libraries available, and differences in the language itself. This chapter will cover both of these facets.

@sect{Language Differences}

  @sect{Primitive data types}
    @p
      All primitive data types work exactly as on the JVM, with the three following
      exceptions.

    @sect{Floats can behave as Doubles by default}
      @p
        Scala.js underspecifies the behavior of @code{Float}s by default. Any @code{Float} value can be stored as a @code{Double} instead, and any operation on @code{Float}s can be computed with double precision. The choice of whether or not to behave as such, when and where, is left to the
        implementation.
      @p
        If exact single precision operations are important to your application, you can enable strict-floats semantics in Scala.js, with the following sbt setting:
      @hl.scala
        scalaJSSemantics ~= { _.withStrictFloats(true) }
      @p
        Note that this can have a major impact on performance of your application on JS interpreters that do not support @lnk("the Math.fround function", "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/fround").

    @sect{toString of Float, Double and Unit}
      @p
        @code{x.toString()} returns slightly different results for floating point numbers and @code{()} (@code{Unit}).

      @split
        @half
          @hl.scala
            // Scala-JVM
            > println(())
            ()
            > println(1.0)
            1.0
            > println(1.4f)
            1.4

        @half
          @hl.scala
            // Scala.js
            > println(())
            undefined
            > println(1.0)
            1
            > println(1.4f)
            1.399999976158142

      @p
        In general, a trailing @code{.0} is omitted. Floats print in a weird way because they are printed as if they were Doubles, which means their lack of precision shows up.
      @p
        To get sensible and portable string representation of floating point numbers, use @code{String.format()} or related methods.

    @sect{Runtime type tests are based on values}
      @p
        Instance tests (and consequently pattern matching) on any of @code{Byte}, @code{Short}, @code{Int}, @code{Float}, @code{Double} are based on the value and not the type they were created with. The following are examples:
      @ul
        @li
          1 matches @code{Byte}, @code{Short}, @code{Int}, @code{Float}, @code{Double}
        @li
          128 (@code{> Byte.MaxValue}) matches @code{Short}, @code{Int}, @code{Float}, @code{Double}
        @li
          32768 (@code{> Short.MaxValue}) matches @code{Int}, @code{Float}, @code{Double}
        @li
          2147483647 matches @code{Int}, @code{Double} if strict-floats are enabled, otherwise @code{Float} as well
        @li
          2147483648 (@code{> Int.MaxValue}) matches @code{Float}, @code{Double}
        @li
          1.5 matches @code{Float}, @code{Double}
        @li
          1.4 matches @code{Double} only if strict-floats are enabled, otherwise @code{Float} and @code{Double}
        @li
          @code{NaN}, @code{Infinity}, @code{-Infinity} and @code{-0.0} match @code{Float}, @code{Double}
      @p
        As a consequence, the following apparent subtyping relationships hold:
      @hl.scala
        Byte <:< Short <:<  Int  <:< Double
                       <:< Float <:<
      @p
        if strict-floats are enabled, or
      @hl.scala
        Byte <:< Short <:< Int <:< Float =:= Double
      @p
        otherwise.

  @sect{Undefined behaviors}
    @p
      The JVM is a very well specified environment, which even specifies how some bugs are reported as exceptions. Some examples are:
    @ul
      @li
        @code{NullPointerException}
      @li
        @code{ArrayIndexOutOfBoundsException} and @code{StringIndexOutOfBoundsException}
      @li
        @code{ClassCastException}
      @li
        @code{ArithmeticException} (such as integer division by 0)
      @li
        @code{StackOverflowError} and other @code{VirtualMachineError}s
    @p
      Because Scala.js does not receive VM support to detect such erroneous conditions, checking them is typically too expensive.
    @p
      Therefore, all of these are considered @lnk("undefined behavior", "http://en.wikipedia.org/wiki/Undefined_behavior").
    @p
      Some of these, however, can be configured to be compliant with sbt settings. Currently, only @code{ClassCastException}s (thrown by invalid @code{asInstanceOf} calls) are configurable, but the list will probably expand in future versions.
    @p
      Every configurable undefined behavior has 3 possible modes:
    @ul
      @li
        @b{Compliant}: behaves as specified on a JVM
      @li
        @b{Unchecked}: completely unchecked and undefined
      @li
        @b{Fatal}: checked, but throws @lnk("UndefinedBehaviorError", "http://www.scala-js.org/api/scalajs-library/0.6.0/#scala.scalajs.runtime.UndefinedBehaviorError")s instead of the specified exception.
    @p
      By default, undefined behaviors are in Fatal mode for fastOptJS and in Unchecked mode for fullOptJS. This is so that bugs can be detected more easily during development, with predictable exceptions and stack traces. In production code (fullOptJS), the checks are removed for maximum efficiency.
    @p
      @code{UndefinedBehaviorError}s are @i{fatal} in the sense that they are not matched by @code{case NonFatal(e)} handlers. This makes sure that they always crash your program as early as possible, so that you can detect and fix the bug. It is @i{never} OK to catch an @code{UndefinedBehaviorError} (other than in a testing framework), since that means your program will behave differently in fullOpt stage than in fastOpt.
    @p
      If you need a particular kind of exception to be thrown in compliance with the JVM semantics, you can do so with an sbt setting. For example, this setting enables compliant @code{asInstanceOf}s:
    @hl.scala
      scalaJSSemantics ~= { _.withAsInstanceOfs(
        org.scalajs.core.tools.sem.CheckedBehavior.Compliant) }
    @p
      Note that this will have (potentially major) performance impacts.
    @p
      For a more detailed rationale, see the section @sect.ref{Why does error behavior differ?}.

  @sect{Reflection}
    @p
      Java reflection and, a fortiori, Scala reflection, are not supported. There is limited support for @code{java.lang.Class}, e.g., @code{obj.getClass.getName} will work for any Scala.js object (not for objects that come from JavaScript interop). Reflection makes it difficult to perform the optimizations that Scala.js heavily relies on. For a more detailed discussion on this topic, take a look at the section @sect.ref{Why No Reflection?}.

  @sect{Regular expressions}
    @p
      @lnk("JavaScript regular expressions", "http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Guide:Regular_Expressions") are slightly different from @lnk("Java regular expressions", "http://docs.oracle.com/javase/6/docs/api/java/util/regex/Pattern.html"). The support for regular expressions in Scala.js is implemented on top of JavaScript regexes.
    @p
      This sometimes has an impact on functions in the Scala library that use regular expressions themselves. A list of known functions that are
      affected is given here:
    @ul
      @li
        @code{StringLike.split(x: Array[Char])}

  @sect{Symbols}
    @p
      @code{scala.Symbol} is supported, but is a potential source of memory leaks in applications that make heavy use of symbols. The main reason is that
      JavaScript does not support weak references, causing all symbols created by Scala.js to remain in memory throughout the lifetime of the application.

  @sect{Enumerations}
    @p
      The methods @code{Value()} and @code{Value(i: Int)} on @code{scala.Enumeration} use reflection to retrieve a string representation of the member name and are therefore -- in principle -- unsupported. However, since Enumerations are an integral part of the Scala library, Scala.js adds limited support for these two methods:
    @p
      Calls to either of these two methods of the forms:
    @hl.scala
      val <ident> = Value
      val <ident> = Value(<num>)
    @p
      are statically rewritten to (a slightly more complicated version of):
    @hl.scala
      val <ident> = Value("<ident>")
      val <ident> = Value(<num>, "<ident>")
    @p
      Note that this also includes calls like
    @hl.scala
      val A, B, C, D = Value
    @p
      since they are desugared into separate @code{val} definitions.
    @p
      Calls to either of these two methods which could not be rewritten, or calls to constructors of the protected <code>Val</code> class without an explicit name as parameter, will issue a warning.
    @p
      Note that the name rewriting honors the @code{nextName} iterator. Therefore, the full rewrite is:
    @hl.scala
      val <ident> = Value(
        if (nextName != null && nextName.hasNext)
          nextName.next()
        else
          "<ident>"
      )
    @p
      We believe that this covers most use cases of @code{scala.Enumeration}. Please let us know if another (generalized) rewrite would make your life easier.

@sect{Library Differences}
  @val myTable = Seq(
    ("Most of java.lang.*", "java.lang.Thread, java.lang.Runtime, ..."),
    ("Almost all of scala.*", "scala.collection.parallel, scala.tools.nsc"),
    ("Some of java.util.*", "org.omg.CORBA, sun.misc.*"),
    ("Macros: uPickle, Scala-Async, Scalaxy, etc", "Reflection: Scala-Pickling, Scala-Reflect"),
    ("Shapeless, Scalaz, Scalatags, uTest", "Scalatest, Scalate"),
    ("XMLHttpRequest, Websockets. Localstorage", "Netty, Akka, Spray, File IO, JNI"),
    ("HTML DOM, Canvas, WebGL", "AWT, Swing, SWT, OpenGL"),
    ("Chipmunk.js, Hand.js, React.js, jQuery", "Guice, JUnit, Apache-Commons, log4j"),
    ("IntelliJ, Eclipse, SBT, Chrome console, Firebug", "Scala REPL, Yourkit, VisualVM, JProfiler")
  )

  @p
    Scala.js differs from Scala-JVM not just in the corner-cases of the language, but also in the libraries available. Scala-JVM has access to JVM APIs and the wealth of the Java libraries, while Scala.js has access to Javascript APIs and Javascript libraries. It's also possible to write pure-Scala libraries that run on both Scala.js and Scala-JVM, as detailed @a("here").
  @p
    This table gives a quick overview of the sorts of libraries you can and can't use when working on Scala.js:

  @val tableHead = pureTable(th("Can Use"), th("Can't Use"))

  @tableHead
    @for(tuple <- myTable)
      @tr
        @td{@tuple._1}@td{@tuple._2}

  @p
    We'll go into each section bit by bit

  @sect{Standard Library}
    @tableHead
      @for(tuple <- myTable.slice(0, 3))
        @tr
          @td{@tuple._1}@td{@tuple._2}

    @p
      You can use more-or-less the whole Scala standard library in Scala.js, sans some more esoteric components like the parallel collections or the tools. Furthermore, we've ported some subset of the Java standard library that many common Scala libraries depends on, including most of @hl.scala{java.lang.*} and some of @hl.scala{java.util.*}.
    @p
      There isn't a full list of standard library library APIs which are available from Scala.js, but it should be enough to give you a rough idea of what is supported. The full list of classes that have been ported to Scala.js is available under @sect.ref{Available Java APIs}

  @sect{Macros v.s. Reflection}
    @tableHead
      @for(tuple <- myTable.slice(3, 4))
        @tr
          @td{@tuple._1}@td{@tuple._2}

    @p
      As described @sect.ref("Why No Reflection?", "here"), Reflection is not supported in Scala.js, due to the way it inhibits optimization. This doesn't just mean you can't use reflection yourself: many third-party libraries also use reflection, and you won't be able to use them either.

    @p
      On the other hand, Scala.js does support Macros, and macros can in many ways substitute many of the use cases that people have traditionally used reflection for (see @sect.ref("Macros", "here")). For example, instead of using a reflection-based serialization library like @lnk.github.scalaPickling, you can use a macro-based library such as @lnk.github.uPickle.

  @sect{Pure-Scala v.s. Java Libraries}
    @tableHead
      @for(tuple <- myTable.slice(4, 5))
        @tr
          @td{@tuple._1}@td{@tuple._2}
    @p
      Scala.js has access to any pure-Scala libraries that you have cross-compiled to Scala.js, and cross-compiling a pure-Scala library with no dependencies is straightforward. Many of them, such as the ones listed above, have already been cross-compiled and can be used via their maven coordinates.
    @p
      You cannot use any libraries which have a Java dependency. This means libraries like @lnk.misc.ScalaTest or @lnk.misc.Scalate, which depend on a number of external Java libraries or source files, cannot be used from Scala.js. You can only use libraries which have no dependency on Java libraries or sources.

  @sect{Javascript APIs v.s. JVM APIs}
    @tableHead
      @for(tuple <- myTable.slice(5, 7))
        @tr
          @td{@tuple._1}@td{@tuple._2}

    @p
      Apart from depending on Java sources, the other thing that you can't use in Scala.js are JVM-specific APIs. This means that anything which goes down to the underlying operating system, filesystem, GUI or network are unavailable in Scala.js. This makes sense when you consider that these capabilities are no provided by the browser which Scala.js runs in, and it's impossible to re-implement them ourselves.
    @p
      In exchange for this, Scala.js provides you access to Browser APIs that do related things. Although you can't set up a HTTP server to take in-bound requests, you can make out-bound requests using @lnk.dom.XMLHttpRequest to other servers. You can't write to the filesystem or databases directly, but you can write to the @hl.scala{dom.localStorage} provided by the browser. You can't use Swing or AWT or WebGL but instead work with the DOM and Canvas and WebGL.
    @p
      Naturally, none of these are an exact replacement, as the browser environment is fundamentally different from that of a desktop application running on the JVM. Nonetheless, there are many analogues, and if so desired you can write code to abstract away these differences and run on both Scala.js and Scala-JVM


  @sect{Scala/Browser tooling v.s. Java tooling}
    @tableHead
      @for(tuple <- myTable.slice(7, 8))
        @tr
          @td{@tuple._1}@td{@tuple._2}


    @p
      Lastly, there is the matter of tools. Naturally, all the Scala tools which depend on the JVM are out. This means things like the @lnk("Yourkit", "http://www.yourkit.com/"), @lnk("VisualVM", "http://visualvm.java.net/") and @lnk("JProfiler", "https://www.ej-technologies.com/products/jprofiler/overview.html") profilers, as well as things like the Scala command-line REPL which relies on classloaders and other such things to run on the JVM
    @p
      On the other hand, you do get to keep and continue using many tools which are build for Scala but JVM-agnostic. For example, IDEs such a @lnk.misc.IntelliJ and @lnk.misc.Eclipse work great with Scala.js; from their point of view, it's just Scala, and things like code-navigation, refactoring and error-highlighting all work out of the box. SBT works with Scala.js too, and you see the same compile-erorrs in the command-line as you would in vanilla Scala, and even things like incremental compilation work un-changed.
    @p
      Lastly, you gain access to browser tools that don't work with normal Scala: you can use the Chrome or Firefox consoles to poke at your Scala.js application from the command line, or their profilers/debuggers. With source maps set up, you can even step-through debug your Scala.js application directly in Chrome.