Hologram v0.8.0: Elixir Gets JavaScript Interop

Hologram v0.8.0 is out! This release brings JavaScript interoperability - the most requested feature since the project’s inception. You can now call JS functions, use npm packages, interact with Web APIs, instantiate classes, and work with Web Components - all from Elixir code, with zero latency on the client side.

Special thanks to @robak86 for extensive help with the JS interop API design. Thanks to @ankhers for contributing Web Components support and to @mward-sudo for a language server compatibility fix and :unicode module refactoring.

Thanks to our sponsors for making sustained development possible: Curiosum (Main Sponsor), Erlang Ecosystem Foundation (Milestones Sponsor), and our GitHub sponsors - Innovation Partner: @sheharyarn, Framework Visionaries: @absowoot, Oban, @Lucassifoni, @robertu, and all other GitHub sponsors.

Full details in the blog post: Hologram v0.8.0: Elixir Gets JavaScript Interop

26 Likes

Massive release!

I’ve not played with the new JS interop, but the API looks very well considered. Hologram is moving into very interesting territory.

I look forward to continuing to contribute and to using the project for something real sometime soon.

2 Likes

Thank you @mward-sudo! It was a massive amount of work indeed - I think @robak86 aged about 5 years during the API design sessions alone. The fact that he still answers my phone calls is either a testament to his patience or evidence that he hasn’t figured out how to block a number yet… Or maybe he just picks up out of morbid curiosity about what I’ll ask next.

10 Likes

Amazing! Looks very comprehensive.

2 Likes

I’m sure y’all considered this as part of the design but I can’t help but think how clean it’d be to drop the brackets and create the list as an implementation detail under the hood.

So this

result =
  :Decimal
  |> JS.new([100])
  |> JS.call(:plus, [23])
  |> JS.call(:toNumber, [])  

becomes

result =
  :Decimal
  |> JS.new(100)
  |> JS.call(:plus, 23)
  |> JS.call(:toNumber)  
2 Likes

Unfortunately there are two problems with dropping the brackets:

  1. List ambiguity - JS.call(:obj, :processList, [1, 2, 3]) becomes impossible to distinguish between one argument (the array [1, 2, 3]) and three separate arguments.

  2. Arity clash - JS.call/2 calls a standalone function, JS.call/3 calls a method on a receiver. If args were optional, JS.call(:Decimal, :toNumber) could be either call/2 with one arg or call/3 with no args.

The explicit args list is also consistent with Kernel.apply/3, which always requires the args list. I actually considered naming it JS.apply at one point, so the parallel is intentional.

2 Likes