Open6
gleamメモ
echo ( 1 + 1) * 1
はerror
()ではなく{}をつかう
echo { 1 + 1 } * 1
// => 2
debug print はio.debug ではなく、echoを使用する。
現在、標準ライブラリにio.debugはない。
v1.9.0のアップデート時の echo 導入により削除された?
lustreで開発中にブラウザAPIが必要になったら、plinthライブラリを使えば大体何とかなる。
ただ、uuid生成はないので他のライブラリを使うか、externalを利用して自分で作る。
以下参考
//ffi.mjs
export function getUuid() {
return self.crypto.randomUUID()
}
@external(javascript, "./ffi.mjs", "getUuid")
pub fn get_uuid() -> Uuid
rsvpライブラリのhandler作り方sample
stringの場合
fn txt_handler(decoder: Decoder(a), tomsg: fn(Result(a, rsvp.Error)) -> msg) {
fn(input){txt_handler_helper(input,decoder)|> tomsg}
|>rsvp.expect_text
}
fn txt_handler_helper(
input: Result(String, rsvp.Error),
decoder: Decoder(a),
) -> Result(a, rsvp.Error) {
result.try(input, fn(json_string) {
decode.run(dynamic.string(json_string), decoder)
|> result.map_error(json.UnableToDecode)
|> result.map_error(rsvp.JsonError)
})
}
PromiseをEffectに変換する例:
pub type Msg {
PromiseSuccess(Nil)
PromiseFailure(String)
}
fn set_promise(promise: Promise(Result(Nil, String)), dispatch: fn(Msg) -> Nil) -> Nil {
promise
|> promise.map(fn(result) {
//resultをMsgに変換
case result {
Ok(Nil) -> PromiseSuccess(Nil)
Error(err) -> PromiseFailure(err)
}
})
|> promise.tap(dispatch)
Nil
}
/// Promise(Result(Nil, String))をEffect(Msg)に変換
fn promise_to_effect(promise: Promise(Result(Nil, String))) -> Effect(Msg) {
effect.from(set_promise(promise,_))
}
AIにGleamを書かせると、あまりうまくいかない。(elmやHaskellと混同している?)
AIに読み込ませる用のコーディングガイドを用意する。
GLEAM_CODING_GUIDLINE.md
# Gleam コーディングガイドライン
## 1. 基本的な言語機能と構文
- **不変性 (Immutability)**: Gleamのデータ構造はすべて不変です。データを変更する場合は、必ず新しいレコードやリストを作成してください。
- レコードの更新は `Model(..model, field: new_value)` のようにスプレッド構文を使います。
- フィールド名と変数名が同じ場合は `Model(..model, field:)` のように省略できます。
- **`if`文の代わりに`case`式**: 条件分岐には`if`文は存在せず、`case`式を使用します。
```gleam
case a > b {
True -> "a is greater"
False -> "a is not greater"
}
```
- **`use`構文の活用**: `Result`型や`Option`型を扱う際は、ネストした`case`式を避けるために`use`構文を積極的に利用してください。コードがクリーンになります。
```gleam
pub fn process_data() -> Result(Int, String) {
use value1 <- first_step()
use value2 <- second_step(value1)
third_step(value2)
}
```
- **型定義**: 型には`type`、外部に公開しない型には`opaque type`を使用します。型名は`PascalCase`で記述します。
- **関数ラベル**: 関数を呼び出す際は、引数にラベルを付けます。 `my_function(label: value)`
- **パッケージ関数の呼び出し**: `lustre/attribute`のようなパッケージから関数を呼び出す場合、`import`したモジュール名(通常はパスの最後の部分)を使って`attribute.type_()`のように呼び出します。
## 2. プロジェクト固有の規約
- **アーキテクチャ**: このプロジェクトは **Lustre** フレームワークを使用しており、TEA (The Elm Architecture) に従います。
- `Model`: コンポーネントの状態を定義します。
- `Msg`: ユーザーのアクションやイベントを定義します。
- `init`: `Model`の初期状態を返します。
- `update`: `Msg`を受け取り、`Model`を更新します。
- `view`: `Model`を元にUIを描画します。
- **命名規則**:
- 関数名、変数名: `snake_case` (例: `get_user_name`)
- 型名: `PascalCase` (例: `QuizAppModel`)
- **ディレクトリ構造**:
- 型定義は `src/types.gleam` に集約します。
- テストは `test/` ディレクトリに、元ファイルに対応する形で作成します。
## 3. ベストプラクティスとスタイル
- **パイプ演算子 `|>`**: 複数の関数を連続して適用する場合は、パイプ演算子 `|>` を使って可読性を高めてください。
```gleam
// 悪い例: a(b(c(d)))
// 良い例: d |> c |> b |> a
```
- **ドキュメンテーション**: 公開(`pub`)するすべての関数と型には、`///` から始まるドキュメンテーションコメントを記述してください。
- **パターンマッチ**: `case`式では、すべての可能性を網羅するようにしてください。Gleamコンパイラが網羅性をチェックしてくれます。
- **モジュール分割**: 1つのファイル(モジュール)は、1つの関心事に集中するようにしてください。
## 4. JavaScriptとの連携 (Promise)
GleamからJavaScriptのPromiseを扱う場合は `gleam/javascript/promise` モジュールを利用します。
- **`promise.map(Promise(a), fn(a) -> b) -> Promise(b)`**:
Promiseが成功した場合に、その結果に対して同期的な関数を適用します。新しい非同期処理は生成しません。
- **`promise.map_try(Promise(Result(a, e)), fn(a) -> Result(b, e)) -> Promise(Result(b, e))`**:
Promiseが `Ok` の結果を返した場合にのみ、後続の関数を適用します。`Error` の場合は関数をスキップし、エラーをそのまま伝播させます。
- **`promise.await(Promise(a), fn(a) -> Promise(b)) -> Promise(b)`**:
依存関係のある非同期処理を連結するために使用します。最初のPromiseの完了を待ってから、次のPromiseを返す関数を実行します。
- **`promise.try_await(Promise(Result(a, e)), fn(a) -> Promise(Result(b, e))) -> Promise(Result(b, e))`**:
`Result`を返すPromiseを連結します。前の処理が `Ok` の場合にのみ次の非同期処理を実行するため、失敗する可能性のある非同期処理を安全に繋げることができます。
- **`promise.tap(Promise(a), fn(a) -> Nil) -> Promise(a)`**:
Promiseチェーンの途中で、結果に影響を与えずに副作用を実行します。主にデバッグ目的で、中間値をコンソールに出力する際などに便利です。
- **サンプルコード**:
`use` 構文と組み合わせることで簡潔に記述できます。
```gleam
import gleam/javascript/promise.{type Promise}
import gleam/result
import gleam/js // js.logを使うために必要
type User = String
@external(javascript, "./user_api.mjs", "fetch_user")
fn fetch_user(id: Int) -> Promise(dynamic.Dynamic)
fn get_user_posts(id: Int, decoder: decode.Decoder(User)) {
use user <- promise.tap(fetch_user(id))
let decoded =
decode.run(user, decoder)
|> result.map_error(json.UnableToDecode)
echo decoded
Nil
}
```
`gleeunit`での非同期テストの例です。`map`や`await`を使って値を取り出し、`should`でアサーションを行います。
```gleam
// gleeunitでの非同期テストの例
@external(javascript, "./indexedDB_ffi.mjs", "setup")
pub fn setup(db_name: String) -> Promise(DB)
@external(javascript, "./indexedDB_ffi.mjs", "get")
fn get_users(db: DB) -> Promise(Dynamic)
pub fn get_question_by_id_test() {
use db <- promise.tap(setup("db_test"))
use dynamic <- promise.tap(get_users(db))
question.decode_question_list(dynamic)
should.be_ok
Nil
}
```
PromiseをEffectに変換する例:
```gleam
pub type Msg {
PromiseSuccess(Nil)
PromiseFailure(String)
}
fn set_promise(promise: Promise(Result(Nil, String)), dispatch: fn(Msg) -> Nil) -> Nil {
promise
|> promise.map(fn(result) {
//resultをMsgに変換
case result {
Ok(Nil) -> PromiseSuccess(Nil)
Error(err) -> PromiseFailure(err)
}
})
|> promise.tap(dispatch)
Nil
}
/// Promise(Result(Nil, String))をEffect(Msg)に変換
fn promise_to_effect(promise: Promise(Result(Nil, String))) -> Effect(Msg) {
effect.from(set_promise(promise,_))
}
```
## 7. FFI (Foreign Function Interface)
- GleamでFFIを作成する際、外部関数には`_ffi`の接尾辞をつけ、動的なデコードを処理するために接尾辞なしのラッパー関数を作成する必要があります。
- `echo`関数は括弧を必要としません。
## 6. テスト
テストに関する規約は `TESTING_GUIDELINE.md` を参照してください。