+
+
Using Gleam in HTML with as little JavaScript knowledge as possible
+
I've been looking at the Gleam language recently. Among other features, it can be compiled to JavaScript, and thus presumably used on web frontend. I wanted to try that. This is a short tutorial on how to do that with little idea how JavaScript is supposed to work.
+
My initial JS knowledge: alert(3), onclick and some basic selectors, i.e. the little stuff that is useful to add minor interactivity to HTML pages and implement trivial ViolentMonkey scripts. Namely: I have no knowledge about modules and whatnot, and this post is just a result of my trial-and-error attempt at embedding my Gleam. I succeeded, but still have no idea what I am doing :-)
+
+
My code
+
Let's start with some trivial code, in src/lol.gleam (in a project initialised with gleam new lol and the repeatedly package added with gleam add repeatedly):
+
+import gleam/io
+import repeatedly
+
+pub fn say_hello() {
+ repeatedly.call(2000, Nil, fn(_state, _call) {io.println(get_greeting())})
+}
+
+pub fn get_greeting() {
+ "Hello World!"
+}
+
+pub fn main() {
+ io.println("3, 2, 1, go!")
+}
+
+
This snippet has many of the basic stuff I might need in future: it does some io, it uses another package , it returns data I will want to show. At first I am mostly interested in multiplatform stuff (i.e. running also on the BEAM backend), so I don't want to use any DOM frameworks at first, though I will mention some of the ways later in the post.
+
So, build this for JavaScript: gleam build --target javascript, and stuff happens. It is not very clear what to do now, but grep``ping through the ``build/ directory shows that the built code lives in build/dev/javascript/lol/lol.mjs.
+
+
+
Embedding the code
+
Here comes the fun part: the code is in JavaScript module, not plain code. That comes with several surprises:
+- It can only be imported from other modules, not by “plain” code in global scope
+- Due to CORS, imports only work with network schemes, not with the file:// one
+- Due to scoping, I have not found a way of using the functions from devtools console
+
Big wins for the platform /s, but we have to live with that, so let's try to write the respective HTML for this. I put that in main.html in the project root, but it does not probably matter. The code:
+
+<!doctype html>
+<html>
+<head>
+ <meta charset=utf-8> <!-- firefox complains otherwise -->
+ <title>Buh</title>
+</head>
+<body>
+ <!-- some random elements to work with -->
+ <button id=butt>Klik mee</button>
+ <p id=uwu>
+
+ <!-- the “binding” to our Gleam code. This probably needs to be at the end of the page, since it needs to be able to use the selectors. -->
+ <script type=module> //It has to be a module to allow imports
+ import * as lol from "./build/dev/javascript/lol/lol.mjs";
+
+ // Set the text of one element to the computed stuff:
+ let par = document.getElementById('uwu');
+ par.innerText = lol.get_greeting();
+
+ // Let's have an interactive button (I love the come-from pattern, but using `onclick` would be even trickier…)
+ let butt = document.getElementById('butt');
+ butt.addEventListener('click', lol.say_hello);
+
+ // Apparently, the `main` function does not get run automatically, so call that explicitly.
+ lol.main();
+ </script>
+</body>
+</html>
+
+
Shoutout to the person who asked the same question on StackOverflow. From this point on, we only need a HTTP server; luckily, Python has one in stdlib, so just calling python3 -m http.server 12312 in the project root lets us load http://localhost:12312/main.html and see our page.
+
+
+
Next steps, alternatives et cetera
+
When developing web frontend in Gleam, the more common way is using a framework like Lustre or at least a DOM library (there are several, I have no idea how mature those are). I did not go this route yet, because I am more interested in using backend-agnostic Gleam code from JavaScript and don't mind writing the trivial bindings in JavaScript.
+
Also, a common thing to do is using a minifier+bundler like esgleam (it uses esbuild under the hood), so that the whole project is in one file. I don't think I need that now (having the JS be readable is more important to me atm and I don't want to complicate things further), but there is at least one tutorial on how to do that.
+
Also, during writing of this article I realised Gleam can run all the JS code from itself, so I could have a js_main function that would bind the HTML from Gleam itself. But this is probably more readable and separated anyway.
+
+
+
+
+
+
+