From first principles: Why I bet on Scala.js
lihaoyi.com
lihaoyi.com
The OP has contributed so much to the Scala community; somehow he's a Python + Coffeescript developer by day, and an absurdly prolific Scala library author in the evenings.
As for Scala.js, representing browser interactions in types is such a relief, and to have the entire Scala language available is almost too good to be true: client-server interop is seamless.
Really the only negative for me is the Scala collections library. Of the @100KB Hello World[1] generated blob half of that is Scala collections. There's a small initial "tax" to pay basically, and thereafter size increase is on par with normal javascript.
[1] not totally true, vanilla Hello World is more like 40KB, but once you define a Scala collection then generated blob doubles in size.
The compiler is compiled into JS and native code.
It generates highly readable JS code and easier FFI.
It compiles super fast (generally 10~100 faster than Scala) and generates optimized code.
So, in the terms of this article, Ghcjs might be around the same place Scala.js was 2 years ago. In the mean time, Scala.js has taken off in a lot of ways. It's yet to be seen what will happen with Ghcjs.
A strict language can just skip all of that so long as it's willing to live within the limitations of the Javascript model. Scala.js does that for the most part and ends up with both nice FFI and a minimal runtime.
I could give a recap of Haskell development that's happened while Scala.js has been developed to debunk this silly accusation, but it wouldn't change your mind.
I think it's mostly down to determination. Some things are hard, but need to be done.
Haskell people just seem to give up more easily - see all the half-working projects. Nobody finishes stuff, the next dev just starts his own new project, and abandons it later.
Scala.js handily beats JavaScript in terms of IDE support and tooling
The Haskell community has achieved great things even though most contributions are done in people's spare time. That is determination.
There is no determination in failing to get anything done and doing NIH for a decade.
The last I heard, the primary dev of ghcjs intended to build a new codegen which would alleviate some of the issues here, but that hasn't happened yet to my knowledge.
The comment about imperfect semantics arises from a few weirdnesses around JS interaction with laziness. Perhaps the biggest one I remember is that when calling Haskell from JS you have to be prepared for the idea that results may not be fully evaluated. There wasn't any real mechanism to force and block on the Javascript side and certainly no mechanism to handle it asynchronously (promises wouldn't work unless they decorated inner layers of a data types which would be terrible).
This also plays out on the Haskell side in terms of managing retention strategies.
In a strict language both of these obviously just vanish.
[1] http://www.navision-blog.de/blog/2016/08/06/fable-react-nati...
Compiling _entire Scala_ to JavaScript? Without runtime?! To the point of compatibility that Scalaz can be compiled, with all its horrors?!! Wow.
On the other hand Dotty has a lot of very, very concrete benefits I would like. 2x faster compile times for my Scala code sure would be nice...
While I consider myself "pretty experienced" at doing web-stuff, and thus knowing the pain-points and constraints and prior work, I have no clue at all what the world of C/C++ is like. Scala-Native is in that world, so someone who has a Scala + C/C++ background would have much better instincts than I would
As a language, it seems pleasant: somewhere around C#, a mix of JS and Java, less "powerful" (or less "crazy" depending on how you look at it) than Scala. I think I personally prefer Scala, but wouldn't complain too loudly if I had to use Dart/Dart instead of Javascript/PHP.
On the other hand, even with Google's backing they seem to not have managed to aggregate as much of an ecosystem in the 4 years they've been available. In particular, server-side-Dart hasn't really taken off, and client-side dart doesn't seem to be that popular compared to say Coffeescript or Typescript or even Clojurescript.
I think the advantage of Scala.js over dart is that we only need to build the JS side of things: there's already a pretty large server-side Scala ecosystem. There's already all the tooling and IDEs set up. There's already the community of people who already know Scala, and do not need to re-learn anything.
Thus even though the Dart-JS ecosystem is probably not that much smaller than the Scala-JS ecosystem, I think Scala-JS is a lot more viable and sustainable (since it can piggy-back on all the Scala-JVM stuff) while Dart-JS and many other new-language communities have a tough struggle to gain enough critical-mass to be self-sustaining
In your live coding presentation, you mentioned that the output from compiling scala to js is several files.
I'm interested in generating one file that exposed JavaScript functions that can be easily called from a js runtime.
In other words, I'd like to write a JavaScript library in Scala - not a full-blown application.
What are some good resources showing how to do this?
Everything function marked with @JSExport will be exposed to Javascript. There's no concrete difference between an application with one `@JSExport def main` method that you call to initialize it, and a library with multiple `@JSExport`ed methods. It all works the same way: the `@JSExport`ed functions are exposed, and people can call them.
This repo shows how to make a NPM library using Scala.js
- https://github.com/rockymadden/scala-node/
If you want to learn how to use `@JSExport` properly, the [core documentation](http://www.scala-js.org/tutorial/basic/) and [Hands-on Scala.js](http://www.lihaoyi.com/hands-on-scala-js/) are great.
Even when it's targeted at the JVM. Scala.JS usage isn't that hard after you read half of the book.
In Java, NullPointerExceptions is huge problem. IDEs have plugins to check for this error, but there is no standard solution.
Does Scala help in avoiding NullPointerExceptions? Do function signatures specify if a value may be null?
var node = element.firstChild;
while (node) {
// do something
node = node.nextSibling;
}
Perhaps those interfaces use the Option class? def printNodes(targetNode: dom.Node): Unit = {
var c = targetNode.firstChild
while (c != null) {
if (c.nodeValue != null) {
println(c.nodeValue)
}
c = c.nextSibling
}
}
The return value from firstChild and nextSibling can be null.The underlying notion of Scala to X ie Scala Native has lots of potential but Scalajs is more of a segue.
If I was starting something now I'd be tempted to use TypeScript if I wanted one language on the front-end and the back-end.
Also, the design restrictions to ban runtime reflection in GWT are a codesize decision. Allowing reflective calls basically inhibits optimization and forces the retention of tons of metadata.
The Deferred Binding mechanism is essentially dependency injection before DI and annotation processing existed, because GWT hails from 2007. We now recommend using Dagger2 for DI, and Annotation Processors for code generators, so that the system of compile time reflective calls is all part of the standard tool chain.
In the era of mobile browsers, I think 100k is a bridge too far for a hello world, especially in the developing world, so restrictions to the JRE runtime which allow much greater code pruning are a necessary evil. It hasn't really prevented an enormous amount of code sharing.
For example, Google Inbox shares 70% of its Java code between Web, Android, and iOS (via J2Objc)
1. It's based on the Java platform.
2. It's excessive use of operator overloading.
3. Mad implicit conversions all over the place.
(yes, I exaggrated a bit ;))
2. Have you seen languages like APL or J? Now that's operator overloading. '+' is just a symbol, meaning a lot of different things even in mathematics. All meaning is a product of convention.
3. In modern Scala, implicit _conversions_ are discouraged unless you know what are you doing. Besides, Scala IDEs are excellent in showing applied implicits anywhere in the code (you do use an IDE, right?)
As always: cost/benefit! There is a price for the "context switch" out of one set of conventions into another. Sometimes, the benefit far outweighs the cost. For example, there are certain environments where you can jump out of the programming language and simply write a method in SQL. Not embed SQL in code in a string, you can simply write prepared statements as a method. For certain applications, this can be beneficial.
The problem I have with operator overloading, is that it allows the average programmer to switch conventions using some of the the most commonly used conventions with barely a sign that something special is going on. It's too easy to use. It's too easy to miss. It's too easy to get a moment of confusion from it when debugging. (What, I'm in a method? Oh, operator overloading!) And it doesn't produce a great benefit all that often. Yet it has a "nifty" factor that encourages in experienced programmers to make use of it -- precisely the people who probably shouldn't be making that decision.
1. Java platform is overengineered, everytime I had to set up stuff like Maven, I missed the easy JS days.
2. True story, I can infer what a '+' between two strings means, but what about all the non-intuitive operators? (List concats etc. pp.)
3. Not a big fan of IDEs. More of an editor dev, but good to know ICs are now discuraged. I got myself "Programming in Scala" back in the days and it seemed to me, that the Scala makers thought of IC as Scalas killer feature :D
It would be nice to be able to run an integrated debugger (as opposed to a port connection from an SBT spawned JVM), integrate directly with SBT (as opposed to having to run it side by side), provide a full featured REPL (not the neutered version with no completion and poor editor integration), as well as functional import organization, dead code identification, refactoring suggestions, real linting, real code formatting, working extract refactorings, etc..
Overloading at least some could be useful. Java 10+ will probably allow something like this, too. You ever worked with BigDecimal?
In Python, Scala where + (__add__) is overloadable you do.
BigDecimal(1) + BigDecimal(2) + BigDecimal(3)
On Java: (new BigDecimal(1).append(new BigDecimal(2)).append(new BigDecimal(3)
I think you get the point.. Enumeratee.take(4) &>> Iteratee.fold[Byte, Int](0){
case (acc, b) => acc * 256 + (b & 0xFF)
}
It's a community thing, and much like Java suffered from excessive XML and patterns in the 200Xs, but is much better now, Scala has suffered from excessive operator noise, but it's getting better. >>= "fffff...Pwip!" (the sound of monadic bind)
|==> "TCHvvvvvv..." (lightsaber igniting)
|@| "AAAAAAHHHH!" (Edvard Munch 1893 operator)p.s. I banned scalaz and shapeless on my company so there aren't too much operator noises.
Just wondering, do you have your own libraries that use some of the good stuff? I can't imagine living in a world where I'm banned from using...
* Traverse (do you write specialized versions for the cross product of [List, Vector, etc] x [Option, Either, etc] ?)
* Validation (how do you collect errors? mutable.Buffer?)
* NonEmptyList (I've written my own NonEmptyVector that wasn't that hard to write. Do you do that or just assume nonempty and it works out for you?)
* Unapply (maybe this doesn't apply to you because you never use HKT-generic typeclasses?)
I'm genuinely curious as all of these tools get used by me and my coworkers literally every day and without them we'd be writing boilerplate or making unchecked assumptions by the seat of our pants.
* Validation? Actually we are on HTTP so mostly a sealed trait structure is enough for us we barly use `Seq(ValidationError)` from Playframework (only class constructs that manually)
* NonEmptyList - I never came across a palce where I needed NonEmptyList?!
* never needed.
Just keep in mind other languages don't have such abstractions as well (maybe haskell has, but I don't know haskell). Some people need these high level abstractions and it's fine if you use it but most things are just syntactic sugar.
The only stupid thing is something like that:
- Future[Option[A1]] - Future[Option[A2]] ... - Future[Option[AN]]
and now I need a `case Some((a1, a2)) =>` without some magic you write a lot of boilerplate.
So a Monad is really something that would be great and probably we will evaluate if we get to use cats.
Edit: Code Snippet:
val h = (for {
ph <- OptionT(dbService.byId(id))
article <- OptionT(articleDao.by0A(ph.article))
} yield (ph, article)).map{ case (ph, article) =>
(ph.id, article.id)
}
h.value
yes this is something why we consider cats.Do you ever check the length of a list before applying a computation or dispatch based on a result list?
I guess we have something like 30 occurences of .isEmpty/.nonEmpty in one of our codebases
I came back to the JVM many times in the past and every time it was the same. It always seemed like things changed, but the baseline complexity stayed the same.
I had chosen Purescript over Elm, since I have read about Elm being constricting once you move outside of most of the examples use cases.
The real reason is that this 24-page heavily-researched blog post is already too damn long lol
> If you look at other compile-to-JS languages like:
> - Google Web Toolkit which lets you compile Java to Javascript, > - The Opal Ruby Compiler for Ruby > - Brython, PyJS, Skulpt, Transcrypt or RapydScript for Python > - Various flavors of C#-to-Javascript (Salterelle, Bridge.Net, JSIL) > - Or Haskell-to-Javascript compilers (Haste, Faye) > You'll notice one thing in common: none of them have a standard way of writing code that runs on both their "original" runtime (JVM, MRI, CPython, CLI, ...) and on Javascript. And the reason why is straightforward: none of them are compatible enough, complete enough to really let you write "full" Java/Ruby/Python/C#/etc..
ClojureScript is mentioned in the article, but only for this specious comparison:
> Clunky Interop > Many other languages are not as fortunate. For example, here's how you create an element in ClojureScript, compared with Javascript
(let [paragraph (.createElement js/document "p")] (set! (. paragraph -innerHTML) "<b>Bold!</b>"))
var paragraph = document.createElement("p") paragraph.innerHTML = "<strong>It works!</strong>"
> Now, the conversion between these two snippets is mechanical, so it's not something you need a PhD to perform. Nevertheless, it is clear that using Javascript APIs in ClojureScript, while looking like ClojureScript, looks almost nothing like the Javascript it represents.
> This mapping is something that everyone who wishes to learn Clojurescript will have to internalize.
This seems misinformed at best - both Clojure and ClojureScript have interop as a first-class concern, and interop forms are exactly the same between them.
I appreciate the work that Li's done, but I'm disappointed by what seems like a rather disingenuous omission of the only other language which fulfills Li's criteria.
Interop, IDE support (IntelliJ), parent language compatibility, static optimization (judging by the payload size numbers, ClojureScript utilizes dead-code elimination more effectively than Scala.js), performance (ClojureScript immutable vectors are faster to build than JavaScript arrays, far from the 1-2x Scala.js slowdown cited in the article!) - there is (and has been - since 2011) a compile-to-JavaScript language that is in the same league as Scala.js, and arguably eclipses it on many dimensions.
A language-agnostic comparison can't reasonably leave out ClojureScript.
- "Perfect IDE support" when described hinges on types
- "Static Optimizability" specifically mentions enjoying
type-driven AOT optimizations
- "Solving Real Problems" mentions types in the
"Refactoring is Painful", "'Non-trivial' abstractions",
and, briefly, in "Scala.js Solves Real Problems"
Furthermore, all of OpalRuby, Python, and CoffeeScript were explicitly dinged on their failures to have statically analyzed types.But yeah, it does feel like he didn't give ClojureScript as much examination.
Here's an example of a react component from the re-agent tutorial (http://reagent-project.github.io/), e.g., but you'd build straight html in the same-ish way using a library:
(defn simple-component []
[:div
[:p "I am a component!"]
[:p.someclass
"I have " [:strong "bold"]
[:span {:style {:color "red"}} " and red "] "text."]])You could create a nice wrapper-library around anything in any-language, really, but that misses the entire point of that example: to show how interoperability is when you haven't wrapped a nice wrapper-library around everything.
I know you can use asm.js but it looks like they're not using asm.js
>It gets compiled to vanilla JS
thanks