ElixirScript - converts Elixir to JavaScript

Haha, this one is good. However after longer time working with Elixir, for me there is not such need to want OOP back :smiley:

2 Likes

@OvermindDL1 wow :smiley:

I do think it’s valid to think about interoperability when it comes to JavaScript classes. Since JavaScript may or may not be heading in a class heavy direction. My current recommendation is to define them in JavaScript and use them in Elixirscript if you have to. As more people start using Elixirscript and thinking about it more, there may be a more elegant solution.

What I would LOVE to do is expose the JavaScript AST in Elixirscript, let anyone with the knowledge do whatever they want with it, have the JS AST returned to Elixirscript and go on from there. I don’t know where to begin with that one, but it would be nice.

2 Likes

Just published version 0.27.0 of elixirscript. One major change is that super and defoverridable are now implemented. The other is that JavaScript functions, properties, and modules can now be accessed from the JS module, examples: JS.alert("hi"), JS.console.log("hi"), JS.Date.now()

For the rest of the changes, please check the changelog

6 Likes

@bryanjos: Well done!
One small tip: please include all modules in your namespace module like:

defmodule ElixirScript.JS do
  # ...
end

because you are using bad practice.
Imagine that I want to add another dependency to my project with for example JavaScript analyser/parser or similar library.
When two libraries declares modules with same names then it’s easy to get conflict sooner or later.
We can alias your modules, but don’t know how hard will be to resolve module conflicts.
Anyway, good work and thanks to implement my comments!

2 Likes

No new release just yet, but thought I’d send an update.

Thanks to the changes in OTP 20 and Elixir 1.5, ElixirScript was rewritten and can now support almost all Elixir features besides receive. More information is in this blog post.

Another addition is a change in JavaScript interoperability with an FFI module. More information on that can be found here

Next I’ve update the Todo Example to work with ElixirScript in master. I have taken the react parts in made it into a library that it uses. Showing how an ElixirScript library could look. Basically a normal mix project with javascript files in the priv folder if neeeded.

Most of the work now is implementing whatever Erlang calls Elixir makes in JavaScript. That’s where contributions to the project are definitely the most helpful. The other is in fixing bugs that appear along the way.

For a release, hoping to have something before ElixirConf.

P.S: receive

Want to support this eventually. I would like to be able to implement a process system in JavaScript using async/await and use that in ElixirScript, but my brain can’t quite grasp how to do so. Any help there would be appreciated

Edit: Or using the current implementation that uses generators is a possibility, but async functions seem to play better with normal javascript functions.

4 Likes

What about using webworkers? They communicate by messaging too, so…

WebWorkers are limited (for security), so you can’t:

  1. Access DOM, because it’s not thread safe
  2. document object
  3. parent object
  4. window object
  5. send message with function definition, because messages using for communication are converted to JSON format

So I think that async/await would be better for full API support and WebSockets could be optionally implemented also.

@bryanjos: This is sample code from Polish tutorial (I translated messages only):

var worker = new Worker('background.js');

// register `message` event listener - handle message from worker thread
worker.addEventListener('message', function(e) {
    alert('received answer: ' + e.data);
}, false);

// send start message
worker.postMessage('start');

// send stop message
worker.postMessage('stop');

/* ---------------- source code of background.js ---------------- */

// register 'message' event listener - handle message from parent thread
this.addEventListener('message', function(e) {
    switch(e.data) {
        case 'start':
            this.postMessage('thread started!');
            break;
        case 'stop':
            this.postMessage('thread stopped!');
            this.close(); // stop this worker
            break;
        default:
            this.postMessage('unknown command!');
    }
}, false);

in case you decide to use WebWorkers API for this purpose.

Webworkers are a bit too heavyweight to work since they are pretty much threads. But they may work. There was another reason that I can’t remember why I stayed away from them that I can’t remember.

Edit: Now I remember. What @Eiji said about not having access to everything
Edit again: Also receive in Elixir is synchronous, but it’s not in web workers unless you wrap it in a promise and then use async/await anyways.

Precisely!

The DOM and everything related to it should be accessed by a single ‘thread’, messages should be posted to it to alter it in some way, just like you message GenServer’s now.

You could always term_to_binary and vice-versa back or use some other encoding format to marshall data across.

Well you could break up a ‘process’ to carry around and use its own stack, then you have greenthreads, and could keep it on the main thing then.

Honestly I’d say don’t both making receive or threads of any form, javascript is way too different to be able to wrap it in OTP well unless you used emscripten to recompile the BEAM itself to run on the client itself (which you could do).

Yeah the plan right now is just to not support receive until there is a clearer solution or more resources. But I do know it is possible

1 Like

ElixirScript 0.30.0 is now published in Hex. This release has many changes from previous versions. For more information, check out this blog post, the changelog, and the docs.

4 Likes

From what I understand, Elixirscript compiles Elixir to erlang and then converts erlang to Javascript, right?
This means that I can’t convert isolated elixir expressions into Javascript expressions based on the AST of Elixir and not the compiled output. I was thinking of something like f(x, 1, %{a: 1, b: 2}) bring converted into f(x, 1, {a: 1, b: 3}).

Is there anything in Elixirscript that can help me do this, or my best bet is to write a custom solution myself?

1 Like

I don’t think elixirscript runs on erlang bytecode. From the readme:

This is done by taking the Elixir AST and converting it into JavaScript AST and then to JavaScript code. This is done using the Elixir-ESTree library.

2 Likes

I haven’t looked at the source, but from the blog:

Calls to Erlang functions that the Elixir standard library make have to be reimplemented in JavaScript. There are many that are implemented, but there are more that need to be. This effects the availability of some Elixir standard library calls. A major contribution to ElixirScript would be to help with an implementation of an Erlang function that is blocking the use of an Elixir function you want to use!

Any usage examples / use cases mates?

I wanted to offer some feedback:

I am glad you have been working on compiling Elixir to JS. But what I’d really like to see is a ReactJS-like experience on the frontend. What I mean by that is:

  • keep the modules structure of Elixir, where anything in the project can be accessed without needing to know the specific file path or explicitly export anything

  • keep the ability for Elixir syntax to be transpiled to JavaScript

  • otherwise, remove the quirks and follow the structure of React

I’ll elaborate on the third point.

My main beef with JavaScript is the syntax includes meaningless, “boilerplate” characters. Conversely, I really like JSX. While it’s already too late and doesn’t mean much coming from a stranger, I think you went in the wrong direction with a DSL different from JSX.

I wish I could write a file like this:

# App.ex

defmodule ProjectName.App do
  use React # EX
  import ReactNativeWeb
  alias ProjectName.ErrorBoundary

  def mount,
    do: IO.inspect "Mounted!"

  def render(props, state) do
    <ErrorBoundary>
      <Text elixir={
        if Platform.OS == "web", do: "web", else: "screen"
      }/>
    </ErrorBoundary>
  end
end

This would render the text web on your computer or screen on your phone.

In the JSX article, there is a quote:

Angular, Ember and Knockout put “JS” in your HTML.
React puts “HTML” in your JS.

The analogy here is:

ElixirScript puts “JS” in your Elixir.
What I have proposed puts “Elixir” in your JSX.

I.e. Yes, Elixir is more powerful than JS–that’s why we’re replacing it. But, I hope you’d agree that JSX is a better DSL for the frontend.

From an implementation standpoint, this may mean creating a JSX DSL for Elixir (like Phoenix’s Ecto is for SQL queries).

I.e. Keep React’s design patterns and JSX. Replace JavaScript’s syntax with Elixir’s.


Actually, you don’t even need to handroll the DSL. You can just have a special render macro which accepts a do: block with JSX as its last argument.

I know this isn’t exactly what you are envisioning embedded directly into Elixir but have you looked at ReasonML with ReasonReact?

That’s what I am happily using to get JSX with elixir style “module”/component structure you are talking about (no explicit importing/exporting) and great type inference.

1 Like

ElixirScript works at a level where if you can do what you defined in standard Elixir, it’s work in ElixirScript. I’d be interested if anyone did what you prescribed. There was an ElixirScript issue open for almost a year about JSX but nothing really came about from it.

Googling, JSX has not yet come to Elixir… Anyone know off the top of one’s head about other languages that have implemented it?

I found this article. In a nutshell, JSX syntax is merely syntactic sugar for an AST of React.createElement calls.

If you’ve seen the library Shorthand, it allows one to create maps with JS-like behavior: m(item) => %{item: item}. For ElixirScript, you could simply have a function jsx(type, props, children) that evaluates to the JS equivalent.

1 Like