Open7

Gleamのサンプル「multi_threaded_hello_world」を試す

MzRyuKaMzRyuKa

Gleamの本家TOPページにある「multi_threaded_hello_world」を動かしてみる。

https://gleam.run/

まずは、プロジェクト作成。

$ gleam new multi_threaded_hello_world
Your Gleam project multi_threaded_hello_world has been successfully created.
The project can be compiled and tested by running these commands:

	cd multi_threaded_hello_world
	gleam test

プロジェクトのディレクトリへ移動。

$ cd multi_threaded_hello_world

ソースコードをコピペ。

multi_threaded_hello_world.gleam
import gleam/io
import gleam/int
import gleam/list
import gleam/string
import gleam/otp/process

pub fn main() {
  list.range(0, 1000)
  |> list.map(start_process)
  |> list.map(process.monitor_process)
  |> list.each(process.receive(_, 3)) // Wait for them to finish
}

fn start_process(i) {
  process.start(fn() {
    let message = string.append("Hello world: ", int.to_string(i))
    io.println(message)
  })
}

何も考えずにgleam runで実行。

$gleam run
  Resolving versions
Downloading packages
 Downloaded 2 packages in 0.06s
  Compiling gleam_stdlib
  Compiling gleeunit
  Compiling multi_threaded_hello_world
error: Unknown module
  ┌─ ./src/multi_threaded_hello_world.gleam:5:8
  │
5 │ import gleam/otp/process
  │        ^^^^^^^^^^^^^^^^^ Did you mean `list`?

No module has been found with the name `gleam/otp/process`.

gleam/otp/processが見つからねーぞ」と怒られた。

gleam.tomlファイルのdependenciesを確認する。
gleam_stdlibしかない。
otpは別のmoduleなのだろう。

gleam.toml
name = "multi_threaded_hello_world"
version = "0.1.0"

# Fill out these fields if you intend to generate HTML documentation or publish
# your project to the Hex package manager.
#
# licences = ["Apache-2.0"]
# description = "A Gleam library..."
# repository = { type = "github", user = "username", repo = "project" }
# links = [{ title = "Website", href = "https://gleam.run" }]

[dependencies]
gleam_stdlib = "~> 0.22"

[dev-dependencies]
gleeunit = "~> 0.6"
MzRyuKaMzRyuKa

gleam/otp/processを利用するために、モジュールを追加する。

gleam/otp/processを利用するには、gleam addgleam_otpを追加する。

$gleam add gleam_otp
  Resolving versions
Downloading packages
 Downloaded 2 packages in 0.05s
      Added gleam_otp v0.4.0

もう一度、gleam.tomlファイルのdependenciesを確認する。
dependenciesの箇所にgleam_otp = "~> 0.4"が増えている。

gleam.toml
name = "multi_threaded_hello_world"
version = "0.1.0"

# Fill out these fields if you intend to generate HTML documentation or publish
# your project to the Hex package manager.
#
# licences = ["Apache-2.0"]
# description = "A Gleam library..."
# repository = { type = "github", user = "username", repo = "project" }
# links = [{ title = "Website", href = "https://gleam.run" }]

[dependencies]
gleam_stdlib = "~> 0.22"
gleam_otp = "~> 0.4"

[dev-dependencies]
gleeunit = "~> 0.6"

モジュールが追加されたので、もう一度実行っしたら動くはず。
gleam runで実行!

$gleam run
  Resolving versions
  Compiling gleam_erlang
  Compiling gleam_otp
  Compiling multi_threaded_hello_world
   Compiled in 4.48s
    Running multi_threaded_hello_world.main
Hello world: 783
Hello world: 786
Hello world: 789
Hello world: 0

〜中略〜

Hello world: 713
Hello world: 717
Hello world: 729
$

うむ、動いた。

MzRyuKaMzRyuKa

備忘:

モジュールに対してgleam addするつもりがgleam newしたら、以下のように怒られた。

This prefix is indended for official Gleam packages only.
(このプレフィックスはgleamの公式パッケージ用だよん)

怒られない作り方とかあるんかな?

$gleam new gleam_otp
error: Invalid project name

We were not able to create your project as `gleam_otp` has the reserved
prefix `gleam_`. This prefix is indended for official Gleam packages only.

Please try again with a different project name.
MzRyuKaMzRyuKa

HelloWorldの部分を、FizzBuzzに変えてみることにする。

事前に、FizzBuzzの関数を作っておく。
ついでにテストもしたいので、moduleとして別途用意する形式にしてみる。

まず、src下に、fizzbuzz/fizzbuzz.gleamを作成。

multi_threaded_fizzbuzz/src/fizzbuzz/fizzbuzz.gleam
import gleam/int

pub fn to_fizzbuzz(i) {
  case i % 3, i % 5 {
    0, 0 -> "FizzBuzz"
    0, _ -> "Fizz"
    _, 0 -> "Buzz"
    _, _ -> int.to_string(i)
  }
}

fizzbuzzのテストコードも書いとく。

multi_threaded_fizzbuzz/test/fizzbuzz/fizzbuzz_test.gleam
import fizzbuzz/fizzbuzz
import gleeunit/should

pub fn to_fizzbuzz_test() {
  fizzbuzz.to_fizzbuzz(1)
  |> should.equal("1")

  fizzbuzz.to_fizzbuzz(3)
  |> should.equal("Fizz")

  fizzbuzz.to_fizzbuzz(5)
  |> should.equal("Buzz")

  fizzbuzz.to_fizzbuzz(15)
  |> should.equal("FizzBuzz")

  fizzbuzz.to_fizzbuzz(40)
  |> should.equal("Buzz")

  fizzbuzz.to_fizzbuzz(42)
  |> should.equal("Fizz")

  fizzbuzz.to_fizzbuzz(43)
  |> should.equal("43")

  fizzbuzz.to_fizzbuzz(45)
  |> should.equal("FizzBuzz")
}

gleam testを実行してみる。

$gleam test
  Compiling multi_threaded_fizzbuzz
   Compiled in 1.33s
    Running multi_threaded_fizzbuzz_test.main
..
Finished in 0.016 seconds
2 tests, 0 failures

従来とは逆だが、今度はテストがred(NG)になることを試す。
15を渡している箇所の戻り値をBuzzFizzに変えてみる。

multi_threaded_fizzbuzz/test/fizzbuzz/fizzbuzz_test.gleam(一部修正)
〜(略)〜
  fizzbuzz.to_fizzbuzz(15)
  |> should.equal("BuzzFizz")
〜(略)〜

改めて、テストを実行。
想定通り、15の箇所でテストがNGとなった。

$gleam test
  Compiling multi_threaded_fizzbuzz
   Compiled in 1.46s
    Running multi_threaded_fizzbuzz_test.main
F.
Failures:

  1) fizzbuzz@fizzbuzz_test:to_fizzbuzz_test/0: module 'fizzbuzz@fizzbuzz_test'
     Failure: ?assertEqual(<<66,117,122,122,70,105,122,122>>, Actual)
       expected: <<"BuzzFizz">>
            got: <<"FizzBuzz">>
     %% eunit_proc.erl:505:in `eunit_proc:handle_test/2`
     Output: 
     Output: 

Finished in 0.023 seconds
2 tests, 1 failures

うーん、テストコードの行番号あたりが出てくれると嬉しいかも。

MzRyuKaMzRyuKa

FizzBuzz関数ができたので、mainの処理を置き換えてみる。

もとのコードからは少し実行数を減らし100程度に。
また、出力分を作る箇所も、string.appendからstring.concatに変更。

multi_threaded_fizzbuzz/src/multi_threaded_fizzbuzz.gleam
import gleam/io
import gleam/int
import gleam/list
import gleam/string
import gleam/otp/process
import fizzbuzz/fizzbuzz

pub fn main() {
  list.range(0, 100)
  |> list.map(start_process)
  |> list.map(process.monitor_process)
  |> list.each(process.receive(_, 3))
  // Wait for them to finish
}

fn start_process(i) {
  process.start(fn() {
    let fizzbuzz_msg = fizzbuzz.to_fizzbuzz(i)
    let message = string.concat([int.to_string(i), " is ", fizzbuzz_msg])
    io.println(message)
  })
}

gleam runを実行。

$gleam run
  Compiling multi_threaded_fizzbuzz
   Compiled in 1.33s
    Running multi_threaded_fizzbuzz.main
5 is Buzz
6 is Fizz
7 is 7
2 is 2
8 is 8
9 is Fizz
10 is Buzz
11 is 11
12 is Fizz
13 is 13
14 is 14
15 is FizzBuzz
16 is 16
22 is 22
24 is Fizz
25 is Buzz
26 is 26
27 is Fizz
28 is 28
29 is 29
30 is FizzBuzz
〜(略)〜
0 is FizzBuzz
1 is 1
3 is Fizz
4 is 4
〜(略)〜
98 is 98
68 is 68
69 is Fizz
$

あ、fizzbuzzで0は対象外だっけか。rangeから0は抜いとこう。
とはいえ、パラで動いているのを確認。

MzRyuKaMzRyuKa

そういえば、「ふぃずばずお嬢様」ってのを見かけたっけ。

https://blog.vtryo.me/entry/ojousama-fizzbuzz

ルールがよくわからないから、語尾に「ですわ〜」「ですのよ〜」くらい、つけておこうかな。
odd/evenでの切り分けでいいや。

multi_threaded_fizzbuzz/src/fizzbuzz/fizzbuzz.gleam
〜(fizzbuzzの処理は省略)〜

pub fn to_gobi(i) {
  case int.is_odd(i) {
    True -> "ですわ〜"
    False -> "ですのよー"
  }
}
```gleam:multi_threaded_fizzbuzz/test/fizzbuzz/fizzbuzz_test.gleam
〜(fizzbuzzのテスト部分は省略)〜

pub fn to_gobi_test() {
  fizzbuzz.to_gobi(1)
  |> should.equal("ですわ〜")

  fizzbuzz.to_gobi(2)
  |> should.equal("ですのよー")

  fizzbuzz.to_gobi(8)
  |> should.equal("ですのよー")

  fizzbuzz.to_gobi(9)
  |> should.equal("ですわ〜")
}

gleam testを実行

$gleam test
  Compiling multi_threaded_fizzbuzz
   Compiled in 1.23s
    Running multi_threaded_fizzbuzz_test.main
...
Finished in 0.018 seconds
3 tests, 0 failures

テストが通りましたわー。

MzRyuKaMzRyuKa

語尾をつけ、微妙に出力メッセージを変える。

multi_threaded_fizzbuzz/src/multi_threaded_fizzbuzz.gleam
import gleam/io
import gleam/int
import gleam/list
import gleam/string
import gleam/otp/process
import fizzbuzz/fizzbuzz

pub fn main() {
  list.range(1, 100)
  |> list.map(start_process)
  |> list.map(process.monitor_process)
  |> list.each(process.receive(_, 3))
  // Wait for them to finish
}

fn start_process(i) {
  process.start(fn() {
    let fizzbuzz_msg = fizzbuzz.to_fizzbuzz(i)
    let gobi_msg = fizzbuzz.to_gobi(i)
    let message =
      string.concat([int.to_string(i), " は ", fizzbuzz_msg, gobi_msg])
    io.println(message)
  })
}

実行。

$gleam run
  Compiling multi_threaded_fizzbuzz
   Compiled in 1.47s
    Running multi_threaded_fizzbuzz.main
1 は 1ですわ〜
2 は 2ですのよー
99 は Fizzですわ〜
4 は 4ですのよー
20 は Buzzですのよー
40 は Buzzですのよー
41 は 41ですわ〜
42 は Fizzですのよー
43 は 43ですわ〜
44 は 44ですのよー
45 は FizzBuzzですわ〜
〜(略)〜