Gleamのサンプル「multi_threaded_hello_world」を試す
Gleamの本家TOPページにある「multi_threaded_hello_world」を動かしてみる。
まずは、プロジェクト作成。
$ 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
ソースコードをコピペ。
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なのだろう。
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"
gleam/otp/process
を利用するために、モジュールを追加する。
gleam/otp/process
を利用するには、gleam add
でgleam_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"
が増えている。
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
$
うむ、動いた。
備忘:
モジュールに対して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.
HelloWorld
の部分を、FizzBuzz
に変えてみることにする。
事前に、FizzBuzzの関数を作っておく。
ついでにテストもしたいので、moduleとして別途用意する形式にしてみる。
まず、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のテストコードも書いとく。
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
に変えてみる。
〜(略)〜
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
うーん、テストコードの行番号あたりが出てくれると嬉しいかも。
FizzBuzz関数ができたので、mainの処理を置き換えてみる。
もとのコードからは少し実行数を減らし100程度に。
また、出力分を作る箇所も、string.append
からstring.concat
に変更。
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
は抜いとこう。
とはいえ、パラで動いているのを確認。
そういえば、「ふぃずばずお嬢様」ってのを見かけたっけ。
ルールがよくわからないから、語尾に「ですわ〜」「ですのよ〜」くらい、つけておこうかな。
odd/evenでの切り分けでいいや。
〜(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
テストが通りましたわー。
語尾をつけ、微妙に出力メッセージを変える。
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ですわ〜
〜(略)〜