毎年1つ新しい言語を学ぶべし,という達人プログラマーに出てくる教えを今年は実践できていなかったので,気になっていたGleamを調べてみることにしました.
Gleamは関数型プログラミング言語でErlang VM(BEAM)上で動作します.
BEAMのエコシステムを活用できるため,並行処理に優れている言語です.
また,JavaScriptにもコンパイル可能なため,ブラウザやモバイルデバイスなどJavaScriptの実行環境があればどこでも動作させることが可能です.
Hello, World!
簡単にGleamの環境構築とプログラムの実行をやってみます.
Macの場合の環境構築方法を記載しています.他OSについては公式ドキュメントを参照ください.
パッケージのインストール
# Gleamのインストール
brew update
brew install gleam
# Erlangのインストール
# ※GleamはBEAMで動作するのでErlangのインストールが必要
brew install erlang
プロジェクト作成と動作確認
# プロジェクト作成
gleam new sample
# 動作確認
cd sample
gleam run
出力
> Hello from sample!
以上でハロワができました.
言語仕様
構文はかなりRustに似ています.
下記はGleam Lanugage Tourからの抜粋が主なので,詳しくはそちらを参照ください.
文字列
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
まずはライブラリコードを定義してみます.
pub fn add(left: Int, right: Int) -> Int {
left + right
}
利用者側からは src
ディレクトリからの位置でインポートが可能です.
また,.{関数名}
をモジュール名の後ろにつけることで,呼び出時にモジュール名を省略することができます.
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のコードシンタックスがサポートされていないので,かなり見辛くなっちゃいますね..
次は何かしら簡単なアプリでも作ってみようと思います.