関数型言語
Elm
adventcalendar2017
ElmDay 16

Elm初心者がElm Packagesを使えるようになるまで

ごあいさつ

皆さんはじめまして。@enmaと申します。
Elmに興味があってここをご覧になられている方と、そうでない方もおられると思いますが、Elm Advent Calendar 2017の16日目の記事です。
一応断っておきますと、筆者はこれまで関数型言語での実装経験はありませんし、Webページ制作の経験もほとんどありません。しかしながら、Elmは純粋関数型言語としての魅力があり、またWebページ作成の観点からもかなり実用的な言語であると思いました。
初心者なりにElmの魅力を伝えられれば、更なるコミュニティの拡大にも繫がりますし、今回僭越ながら「Elm初心者がElm Packagesを使えるようになるまで」というタイトルで書かせて頂いております。どうぞ続けてご覧頂けると嬉しいです。

Elmことはじめ

筆者はこれまで関数型プログラミングに縁が無く、@ababup1192さんの記事をきっかけにElmの学習を始めました。今回は、これまで筆者が学習した内容と、Elmのコードを動作させる環境構築の手順を説明します。その後、Ellieと呼ばれるオンライン上のWebサービスを使って、Elmのプログラムを動作させるまでが記事の内容となります。

Elmは、HTMLやJavaScript、CSSで構成されたWebアプリケーションを、Elmのみで簡単に生成することができるプログラミング言語です。
elm-makeを実行することで、Elmで書かれたファイル(.elm)の依存関係を解決して(すなわちelm-package.jsonの作成が為されて)コンパイルが行われ、HTMLやJavaScriptのファイルを生成することが可能です。
筆者は恥ずかしながらHTMLやJavaScriptが使えないので、Elmさえ覚えれば(痒い所に手は届くかは置いておいて)Webアプリが作れるという点はかなり魅力的です。
※ CSSに関しては、例えばelm-cssがありました。また新しく出された@arowMさんの記事も参考になりそうです。

また、Elmは以下のような特徴を持っています。

・純粋関数型言語であり、静的型付き言語である
・型についてはかなり厳密なイメージ(その分実行時エラーが起こりにくい)
・他言語と比較して、シンプルなコード記述で求める機能の実装が可能
・型推論、型検査の機能を持ち、Haskell由来の強力なパターンマッチを有する

Elmは現在、The Elm Architectureという設計思想に則りWebアプリケーションを構成するのが定石とされています。リリースバージョンはまだ0.18ということもあって、破壊的なパラダイムシフトが行われる可能性もありますが、The Elm Architectureはシンプルかつ明快な思想であることから、廃止される可能性は低いと思っています。

The Elm Architectureは、beginnerProgramを使う方針とprogramを使う方針の2通りがあります(詳しくはElm PackagesのProgramの項を参照)。筆者は取っ掛かりとして、今回はbeginnerProgramを使う方法を中心に学習しました。
beginnerProgramを使用するには、model/view/msg/updateという4種類の関数と型を用意します。

beginnerProgram
beginnerProgram
    :  { model : model, view : model -> Html msg, update : msg -> model -> model }
    -> Program Never model msg

それぞれの関数/型のイメージは、Webアプリケーションの場合
model:Webアプリを構成する要素を定義し、それらの状態を示す(したがって初期状態の定義が必要)
view:modelをhtmlに変換する関数
msg:何らかのイベント(例えばボタンクリック)を示す
update:msgが投げられた際に、msgと元のmodelから新たなmodelを作成する

です。Elmの構文はHaskellライクのようですが、Haskell未経験の方はおそらく私同様、少し面食らうでしょう。まずは、Elmの文法/構造についてWeb上の入門記事を見ながら、目で追って確認していくのが良いです。

文法学習にはおそらくElmチートシートを見るのが手っ取り早いですが、英語に抵抗のある方はElmチュートリアル(日本語版)の基本の項や、プログラミング言語Elmの薄い本のPartIIの項を眺めてみてください。
その後、例えば@mather314さんの記事「Elm 0.18 入門 (1)カウントアプリを作ってみる」を読めば導入が割とスムーズです。

更には、Elm Packages内のPopular Packagesを徐々に見ていくとElmはデフォルトで豊富な機能を用意してくれている事が分かります。パッケージをimportすれば、パッケージの中で既に用意されている関数を利用することが出来ます。
Elm Core LibrariesHTML for Elmはおそらく必修に近いレベルであり、Webページ作成を行う際にこれらは使いこなせるようになっておく必要があると感じました。

Elmの文法については割愛させて頂きますが、個人的に少し悩み、つまづきポイントだと感じた点を以下にいくつか挙げました。
➀ typeとtype aliasの使い所のイメージ
➁ Elm Packagesの個々のパッケージの中で定義されている関数の見方
➂ 複数の型や値を持つものから、特定のものを取り出す方法

➀ typeとtype aliasの使い所のイメージについて
typeは新しい型を定義したい時に使う、type aliasはレコード型の定義もしくは既存の型に別名を付けたい時に使う、ということで意味は理解できるのですが、同じ"type"という語を使っていることもあり少し混乱しました。混乱の元となったのはおそらく、ユニオン型(Union Types)があるせいです。ユニオン型は、以下のように型の集約が可能です。

ユニオン型による定義
type Fruits = Orange | Melon | Apple

コード内の|記号が"OR"という意味ではないということに注意してください。上の例は、FruitsOrangeMelonAppleのいずれかの値を取り得るという定義です。ユニオン型は列挙した値のいずれかになるというイメージをしっかりと持った方が、理解がしやすくなると思いました。

ユニオン型ではない型定義の場合、

typeで新たな型を定義する
--type 型名 = データ構築子 (定義に必要な既存の型)...
type Msg = Get String

のように、型名とデータ構築子は大文字から始まる必要があること、必ず他と被らない新しいデータ構築子も定義される必要があることに注意して新たな型を定義してください。

また、type aliasで定義された型変数は以下のようなイメージを持つと分かりやすいです。

型エイリアス
-- 整数型 Intに"金額"という意味を付加することで扱いやすくする
type alias Amount =  Int


-- レコード型を用いて"Player"型に"id"と"name"という複数の属性を付加させる
type alias Player =
    { id : Int
    , name : String
    }

➁ Elm Packagesの個々のパッケージの中で定義されている関数の見方について
ひどいものだと->が何個も登場するものがあります。

Html.Lazy
lazy3 : (a -> b -> c -> Html msg) -> a -> b -> c -> Html msg

どんなに->が多くても結局、戻り値は一つしかないので
・右端の型にたどり着くまでの型は全て引数とみなす
・パイプ演算子や部分適用などを駆使して、戻り値が右端の最後の型になるように -> で繋げて型を定義する
という見方が良いかなと思われます。

➂ 複数の型や値を持つものから、特定のものを取り出す方法について
名前付き関数の定義の際、最初の行には関数のシグネチャを書きます(関数の意図を明確にするために記述することを推奨)。次の行で関数名 引数1 引数2 ... = 式という構造を用いて、関数内の全体をifで切り分ける代わりに、特定の引数を指定して関数を分割定義できます。このような書き方はパターンマッチと呼ばれ、意外と習熟が必要なところで難しいです。

パターンマッチの仕方
type alias Amount =
    Int


type Dollar
    = Dollar Amount


-- "Dollar"型から"Amount"型を取り出すmoney関数
money : Dollar -> Int
money (Dollar amount) =
    amount

case式によるパターンマッチ以外にも、上記のように引数によるパターンマッチも可能です。money関数の定義の2行目で、"amount"という引数を用いて"Dollar"型から"Amount"型を取り出すことを実現しています。上記を書くに当たり、@ababup1192さんの記事をはじめ、株式会社ガラパゴスさんの記事@7shiさんの記事も参考にさせて頂きました。

type A = Hoge Intの形なら引数のパターンマッチが可能だが、type B = Hoge Int | Foo Stringの場合case式を必ず使わないといけないので注意すべき、とのご指摘を@ababup1192さんから頂きました。ありがとうございます。

コードの実行環境について

それでは、実際にコードを実行する環境についての話に進みます。
Elmのコードを実行するには、まずNode.jsのインストールが必要です。Node.jsはサーバーサイドJavaScriptの実行環境です。Node.jsはNode.jsのモジュールを管理するためのツール「npm」を用いてnpm iでインストールします(npm iが通らない場合、npm自体のヴァージョンが古い可能性があります。筆者はnpmのバージョンが古かったのでnpm install -g npmで上げました)。
また、Elmのヴァージョンは0.18.0です(Elm -vコマンドで確認できます)

※1.筆者の使用PCはSurface Pro4、OSはWindows10 Pro RS2です
※2.万が一npmが使えない場合、MacとWindowsは、公式サイトからインストーラが提供されていますのでそちらを使用してください

インストールできたかどうかは、コマンドライン上でelm -velm make -hを叩いたりして、何らかの実行結果が返ってくれば成功です。筆者の環境ではGit bash、Command prompt、WSL(Windows Subsystem for Linux)でいずれも動作可能でした。

elm-package install elm-lang/htmlHtmlパッケージ(パッケージはPythonで言うところのモジュールのイメージです)をインストールした後、適当なコード(例えば以下)を書いてelm-make hello.elmを実行すればコンパイル成功です。

hello.elm
import Html exposing (text)

main =
  text "Hello World"

また、記述したコードが特定のパッケージに依存している場合、elm-package.jsonが置いてあるフォルダ上でelm-package installを実行すれば、elm-package.jsonに記述してある依存ライブラリを自動でインストールしてくれます。

コードの記述環境について

Elmを書くエディタはVisual Studio Codeを使用しています。VS CodeにはElmプラグインがあり、導入することでElmが非常に見やすく、書きやすくなります。中でもelm-formatという機能はElmの独特な(or Haskellライクな)コードの整形を半自動で行ってくれるのでおすすめです。
npmが使えれば導入は問題ないと思われますが、例えばプロキシでネットワークが制限されている場合、npmを使ったインストールができないことがあります。Windowsの場合は、elm-format.exeがリリースされています(以下はファイルのリンクです)
https://github.com/avh4/elm-format/releases/download/0.7.0-exp/elm-format-0.18-0.7.0-exp-win-i386.zip
elm-make.exeelm-repl.exeがあるフォルダにelm-format.exeを格納したあと(筆者はGit bashを入れていたので、VS Code上でwhichコマンドを実行してelm-make.exeが置いてある場所を確認しました)、ワークスペース設定のパス指定のところで

"elm.formatCommand":"elm-format"

と記述すれば、新たにパスを通さずともShift+Alt+Fコマンドによるコード整形が機能するようになります。

書いたコードの実行結果は、VS Codeでも確認することは出来ますが、他にもElmに標準で用意されているelm-reactor(HTTPサーバーを通して、書いたプログラムの実行結果をブラウザを見ながら確認できる)や、対話環境としてのelm-repl(式を実行して値や型の確認が可能)があります。

また、筆者は勉強する中でEllie というElm専用のWebサービスがあることを知りました。
Ellieはオンライン上のElmのテキストエディタで、コードを書いてコンパイルすることで、Webページがどのように表示されるか確認することができます。Elmのパッケージ(Elm Packages)も追加できるので、小規模な開発環境としては有用だと思います。Pythonで言うところのJupyter Notebookに近い使い勝手の良さですね。詳しくはElm 0.18 入門 (1)カウントアプリを作ってみるのページを参考にしてください。

Ellieを使ってElm Packagesのコードを実行してみる

試しにelm-colorpickerというパッケージを使ってコードを書き、コンパイルを通してみましょう。Elm Packagesには有志が作成したパッケージが数多くあり、elm-colorpickerもその中の一つです(日本だとジンジャーさんという方が数多くパッケージを公開されています(http://jinjor-labo.hatenablog.com/)

GoogleChromeでEllieを開いた後(筆者の環境では、IEだとうまく動作しませんでした)、左側のSearch for packagesでelm-colorpickerと入力し、パッケージをインストールしてください。その後、Elm PackagesのSearchにelm-colorpickerと入力し、パッケージの説明ページを見てください。Exampleという項におよその使い方が書かれてあります。これを参考にしながら、beginnerProgramを使ってEllie上でプログラムを実行してみましょう。
誤植を訂正しつつ、beginnerProgramを使ってEllie上で最低限動作するプログラムを以下に書きました。

elm-colorpicker
module Main exposing (..)

import ColorPicker exposing (State, empty, view)
import Html exposing (Html, beginnerProgram)
import Color


main : Program Never Model Msg
main =
    Html.beginnerProgram
        { model = init
        , update = update
        , view = view
        }


type alias Model =
    { colour : Color.Color
    , colorPicker : ColorPicker.State
    }


init : Model
init =
    { colorPicker = ColorPicker.empty
    , colour = Color.rgb 255 0 0
    }


type Msg
    = ColorPickerMsg ColorPicker.Msg


update : Msg -> Model -> Model
update message model =
    case message of
        ColorPickerMsg msg ->
            let
                ( m, colour ) =
                    ColorPicker.update msg model.colour model.colorPicker
            in
                { model
                    | colorPicker = m
                    , colour = colour |> Maybe.withDefault model.colour
                }


view : Model -> Html Msg
view model =
    ColorPicker.view model.colour model.colorPicker
        |> Html.map ColorPickerMsg

ページの右上辺りにカラーピッカーが現れればコンパイル成功です。
本当は、Googleのcolorpickerのように、コンターだけではなくカラーコードも表示できるようにしたかったのですが、経験不足で今回は実装できませんでした。今後の課題とさせて頂きます。

あとがき

Elm初心者として、Elm導入からサンプルコード実行までの流れを記事にしてみました。The Elm Architectureという分かりやすい設計思想を持ち、関数型言語として簡潔な記述が可能であるという点は十分魅力的なポイントです。教育的かつ実用的であり、周りの方にも勧めやすい言語である気がします。Elmをベースにして、関数型プログラミングについての知識を更に深めていきたいです。ここまで読んで頂き、ありがとうございました!