1

Hey, all. I want to figure out how to use Scala.js tools to compile Scala expressions to Javascript during runtime. Here's a simplified setup, as an example.

Say, we have a simple DSL that consists of Ctx => Boolean functions and boolean operations on them, a la the following:

implicit class Simple[Ctx](f: Ctx => Boolean) {
  def &&(g: Ctx => Boolean): Ctx => Boolean = ctx => f(ctx) && g(ctx)
  def ||(g: Ctx => Boolean): Ctx => Boolean = ctx => f(ctx) || g(ctx)
  def unary_!: Ctx => Boolean = ctx => !f(ctx)
}

And let's assume that we have some "building blocks" hardcoded, compiled into Javascript, and exported, as follows:

@ExportJSTopLevel("foo") def foo[Ctx](ctx: Ctx): Boolean = ???
@ExportJSTopLevel("bar") def bar[Ctx](ctx: Ctx): Boolean = ???
// and so on

Now one can assemble simple boolean expressions out of these building blocks, such as: foo && bar or foo || !bar, etc.

Let's say that some persistent entities are created at runtime, with such expressions as their bodies. I want to be able to compile them to Javascript, as functions with the same signature as the building blocks above, which call those building blocks.

I found several references online to the mysterious class called ScalaJSOptimizer somewhere in Scala.js tools. However, the links provided to that class are always broken or show it belonging to a package that the latest version of the "scalajs-tools" artifact doesn't even have.

What is the best way to accomplish what I want to do?

silverberry
  • 786
  • 5
  • 20
  • I don't understand half of what you're trying to achieve, but the replacement for the (long dead) `ScalaJSOptimizer` is [`StandardLinker`](https://www.scala-js.org/api/scalajs-tools/0.6.19/#org.scalajs.core.tools.linker.StandardLinker$), which is factory for [`Linker`](https://www.scala-js.org/api/scalajs-tools/0.6.19/#org.scalajs.core.tools.linker.Linker), whose important method is `link`. – sjrd Aug 08 '17 at 16:31
  • What you are looking for is called (at least in the Scala research context) a "deep embedding" (or a variant thereof). This is an active topic of research and not something "solved". The best I can think of for your specific problem is to write a little JavaScript compiler yourself and `eval` the result. – gzm0 Aug 10 '17 at 11:31

3 Answers3

2

I also do not know what your end-goal is, but if you really-truly want to compile Scala.js at runtime your best bet is to look at the source code for scalafiddle.io, which does exactly that, and start by copying whatever it is doing, and changing it from there.

Here is the code that deals with the compilation from Javascript to Scala.js's IR, and then from IR to a Javascript String:

The interesting methods here are compile, link (fastOptJS and fullOptJS), and export. I know StackOverflow discourages external links, but the code is really too big and gnarly to be worth reproducing in-line here. Your best bet is to clone down that repo, open it up in your IDE and jump around seeing how the code transforms the Scala.js code: String coming in the constructor of that Compiler class, through all the intermediate steps, to the final Javascript String being returned from the export method

Li Haoyi
  • 15,330
  • 17
  • 80
  • 137
  • But that thing is still JVM only no? Or does it hack scalac so badly so that it runs on JS? – gzm0 Aug 14 '17 at 16:17
  • Yeah it's still JVM. It wasn't clear to me from the original question whether "Runtime" meant on the server, or on the client. This only applies on the server – Li Haoyi Aug 15 '17 at 04:27
1

What you are trying to do is possible and has been researched extensively in many languages (deep DSL embedding). To achieve what you want you would have to:

  1. Find or build a deep embedding framework in Scala that can output JavaScript.
  2. Make sure that framework compiles with Scala.js.
  3. Build your DSL using this framework.

The frameworks I know that you could use are:

  1. DB Lab: I don't think it has a JS backend but it could be added for your needs.
  2. LMS: Has a JS backend, however, you have to use Rep types in your DSLs.

You would have to check with the authors if these frameworks compile with Scala.js and what is the state of their JS backends.

Your proposal tries to use Scala.js as a deep embedding framework, but Scala.js can't be compiled with Scala.js.

vjovanov
  • 120
  • 5
  • Thanks! I have actually started trying the LMS-based js-scala library (github.com/js-scala/js-scala), but I'm having trouble working with Rep[T], where T is a case class. The examples provided with the library itself are trivial, only using primitive types. Any more complex examples that you know of? – silverberry Aug 10 '17 at 15:49
  • Let me rephrase my question. How does one provide a custom code generator for one's own domain types? – silverberry Aug 10 '17 at 17:51
  • I think I figured this out. – silverberry Aug 10 '17 at 19:58
0

Honestly, I think that framing it as "compile Scala expressions to Javascript during runtime" is constructing a recipe for failure. Scala.js can't compile Scala, and probably won't be able to do so in the near future -- too much of the compiler infrastructure is JVM-centric.

So instead, I'd recommend reframing the problem. Formally define the language that you want to be able to work with at runtime. (Which might well be a subset of Scala.) Use a Scala.js parser such as FastParse to write a parser for it that generates abstract syntax tress, and an interpreter that turns those syntax trees into JavaScript.

Yes, it's a bit of effort. But it's achievable, which trying to compile arbitrary Scala to JavaScript at runtime, in Scala.js, isn't...

Justin du Coeur
  • 2,699
  • 1
  • 14
  • 19
  • Thanks. But I'm not trying to compile arbitrary Scala into Javascript. Just the boolean operations on the building blocks that are already compile into Javascript (see above). – silverberry Aug 08 '17 at 18:47
  • I wonder if js-scala could be used for this.... (https://github.com/js-scala/js-scala) – silverberry Aug 09 '17 at 00:43