Compare commits

...

4 commits

6 changed files with 135 additions and 20 deletions

3
.gitmodules vendored Normal file
View file

@ -0,0 +1,3 @@
[submodule "v2/syntaxes/gleam"]
path = v2/syntaxes/gleam
url = https://github.com/digitalcora/sublime-text-gleam.git

View file

@ -19,6 +19,7 @@ build_search_index = false
# Theme can be customised by setting the `highlight_theme` variable to a theme supported by Zola # Theme can be customised by setting the `highlight_theme` variable to a theme supported by Zola
highlight_code = true highlight_code = true
smart_punctuation = true smart_punctuation = true
extra_syntaxes_and_themes = ["syntaxes/gleam"]
[extra] [extra]
author = "Wesley Moore" author = "Wesley Moore"

View file

@ -408,7 +408,7 @@ Enter `systemd-sysusers`. Now the `chrony` package includes a file
`sysusers.conf`, which is installed into `/usr/lib/sysusers.d/chrony.conf` when the package `sysusers.conf`, which is installed into `/usr/lib/sysusers.d/chrony.conf` when the package
is installed: is installed:
```tcl ```
# Create chrony system user # Create chrony system user
u _chrony - "chrony user" /var/lib/chrony /usr/bin/nologin u _chrony - "chrony user" /var/lib/chrony /usr/bin/nologin

View file

@ -2,8 +2,8 @@
title = "Building a Hybrid Native Application With Gleam and Tauri" title = "Building a Hybrid Native Application With Gleam and Tauri"
date = 2024-02-19T09:56:49+10:00 date = 2024-02-19T09:56:49+10:00
#[extra] [extra]
#updated = 2023-01-11T21:11:28+10:00 updated = 2024-02-20T22:57:15+10:00
+++ +++
I took a few hours this weekend to experiment with building a hybrid I took a few hours this weekend to experiment with building a hybrid
@ -35,14 +35,50 @@ maintained Elm without the restrictions on interop with existing JavaScript
code. code.
To get started here's some Gleam code that demonstrates a decent chunk of the To get started here's some Gleam code that demonstrates a decent chunk of the
language. My blog doesn't highlight Gleam code at the moment so what's shown language:
below is a picture. See [example.gleam](example.gleam) for the source file:
<img src="gleam-example.png" width="378" alt=""> ```gleam
import gleam/io
import gleam/list
import gleam/int
pub type Temperature {
F(Float)
C(Float)
}
pub type Celsius {
Celsius(Float)
}
pub fn main() {
let temps = [C(22.0), C(-5.0), F(0.0), C(0.0), F(32.0)]
io.debug(avg(temps))
}
pub fn avg(measurements: List(Temperature)) -> Celsius {
let sum =
list.fold(measurements, 0.0, fn(sum, val) {
let Celsius(c) = to_c(val)
sum +. c
})
let length =
list.length(measurements)
|> int.to_float
Celsius(sum /. length)
}
fn to_c(temp: Temperature) -> Celsius {
case temp {
C(c) -> Celsius(c)
F(f) -> Celsius({ f -. 32.0 } /. 1.8)
}
}
```
When run it outputs: When run it outputs:
Celcius(1.8444444444444443) Celsius(1.8444444444444443)
The generated JavaScript (as of Gleam v1.0.0-rc2) is shown below. While it's The generated JavaScript (as of Gleam v1.0.0-rc2) is shown below. While it's
certainly longer than what you might naively write in JavaScript directly it's certainly longer than what you might naively write in JavaScript directly it's
@ -131,7 +167,7 @@ a copy of Chromium in every application, instead relying on the system web view
on the host operating system. on the host operating system.
You implement your application logic in Rust and communicate with the UI You implement your application logic in Rust and communicate with the UI
by emitting and listing to events. The end result is a cross-platform desktop by emitting and listening to events. The end result is a cross-platform desktop
app that is a lot smaller than if it were built with Electron. app that is a lot smaller than if it were built with Electron.
This weekend I decided to try combining these things to see how feasible it This weekend I decided to try combining these things to see how feasible it
@ -171,7 +207,7 @@ fn greet(name: &str) -> String {
I then needed to be able to use [the `invoke` function][invoke] from the I then needed to be able to use [the `invoke` function][invoke] from the
[@tauri-apps/api npm package][tauri-apps/api]. Following the pattern I observed [@tauri-apps/api npm package][tauri-apps/api]. Following the pattern I observed
in other Gleam packages. I created a JavaScript file to act as a bridge between in other Gleam packages I created a JavaScript file to act as a bridge between
Gleam and `@tauri-apps/api`: Gleam and `@tauri-apps/api`:
```javascript ```javascript
@ -198,12 +234,12 @@ I could then define the external function in the Gleam code and call it:
pub fn greet(name: String) -> Promise(Result(String, String)) pub fn greet(name: String) -> Promise(Result(String, String))
``` ```
The challenge was `greet` is an async function, so it returns a promise, which The next challenge was `greet` is an async function, so it returns a promise,
does not integrate into a [lustre.simple] application well. Fortunately there which does not integrate into a [lustre.simple] application well. Fortunately
the less simple [lustre.application] that adds effects. After looking at some there is the less simple [lustre.application] that adds effects. After looking
existing code I was finally about to come up with a working solution. The full at some existing code I was finally able to come up with a working solution.
Gleam code is shown below. `get_greeting` and `do_get_greeting` being the main The full Gleam code is shown below. `get_greeting` and `do_get_greeting` being
parts of interest. the main parts of interest.
```gleam ```gleam
// src/demo.gleam // src/demo.gleam
@ -305,13 +341,13 @@ fn view(model: Model) -> Element(Msg) {
} }
``` ```
I added a `Greet` message for when the "Greet" button is clicked. In the `update` I added a `Greet` message for when the "Greet" button is clicked. The `update`
function that doesn't update the model but calls `get_greeting` as its function that doesn't update the model but calls `get_greeting` as its
side-effect. That builds an `Effect` from `do_get_greeting`, which calls the side-effect. That builds an `Effect` from `do_get_greeting`, which calls the
FFI function and maps the `Result` to a `GotGreeting` message containing the FFI function and maps the `Result` to a `GotGreeting` message containing the
greeting or an error message. greeting or an error message.
`update` then handles the `GotGreeting` message by updating the model, which in `update` handles the `GotGreeting` message by updating the model, which in
turn updates the UI. I'm skipping over the `Model`, `view`, `update` turn updates the UI. I'm skipping over the `Model`, `view`, `update`
architecture of this Lustre application since it's basically the [Elm architecture of this Lustre application since it's basically the [Elm
architecture]. A similar pattern is seen in Reason React, ReScript, and [React architecture]. A similar pattern is seen in Reason React, ReScript, and [React
@ -361,7 +397,7 @@ fn main() {
In a production application you'd want a mechanism for cleanly shutting the In a production application you'd want a mechanism for cleanly shutting the
thread down but for experimentation purposes I skipped that. Now I needed to thread down but for experimentation purposes I skipped that. Now I needed to
listen for the `tick` event on the UI. I added another glue function to the FFI listen for the `tick` event in the UI. I added another glue function to the FFI
file: file:
```gleam ```gleam
@ -588,8 +624,8 @@ Some unanswered questions I have from this experiment are:
2. What is the right way to import `gleam.mjs` from JavaScript code? 2. What is the right way to import `gleam.mjs` from JavaScript code?
3. What is the structure of the Gleam `build` directory? 3. What is the structure of the Gleam `build` directory?
* I see `dev` and `prod` sub-directories. * I see `dev` and `prod` sub-directories.
* Is the `prod` on used when targeting JavaScript (I can't see any * Is the `prod` on used when targeting JavaScript
equivalent of Cargo's `--release` in the `gleam` CLI help). * I can't see any equivalent of Cargo's `--release` in the `gleam` CLI help.
The full project code is available here: The full project code is available here:

View file

@ -43,6 +43,80 @@ pre {
font-size: 14px; font-size: 14px;
color: #fcfcfc; color: #fcfcfc;
max-height: 800px; max-height: 800px;
position: relative;
}
.language-asm::after,
.language-bash::after,
.language-c::after,
.language-css::after,
.language-dockerfile::after,
.language-gleam::after,
.language-ini::after,
.language-javascript::after,
.language-json::after,
.language-rust::after,
.language-sh::after,
.language-tcl::after,
.language-toml::after,
.language-vim::after,
.language-xml::after,
.language-yaml::after
{
position: absolute;
top: 0px;
right: 0px;
color: black;
background-color: #FFFA;
padding: 0 0.5em 1px 0.5em;
border-bottom-left-radius: 8px;
}
.language-asm::after {
content: 'Assembly';
}
.language-bash::after {
content: 'Bash';
}
.language-c::after {
content: 'C';
}
.language-css::after {
content: 'CSS';
}
.language-dockerfile::after {
content: 'Dockerfile';
}
.language-gleam::after {
content: 'Gleam';
}
.language-ini::after {
content: 'INI';
}
.language-javascript::after {
content: 'JavaScript';
}
.language-json::after {
content: 'JSON';
}
.language-rust::after {
content: 'Rust';
}
.language-sh::after {
content: 'Shell';
}
.language-tcl::after {
content: 'Tcl';
}
.language-toml::after {
content: 'TOML';
}
.language-vim::after {
content: 'Vim';
}
.language-xml::after {
content: 'XML';
}
.language-yaml::after {
content: 'YAML';
} }
h1,h2,h3,h4 { h1,h2,h3,h4 {
font-family: $heading-family; font-family: $heading-family;

1
v2/syntaxes/gleam Submodule

@ -0,0 +1 @@
Subproject commit 6c2080bf6039aa4c844086953d5aca49a7cd5ab6