mirror of
https://github.com/wezm/wezm.net.git
synced 2024-12-18 18:29:54 +00:00
Compare commits
4 commits
c5f5be6abe
...
7b2f7dfeb8
Author | SHA1 | Date | |
---|---|---|---|
7b2f7dfeb8 | |||
1dc3ede6be | |||
7b24b7d3c2 | |||
c191d6efd9 |
6 changed files with 135 additions and 20 deletions
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
[submodule "v2/syntaxes/gleam"]
|
||||
path = v2/syntaxes/gleam
|
||||
url = https://github.com/digitalcora/sublime-text-gleam.git
|
|
@ -19,6 +19,7 @@ build_search_index = false
|
|||
# Theme can be customised by setting the `highlight_theme` variable to a theme supported by Zola
|
||||
highlight_code = true
|
||||
smart_punctuation = true
|
||||
extra_syntaxes_and_themes = ["syntaxes/gleam"]
|
||||
|
||||
[extra]
|
||||
author = "Wesley Moore"
|
||||
|
|
|
@ -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
|
||||
is installed:
|
||||
|
||||
```tcl
|
||||
```
|
||||
# Create chrony system user
|
||||
|
||||
u _chrony - "chrony user" /var/lib/chrony /usr/bin/nologin
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
title = "Building a Hybrid Native Application With Gleam and Tauri"
|
||||
date = 2024-02-19T09:56:49+10:00
|
||||
|
||||
#[extra]
|
||||
#updated = 2023-01-11T21:11:28+10:00
|
||||
[extra]
|
||||
updated = 2024-02-20T22:57:15+10:00
|
||||
+++
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
below is a picture. See [example.gleam](example.gleam) for the source file:
|
||||
language:
|
||||
|
||||
<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:
|
||||
|
||||
Celcius(1.8444444444444443)
|
||||
Celsius(1.8444444444444443)
|
||||
|
||||
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
|
||||
|
@ -131,7 +167,7 @@ a copy of Chromium in every application, instead relying on the system web view
|
|||
on the host operating system.
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
[@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`:
|
||||
|
||||
```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))
|
||||
```
|
||||
|
||||
The challenge was `greet` is an async function, so it returns a promise, which
|
||||
does not integrate into a [lustre.simple] application well. Fortunately there
|
||||
the less simple [lustre.application] that adds effects. After looking at some
|
||||
existing code I was finally about to come up with a working solution. The full
|
||||
Gleam code is shown below. `get_greeting` and `do_get_greeting` being the main
|
||||
parts of interest.
|
||||
The next challenge was `greet` is an async function, so it returns a promise,
|
||||
which does not integrate into a [lustre.simple] application well. Fortunately
|
||||
there is the less simple [lustre.application] that adds effects. After looking
|
||||
at some existing code I was finally able to come up with a working solution.
|
||||
The full Gleam code is shown below. `get_greeting` and `do_get_greeting` being
|
||||
the main parts of interest.
|
||||
|
||||
```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
|
||||
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
|
||||
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`
|
||||
architecture of this Lustre application since it's basically the [Elm
|
||||
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
|
||||
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:
|
||||
|
||||
```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?
|
||||
3. What is the structure of the Gleam `build` directory?
|
||||
* I see `dev` and `prod` sub-directories.
|
||||
* Is the `prod` on used when targeting JavaScript (I can't see any
|
||||
equivalent of Cargo's `--release` in the `gleam` CLI help).
|
||||
* Is the `prod` on used when targeting JavaScript
|
||||
* I can't see any equivalent of Cargo's `--release` in the `gleam` CLI help.
|
||||
|
||||
The full project code is available here:
|
||||
|
||||
|
|
|
@ -43,6 +43,80 @@ pre {
|
|||
font-size: 14px;
|
||||
color: #fcfcfc;
|
||||
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 {
|
||||
font-family: $heading-family;
|
||||
|
|
1
v2/syntaxes/gleam
Submodule
1
v2/syntaxes/gleam
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit 6c2080bf6039aa4c844086953d5aca49a7cd5ab6
|
Loading…
Reference in a new issue