はじめに
この記事は限界開発鯖アドベントカレンダーの10日目の枠埋めに書かれた記事です。
そのため公開日時が12/10以降となっていますが埋めなかった連中が悪いので私は悪くないです。
また、筆者が関数型言語への造形が浅い人間なのでTSやRustなどの他の言語に無理やり解釈して喋っている部分があったり、眠気覚ましのために書いているせいで誤字脱字が目立つと思われますが生暖かい目で見ていただけると幸いです。
この記事はelm公式チュートリアルの焼き増しです。
ある日のこと
そうだ、関数型言語、やろう。
というわけでElm公式のチュートリアルの日本語訳があったため、それを見ながらElmの履修を始めました。
当時平日の午前2時だったため詳しくは覚えてませんがこんな流れだった気がします。
というわけで履修開始...?
さて、サンプルコードでも読むとしますか。
import Browser
import Html exposing (Html, button, div, text)
import Html.Events exposing (onClick)
main =
Browser.sandbox { init = 0, update = update, view = view }
type Msg = Increment | Decrement
update msg model =
case msg of
Increment ->
model + 1
Decrement ->
model - 1
view model =
div []
[ button [ onClick Decrement ] [ text "-" ]
, div [] [ text (String.fromInt model) ]
, button [ onClick Increment ] [ text "+" ]
]
...?
?
?
はい、サンプルコードが全くわかりません。
さすが関数型言語さんやで...
という訳で次の節から言語の基本要素を見ていきます。
ちなみに今回は基本的な文法事項について見ていくためなんとなく何書いてあるかは分かるようになると思いますが、実際にどうやってDOMを描画するのかなどについては公式チュートリアルを参照してください。
基本文法
値
まずプリミティブ型の値から見ていきます。
とは言ってもよくあるものと何ら変わりなさそうですね。
ただ、文字列結合は++
によるものなのでその点は注意が必要です。
> 1 + 1
2
> "Hello " ++ "World"
Hello World
関数
関数は以下の書式で定義されます。
ここで注意したいのが、記法で他の私が触ってきた言語と違って括弧がいらないんですよね。
[関数名] [引数...] = [戻り値]
if、リスト、タプル、レコードに関してはよくあるやつなので飛ばします。
型
Elmってやつは強い型付けの言語なので型機能をうまく利用すると幸せになれるらしいです。
という訳でここでは型について見ていきます。
プリミティブ・リスト・関数
こやつらはよくある感じにまとまってます。
"Hello": String
True: Bool
1: Int
1.0: Float
["Hoge", "Fuga"]: List String
<function>: String -> Int
また、関数が複数の引数を取る場合は以下のような形になります。
以下の例ではStringの引数2つを取ってIntを返す関数を表します。
String -> String -> Int
型変数・制約付き型変数
よく言うジェネリクスですね。
List elem -> List String
ただここで気をつける必要があるのが、型変数は小文字から始める必要があるらしいです。
そのため、他の言語のノリで以下の様に書くとコンパイルエラーになります。
List T -> List String
また、制約付き型変数では、number
というInt
とFloat
のみを利用できる型を使うことも可能なようで、以下が制約付き型変数の一覧となります。
number -- Float + Int
appendable -- String + List a
comparable -- Int + Float + Char + String + List comparable
compappend -- String + List comparable
型エイリアス・レコードコンストラクタ
よくある型エイリアスではあるのですが、構造体のようなものもこれで定義します。
type alias Hoge =
{ a: String
, b: Int
}
さて、このレコードで表現された型エイリアスなのですが、こいつはレコードコンストラクタというものを実装したことになっているらしいです。
そのため、いちいちレコードの記法で書かなくても下記の構文で作成することが可能です。
> Hoge "Hoge" 100
{ a = "Hoge", b = 100 }: Hoge
これ、いる?
列挙型
Elmでは列挙型を以下のように表せます。(公式チュートリアルでは「カスタム型」と表記されていますがどうみても高機能な列挙型なので列挙型としてここでは記載します。)
また、列挙されるそれぞれの値はバリアントと呼びます。
type Enum1 = A | B
type Enum2 = A | B String | C String { a: String, b: Int }
type Enum3 = D | E
type Enum4 = Enum2 | Enum3
注目するべきはEnum2です。
括弧内で「高機能な」列挙型と言ったのはここが理由で単純なバリアントの列挙だけでなく、値を受け取るバリアントもごちゃまぜにして一括処理することができます。
パッと見複雑すぎる言語機能に見えるのですが、次に紹介するパターンマッチと併せて利用することで強力なツールに化けます。
パターンマッチ
パターンマッチでは、列挙型のそれぞれのバリアントをキャプチャし、それに対する処理を個別に書くことができます。
記法としては以下のような形になり、Rustのmatch文によく似たものとなります。
type Enum1 = A | B String | C String { a: String, b: Int}
toString : Enum1 -> String
toString e =
case e of
A -> "A"
B str -> "B " ++ str
C str obj -> "C " ++ str ++ " " ++ obj.a ++ String.fromInt obj.b
このような形でパターンそれぞれに合った処理をそれぞれ行うことができます。
また、もちろんワイルドカードが存在し、一部のバリアントをまとめて処理したり、バリアントの一部を無視して処理することもできます。
type Enum1 = A | B String | C String { a: String, b: Int}
toString : Enum1 -> String
toString e =
case e of
A -> "A"
B _ -> "B"
_ -> "OTHER"
エラーハンドリング
この辺はRustにかなり話が近かった気がする。MayBeとResultサイコー!!!
Maybe
よくあるOptional型ってヤツです。他の言語で言うOption<T>
やT | null
にあたります。もしかしたらその値が無いかもしれないっていうのを表すのに使います。
定義はシンプルでこれだけです。
type Maybe a
= Just a
| Nothing
これを見れば分かる通りJust aとNothingに対しての列挙型なのでパターンマッチを利用して値を取り出すことが可能です。
Result
エラーが返ってくるかもしれないという型です。Golangの(T, err)
みたいなもんですね。なんならRustのResultと一緒です。
こちらも定義はシンプルで以下のようになります。
type Result error value
= OK value
| Err error
ここで注意したいのが、エラーが左、処理結果が右に入るという点ですね。これはよく言うRight(右、正しい)という掛詞のせいですね。わかりにくいのでやめてくれ
こいつも便利なことにパターンマッチが利用可能になっているため、実際に利用する際は相当お世話になることでしょう。その機会が来るかどうかは別として。
おわりに
さて、Elmのチュートリアル焼き増し記事を長々と書いてきたわけですが。
さすが枠埋め用記事なだけあって内容ペラッペラですね。普段からもう少し文章を書くなりアウトプット増やすなりしてネタを蓄えるクセをつけなければいけないというのを痛感しました。
Elmは風変わりではありますが、まだ触りやすく、SPAを作るなど関数型言語にしてはアウトプットに直接つながってくれるタイプの言語であるため今度また何かの機会があれば使ってみたいですね。
という訳で私は自分の担当記事の執筆に戻りたいと思います。また12/20に会いましょう。