Gleam (Rust + Erlang) 事始め

#１．はじめに

https://gleam.run/
https://github.com/gleam-lang/gleam

昨年、2019年9月にVer.0.1がリリースされた非常に新しい言語で、Rustで書かれていてErlang VM上で動くコンパイラです。

最近ElixirとRustを勉強していて興味があったので、Gleamで`Hello World`を表示するまで試してみました。

##Gleamのコード例

構文はRustですが、パイプを使って順次処理を流せるところが、Elixirっぽい感じですね～

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/145581/87976f25-c1a9-237c-72a3-0e93da97141d.png)


#２．インストール

**実行環境**

 - Raspberry Pi 4
 - Raspbian Buster

```sh:コマンドライン
$ uname -a
Linux raspi4 5.4.51-v7l+ #1333 SMP Mon Aug 10 16:51:40 BST 2020 armv7l GNU/Linux
```

##(1)Rust

RaspbianのaptでインストールできるRustでは、gleamがコンパイルできないので、最新版(1.47.0)をソースからビルドしました。（後述にエラーの詳細あり）

こちら[^rust]を参考にインストール。

[^rust]: https://mseeeen.msen.jp/rust-in-raspberry-pi/


```sh:コマンドライン
$ cd gitwork/rust
$ curl https://sh.rustup.rs -sSf | sh
info: downloading installer

Welcome to Rust!

This will download and install the official compiler for the Rust
･･･（省略）･･･
Current installation options:


   default host triple: armv7-unknown-linux-gnueabihf
     default toolchain: stable (default)
               profile: default
  modify PATH variable: yes

1) Proceed with installation (default)
2) Customize installation
3) Cancel installation
>1

info: profile set to 'default'
･･･（省略）･･･
```

```sh:コマンドライン
$ source $HOME/.cargo/env
$ rustc --version
rustc 1.47.0 (18bf6b4f0 2020-10-07)
$ cargo --version
cargo 1.47.0 (f3c7e066a 2020-08-28)
$
```

```sh:コマンドライン
$ sudo apt install -y libssl-dev
$
```

```sh:コマンドライン
$ cargo install cargo-edit
    Updating crates.io index
  Downloaded cargo-edit v0.7.0
  Downloaded 1 crate (57.6 KB) in 0.59s
  Installing cargo-edit v0.7.0
･･･（省略）･･･
    Finished release [optimized] target(s) in 15m 21s
  Installing /home/pi/.cargo/bin/cargo-add
  Installing /home/pi/.cargo/bin/cargo-rm
  Installing /home/pi/.cargo/bin/cargo-upgrade
   Installed package `cargo-edit v0.7.0` (executables `cargo-add`, `cargo-rm`, `cargo-upgrade`)
$
```

```sh:コマンドライン
$ git clone https://github.com/sagiegurari/cargo-make.git
Cloning into 'cargo-make'...
remote: Enumerating objects: 559, done.
remote: Counting objects: 100% (559/559), done.
remote: Compressing objects: 100% (348/348), done.
remote: Total 13676 (delta 386), reused 332 (delta 195), pack-reused 13117
Receiving objects: 100% (13676/13676), 6.72 MiB | 3.07 MiB/s, done.
Resolving deltas: 100% (10889/10889), done.


$ cd cargo-make
$ cargo install --force cargo-make
    Updating crates.io index
  Downloaded cargo-make v0.32.7
  Downloaded 1 crate (1.2 MB) in 1.69s
･･･（省略）･･･
   Compiling cargo-make v0.32.7
    Finished release [optimized] target(s) in 9m 41s
  Installing /home/pi/.cargo/bin/cargo-make
  Installing /home/pi/.cargo/bin/makers
   Installed package `cargo-make v0.32.7` (executables `cargo-make`, `makers`)
$
```

```sh:コマンドライン
 $ rustup update
info: syncing channel updates for 'stable-armv7-unknown-linux-gnueabihf'
info: checking for self-updates

  stable-armv7-unknown-linux-gnueabihf unchanged - rustc 1.47.0 (18bf6b4f0 2020-10-07)

info: cleaning up downloads & tmp directories
$
```

動作確認します。

```sh:コマンドライン
$ cd gitwork/rust
$ mkdir ./hello
$ cd hello
$ nano main.rs
$ cat main.rs 
```

ソースの中身

```rust:main.rs
fn main() {
    println!("Hello, world!");
}
```

コンパイルと実行

```sh:コマンドライン
$ rustc main.rs
$ ./main
Hello, world!
$
```

rustの動作確認ができました。


##(2)Erlang

（記述中。基本的にはElixirのインストール時の手順に倣う）

```sh:コマンドライン
$ erl
Erlang/OTP 22 [erts-10.5.4] [source] [smp:4:4] [ds:4:4:10] [async-threads:1] [hipe]

Eshell V10.5.4  (abort with ^G)
1> 
```

##(3)gleam

公式マニュアル[^gleaminst]に従ってインストール。

[^gleaminst]: https://gleam.run/getting-started/

```sh:コマンドライン
$ cd gitwork/rust

$ git clone https://github.com/gleam-lang/gleam.git --branch v0.11.0
Cloning into 'gleam'...
remote: Enumerating objects: 5, done.
･･･（省略）･･･

$ cd gleam
$ make
build                          Build the compiler
install                        Build the Gleam compiler and place it on PATH
test-watch                     Run compiler tests when files change

#ビルド
$ make build
cargo build --release
  Downloaded arrayvec v0.5.1
  Downloaded askama_derive v0.10.3
  Downloaded nix v0.17.0
･･･（省略）･･･
   Compiling askama v0.10.3
   Compiling hyper-tls v0.4.3
   Compiling reqwest v0.10.7
   Compiling gleam v0.11.0 (/home/pi/gitwork/rust/gleam)
    Finished release [optimized] target(s) in 23m 08s

#ビルド
$ make install
cargo install --path . --force --locked
  Installing gleam v0.11.0 (/home/pi/gitwork/rust/gleam)
    Updating crates.io index
    Finished release [optimized] target(s) in 4.08s
  Installing /home/pi/.cargo/bin/gleam
   Installed package `gleam v0.11.0 (/home/pi/gitwork/rust/gleam)` (executable `gleam`)
$
```

gleamコマンドを実行したところ。

```sh:コマンドライン
$ gleam
gleam 0.11.0

USAGE:
    gleam <SUBCOMMAND>

FLAGS:
    -h, --help       Prints help information
    -V, --version    Prints version information

SUBCOMMANDS:
    build     Compile a project
    docs      Render HTML documentation for a project
    format    Format source code
    help      Prints this message or the help of the given subcommand(s)
    new       Create a new project
$
```


##(4)rebar3

rebar3は、Erlangのビルドツールです。
公式マニュアル[^rebar3inst]に従ってインストール。

[^rebar3inst]: https://github.com/erlang/rebar3

```sh:コマンドライン
$ cd gitwork/

$ git clone https://github.com/erlang/rebar3.git
Cloning into 'rebar3'...
remote: Enumerating objects: 111, done.
remote: Counting objects: 100% (111/111), done.
remote: Compressing objects: 100% (84/84), done.
remote: Total 20765 (delta 57), reused 53 (delta 27), pack-reused 20654
Receiving objects: 100% (20765/20765), 6.23 MiB | 3.30 MiB/s, done.
Resolving deltas: 100% (14204/14204), done.

$ cd rebar3
$ ./bootstrap
===> Verifying dependencies...
===> Fetching bbmustache v1.10.0
===> Fetching cth_readable v1.4.8
･･･（省略）･･･
===> Compiling certifi
===> Analyzing applications...
===> Compiling rebar
===> Building escript for rebar...
$
```

インストールとコマンドの実行

```sh:コマンドライン
$ ./rebar3 local install
===> Extracting rebar3 libs to /home/pi/.cache/rebar3/vsns/3.14.1+build.4800.ref38a3db0e/lib...
===> Writing rebar3 run script /home/pi/.cache/rebar3/bin/rebar3...
===> Add to $PATH for use: export PATH=/home/pi/.cache/rebar3/bin:$PATH

#とりあえずパスを通す
#常用するなら.bashrcに追記のこと
$ export PATH=/home/pi/.cache/rebar3/bin:$PATH

$ rebar3 
Rebar3 is a tool for working with Erlang projects.

Usage: rebar3 [-h] [-v] [<task>]

  -h, --help     Print this help.
  -v, --version  Show version information.
  <task>         Task to run.

  Set the environment variable DEBUG=1 for detailed output.

Several tasks are available:
･･･（省略）･･･
Run 'rebar3 help <TASK>' for details.
$
```

#３．簡単なプログラムを試す

##(1)空のプロジェクト

ドキュメント[^creating-a-project]に従って進めます。

[^creating-a-project]: https://gleam.run/writing-gleam/creating-a-project/

```sh:コマンドライン
$ cd gitwork/gleam

#プロジェクトを生成
$ gleam new hello
* creating hello/LICENSE
* creating hello/.gitignore
* creating hello/README.md
* creating hello/gleam.toml
* creating hello/test/hello_test.gleam
* creating hello/src/hello.gleam
* creating hello/.github/workflows/test.yml
* creating hello/rebar.config
* creating hello/src/hello.app.src

Your Gleam project "hello" has been successfully created.
The rebar3 program can be used to compile and test it.

    cd hello
    rebar3 eunit

#ビルド
$ cd hello/
$ rebar3 eunit
===> Fetching rebar_gleam v0.1.0
===> Analyzing applications...
===> Compiling rebar_gleam
===> Verifying dependencies...
===> Fetching gleam_stdlib v0.11.0
Compiling hello
･･･（省略）･･･
===> Performing EUnit tests...
.
Finished in 0.037 seconds
1 tests, 0 failures
```

続いて、ドキュメント[^running-the-project]に従って進めます。

[^running-the-project]: https://gleam.run/writing-gleam/running-the-project/

コマンド`hello:hello_world().`を入力すると、関数を直接実行します。
[Ctrl-C]を二回入力すると、シェルが終了します。

```sh:コマンドライン
$ rebar3 shell
===> Verifying dependencies...
Compiling hello
Compiling gleam/option
･･･（省略）･･･
Compiling gleam/uri
Done!
===> Analyzing applications...
===> Compiling hello
Erlang/OTP 22 [erts-10.5.4] [source] [smp:4:4] [ds:4:4:10] [async-threads:1] [hipe]

Eshell V10.5.4  (abort with ^G)
1> hello:hello_world().
<<"Hello, from hello!">>
2> 
BREAK: (a)bort (c)ontinue (p)roc info (i)nfo (l)oaded
       (v)ersion (k)ill (D)b-tables (d)istribution
^C
$
```

`Hello, from hello!`が表示出来ました。

ソースファイルはこちらに生成されています。
`hello/src/hello.gleam`
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/145581/9363f22a-eece-2ac9-aa47-2c5ff87d9929.png)


##(2)Webサーバ

Gleamのトップページに紹介されているソースコード、[Examples/A basic webserver](https://gleam.run/) を試します。

プロジェクトを作成します。

```sh:コマンドプロンプト
$ cd gitwork/

#プロジェクトを作成
$ gleam new basicwebserver
* creating basicwebserver/LICENSE
* creating basicwebserver/.gitignore
* creating basicwebserver/README.md
* creating basicwebserver/gleam.toml
* creating basicwebserver/test/basicwebserver_test.gleam
* creating basicwebserver/src/basicwebserver.gleam
* creating basicwebserver/.github/workflows/test.yml
* creating basicwebserver/rebar.config
* creating basicwebserver/src/basicwebserver.app.src

Your Gleam project "basicwebserver" has been successfully created.
The rebar3 program can be used to compile and test it.

    cd basicwebserver
    rebar3 eunit

$ cd basicwebserver/
```

ソースコードを追記します。

```rust:src/basicwebserver.gleam
import gleam/http/elli
import gleam/http
import gleam/bit_builder

pub fn my_service(_req) {
  let body = bit_builder.from_string("Hello, world!")

  http.response(200)
  |> http.set_resp_body(body)
}

pub fn start() {
  elli.start(my_service, on_port: 3000)
}

//　↑　ここまで追記

//　↓　自動的に生成されている箇所
pub fn hello_world() -> String {
  "Hello, from basicwebserver!"
}
```

依存関係のあるライブラリを追記します。
Elixirでお馴染みの`mix.exs`のような書き方ですね。

```conf:rebar.config
{erl_opts, [debug_info]}.
{src_dirs, ["src", "gen/src"]}.

{profiles, [
    {test, [{src_dirs, ["src", "test", "gen/src", "gen/test"]}]}
]}.

{project_plugins, [rebar_gleam]}.

{deps, [
    {gleam_stdlib, "~> 0.11"}, //追記
    {gleam_http, "~> 1.1"},    //追記
    {gleam_elli, "~> 0.1"},    //追記
    elli
]}.
```


実行

```sh:コマンドプロンプト
$ rebar3 shell
===> Verifying dependencies...
Compiling gleam/otp/basic_supervisor
Compiling gleam/otp/supervisor
･･･（省略）･･･
===> Analyzing applications...
===> Compiling basicwebserver
Erlang/OTP 22 [erts-10.5.4] [source] [smp:4:4] [ds:4:4:10] [async-threads:1] [hipe]

Eshell V10.5.4  (abort with ^G)
1> basicwebserver:start().
{ok,<0.221.0>}
2>
```

シェルから実行した後、ブラウザでアクセスします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/145581/5e1c045d-00af-1d5c-3a28-be7c203a707e.png)

##(3)マルチスレッド

引き続き、[Examples/Multi-threaded hello world](https://gleam.run/) を試します。

> ※プロジェクトの設定が上手くいかずコンパイルできない･･･省略

代わりにこちらを試してみました。
[Echo](https://github.com/gleam-lang/example-echo-server)

```
#ソースをクローン
$ git clone https://github.com/gleam-lang/example-echo-server.git
$ cd example-echo-server/

#コンパイルと実行
$ rebar3 shell
･･･（省略）･･･
===> Analyzing applications...
===> Compiling echo
Erlang/OTP 22 [erts-10.5.4] [source] [smp:4:4] [ds:4:4:10] [async-threads:1] [hipe]

Eshell V10.5.4  (abort with ^G)
1> ===> Booted echo

```


#４．まとめ

今回はとりあえず、サンプルのコンパイルまでを試してみました。
まだこれからという所ではあるようですが、今後の成長が楽しみな言語です。

（[Hex](https://hex.pm/)やElixirとの連携もできるようなので、引き続き試してみたいと思います）



#５．その他

細々とした覚え書き。

##(1)古いRustではGleamのビルドができない

Raspbian Busterの`apt`でインストールできるRustのバージョンは、`1.34`でした。
こちらの`rustc`で、gleamをコンパイルしたとき、下記のメッセージが大量にでてしまいました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/145581/3dc17e7e-8ef7-dc3e-f0fa-e001dc6dd065.png)

```
･･･（省略）･･･
'futures_api': futures in libcore are unstable (see issue #50547)
･･･（省略）･･･
```

この辺、良く解らないので、Rustの最新版（執筆時点は`1.47`）をソースからビルドし直しました。
これを使うと、Gleamのビルドが通りました。

##(2)VS Codeの拡張機能

`Gleam Syntax`という拡張機能が、既に用意されてます。（すばらしい！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/145581/db09aca8-b42a-ccee-16a9-1bb13e9590de.png)

コードのハイライト状態

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/145581/db10bcb1-902c-1a50-07d7-8e81d0822f2d.png)


##(3)アップデート

最新版（例ではVer.0.14.0）にアップデートします。

```bash
$ cd gitwork/rust/gleam

#ブランチ名を指定してpull
~/gitwork/rust/gleam $ git pull origin v0.14.0

remote: Enumerating objects: 524, done.
remote: Counting objects: 100% (524/524), done.
remote: Total 1598 (delta 524), reused 524 (delta 524), pack-reused 1074
Receiving objects: 100% (1598/1598), 664.41 KiB | 3.10 MiB/s, done.
Resolving deltas: 100% (1131/1131), completed with 82 local objects.
From https://github.com/gleam-lang/gleam
 * tag               v0.14.0    -> FETCH_HEAD
Updating 374e199..3e044d4
Fast-forward
 .gitattributes                                                    |    3 +
 .github/workflows/ci.yaml                                         |   46 +-
（･･･省略･･･）

#ビルド
$ make build && make install
cargo build --release
    Updating crates.io index
（･･･省略･･･）
    Finished release [optimized] target(s) in 34m 17s

#バージョン確認
~/gitwork/rust/gleam $ gleam
gleam 0.14.0

```


#参考資料