Gleam入門

毎年1つ新しい言語を学ぶべし，という[達人プログラマー](https://www.amazon.co.jp/dp/4274226298)に出てくる教えを今年は実践できていなかったので，気になっていた[Gleam](https://gleam.run/)を調べてみることにしました．

https://gleam.run/

Gleamは関数型プログラミング言語でErlang VM(BEAM)上で動作します．
BEAMのエコシステムを活用できるため，並行処理に優れている言語です．
また，JavaScriptにもコンパイル可能なため，ブラウザやモバイルデバイスなどJavaScriptの実行環境があればどこでも動作させることが可能です．

## Hello, World!
簡単にGleamの環境構築とプログラムの実行をやってみます．
Macの場合の環境構築方法を記載しています．他OSについては[公式ドキュメント](https://gleam.run/getting-started/installing/)を参照ください．

### パッケージのインストール

```bash
# Gleamのインストール
brew update
brew install gleam

# Erlangのインストール
# ※GleamはBEAMで動作するのでErlangのインストールが必要
brew install erlang
```

### プロジェクト作成と動作確認

```bash
# プロジェクト作成
gleam new sample

# 動作確認
cd sample
gleam run
```

出力
```text
> Hello from sample!
```

以上でハロワができました．

## 言語仕様
構文はかなり[Rust](https://www.rust-lang.org/ja)に似ています．
下記は[Gleam Lanugage Tour](https://tour.gleam.run/)からの抜粋が主なので，詳しくはそちらを参照ください．

### 文字列

```
import gleam/io
import gleam/string

pub fn main() {
  io.println("Hello, World!")
  
  // \を前につけることでダブルクオーテーションのエスケープが可能
  io.println("\"X\" marks the spot")

  // 文字列結合
  io.println("One " <> "Two")

  // String functions
  io.println(string.reverse("1 2 3 4 5"))
  io.println(string.append("abc", "def"))
}

```

### 数値
標準ライブラリでは整数`Int`と浮動小数点数`Float`がサポートされています．

```
import gleam/io

pub fn main() {
  // Int
  io.debug(1 + 1)
  io.debug(5 - 1)

  // Float
  io.debug(1.0 +. 1.0)
  io.debug(5.0 -. 1.0)
}
```
オペレータがオーバロードされていないため，`Int`と`Float`用に専用の演算子が定義されています．

※ 標準の `io.println` は`String`しか受け付けていないため，どんな型でも出力が可能な`io.debug`を利用しています


### 変数定義
Gleamは全ての変数がimmutableなので，変数の値を変更することはできません．`let`を使うことで，既存の値を使いつつ新しい変数を宣言することはできます．

また変数名の後にコロン `:` で型アノテーションをつけることができます．

```
import gleam/io

pub fn main() {
  let x: String = "Original"
  io.debug(x)

  // Assign `y` to the value of `x`
  let y: String = x
  io.debug(y)

  // letで新しく変数を宣言しない代入はコンパイルエラー
  // x = "Illegal Assignment"

  // Assign `x` to a new value
  let x: String = "New"
  io.debug(x)

  // The `y` still refers to the original value
  io.debug(y)
}
```


### モジュール

```
src/
  mymodule/
    math.gleam
  sample.gleam
```

まずはライブラリコードを定義してみます．

```gleam:math.gleam
pub fn add(left: Int, right: Int) -> Int {
  left + right
}
```

利用者側からは `src` ディレクトリからの位置でインポートが可能です．
また，`.{関数名}`をモジュール名の後ろにつけることで，呼び出時にモジュール名を省略することができます．

```gleam:sample.gleam
import gleam/io.{debug}
import mymodule/math.{add}

pub fn main() {
  debug(add(1, 2))
}
```

### List
配列は `[elem1, elem2, ...]` の形式で定義できます．
```
import gleam/io

pub fn main() {
  let ints = [1, 2, 3]

  io.debug(ints)

  // Immutably prepend
  io.debug([-1, 0, ..ints])

  // Uncomment this to see the error
  // io.debug(["zero", ..ints])

  // The original lists are unchanged
  io.debug(ints)
}
```

### 高階関数
Gleamでは関数は第一級オブジェクトです．すなわち，他の値と同じく変数として定義したり，引数や返り値として定義することが可能です．
```
import gleam/io

pub fn main() {
  let add_one = fn(a) { a + 1 }

  // Call a function with another function
  io.debug(twice(1, add_one))

  // Functions can be assigned to variables
  let my_function = add_one
  io.debug(my_function(100))
}

fn twice(argument: Int, passed_function: fn(Int) -> Int) -> Int {
  passed_function(passed_function(argument))
}
```

### ジェネリクス
関数の中で型変数を使うと，呼び出し時のパラメータに応じて型が変わる関数が定義できます．
型変数はlowercaseでないとコンパイルエラーになります．

```
import gleam/io

pub fn main() {
  let add_one = fn(x) { x + 1 }
  let exclaim = fn(x) { x <> "!" }

  // Invalid, Int and String are not the same type
  // twice(10, exclaim)

  // Here the type variable is replaced by the type Int
  io.debug(twice(10, add_one))

  // Here the type variable is replaced by the type String
  io.debug(twice("Hello", exclaim))
}

// The name `value` refers to the same type multiple times
fn twice(argument: value, my_function: fn(value) -> value) -> value {
  my_function(my_function(argument))
}
```

### パイプライン
関数の結果に対して，さらに関数を呼び出すための機能です．
他のプログラミング言語だと，関数の呼び出しが深くなるとネストが深くなってしまいますが，Gleamの場合はこのパイプラインのおかげで可読性を保ったまま関数の呼び出しが可能になっています．

パイプラインオペレータ（`|>`）の左にある関数の結果が，その右の関数の一番目の引数に渡されます．アンダースコア（`_`）を使うことで，代入する引数の位置を指定できます．
```
import gleam/io
import gleam/string

pub fn main() {
  // Without the pipe operator
  io.debug(string.drop_start(string.drop_end("Hello, Joe!", 1), 7))

  // With the pipe operator
  "Hello, Mike!"
  |> string.drop_end(1)
  |> string.drop_start(7)
  |> io.debug

  // Changing order with function capturing
  "1"
  |> string.append("2")
  |> string.append("3", _)
  |> io.debug
}
```

### 型定義
独自の型定義は下記のように宣言できます．
```
import gleam/io

pub type Season {
  Spring
  Summer
  Autumn
  Winter
}

pub fn main() {
  io.debug(weather(Spring))
  io.debug(weather(Autumn))
}

fn weather(season: Season) -> String {
  case season {
    Spring -> "Mild"
    Summer -> "Hot"
    Autumn -> "Windy"
    Winter -> "Cold"
  }
}

```

ジェネリクスを使うことも可能です．下記でいう `inner` が型変数を表しています．
```
pub type Option(inner) {
  Some(inner)
  None
}

// An option of string
pub const name: Option(String) = Some("Annah")

// An option of int
pub const level: Option(Int) = Some(10)

```

### Record
タイプの中にデータを持てるようにしたものを`Record`と呼びます．
```
import gleam/io

pub type SchoolPerson {
  Teacher(name: String, subject: String)
  Student(String)
}

pub fn main() {
  let teacher1 = Teacher("Mr Schofield", "Physics")
  let teacher2 = Teacher(name: "Miss Percy", subject: "Physics")
  let student1 = Student("Koushiar")
  let student2 = Student("Naomi")
  let student3 = Student("Shaheer")

  let school = [teacher1, teacher2, student1, student2, student3]
  io.debug(school)
}
```

定義した`Record`内のデータは下記のように参照・更新が可能です．
```
import gleam/io

pub type SchoolPerson {
  Teacher(name: String, subject: String)
}

pub fn main() {
  let teacher = Teacher("Mr Schofield", "Physics")

  io.debug(teacher.subject)

  let teacher2 = Teacher(..teacher, subject: "PE")

  io.debug(teacher2.subject)
}
```


というわけで，基本的なGleamの言語仕様を抜粋してみました．QiitaのコードスニペットだとGleamのコードシンタックスがサポートされていないので，かなり見辛くなっちゃいますね．．

次は何かしら簡単なアプリでも作ってみようと思います．