LoginSignup
10
0

More than 1 year has passed since last update.

ElmのBrowserパッケージについて調べた

Last updated at Posted at 2021-12-04

プロジェクトにてフロントをElmで、バックエンドは Haskell を使うことになりました。

An Introduction to Elm

等を参考に勉強中です。

今回は自分の中での整理も含め、Browser パッケージについてまとめます。

結論 Browserは目的に合わせて関数を選ぶ

Browserの4つの関数はそれぞれ引数レコードの型が異なります。
例えば既存のアプリケーションにElmで作成した要素を部分適用する場合にはsandboxelementを、
一からSPA作ろうと思ったらapplicationを選択するなど、
コンパイル後のhtmlやjsをどのように使用するか、またそのファイルでやりたいことに合わせてsandboxelementdocumentapplicationから選択してmain関数を定義します。

基本的なElmファイルの構成

ボタン・An Introduction to Elmから拝借

import Browser
import Html exposing (Html, button, div, text)
import Html.Events exposing (onClick)



-- MAIN


main =
  Browser.sandbox { init = init, update = update, view = view }



-- MODEL

type alias Model = Int

init : Model
init =
  0


-- UPDATE

type Msg = Increment | Decrement

update : Msg -> Model -> Model
update msg model =
  case msg of
    Increment ->
      model + 1

    Decrement ->
      model - 1


-- VIEW

view : Model -> Html Msg
view model =
  div []
    [ button [ onClick Decrement ] [ text "-" ]
    , div [] [ text (String.fromInt model) ]
    , button [ onClick Increment ] [ text "+" ]
    ]

Elmのガイドにて、真っ先にサンプルとして紹介されているプログラムとなります。
ガイドに詳しくは記載されていますが、Elmの基本的なパターンは

  1. ユーザーからの入力を待ちます
  2. updateにメッセージを送ります
  3. 新しいModelを生成します
  4. view関数を呼び出して新しいHTMLを取得します
  5. 画面上に新しいHTMLを表示します
  6. 繰り返します!

と記載されています。

ここで、今回の本題のBrowserですが、main関数で登場しています。
このmainは、

mainはElmでは特別な値で、画面に何を表示するかを記述します。この例では、アプリケーションをinitで初期化して、view関数ですべてを画面に表示し、ユーザーからの入力をupdate関数に渡します。これがプログラムの大まかな概要だと考えてください。

とのことで、作りたいアプリによってmainBrowserを使って定義する必要があります。

Browser

Browser

Browserには関数が4つ用意されています。
- sandbox :ボタンやチェックボックスなど入力への反応
- element :HTTPやJSとの相互運用などの外界との通信
- document<title><body> を操作
- application :SPAを作る

との説明でこれらを必要に応じて使い分けていきますが、それぞれの関数の型の差を見ていきます。

Browser.sandbox

sandbox :
    { init : model
    , view : model -> Html msg
    , update : msg -> model -> model
    }
    -> Program () model msg

initviewupdateから、Programを返します。
今回は深追いしませんが、Programで型変数flagsmodelmsgをもとにプログラムの状態を返している、くらいの認識で進みます。
()はユニット型と呼ばれ、空の値が入るという認識で進みます。
Browserの関数の中ではレコードの要素が一番少ないです。

後述するflagsやsubscriptions等を使用する場合は、そもそもsandboxの引数の型に含まれていないため、
例えば、

...
-- MAIN

main =
  Browser.sandbox
    { init = init
    , view = view
    , update = update
    , subscriptions = subscriptions
    }
...

はコンパイルが通りません。

Browser.element

element :
    { init : flags -> ( model, Cmd msg )
    , view : model -> Html msg
    , update : msg -> model -> ( model, Cmd msg )
    , subscriptions : model -> Sub msg
    }
    -> Program flags model msg

先ほどのsandboxと比べ、
CmdflagsSubという要素が追加されています。
概要の説明となりますが、elementでは、下記4通りの方法で外界と通信することが出来ます。
一つのHTML elementを返します。それを別のJSのプロジェクトに追加したりできます。
- Cmd :HTTPなど、Elmランタイムに対し命令を出せます。
- Sub :Websocketでつないだ先からのメッセージの購読など、イベントを"subscribe"できます。
- flags :初期化と同時にJSからElmに何らかの値を渡します。
- ports :JSとの相互運用でクライアント-サーバーの関係を設定できます。

Browser.Document

document :
    { init : flags -> ( model, Cmd msg )
    , view : model -> Document msg
    , update : msg -> model -> ( model, Cmd msg )
    , subscriptions : model -> Sub msg
    }
    -> Program flags model msg

elementと比較すると、view関数でHTML msgではなく、Document msgを返しています。

Documentは型エイリアスで、

type alias Document msg =
    { title : String
    , body : List (Html msg)
    }

と定義されており、Browser.elementでは単一のHTMLの要素を返すのに対し、
Browser.documentでは"title"と、"body"を含めたコントロールを返すことが出来ます。

Browser.application

application :
    { init : flags -> Url -> Key -> ( model, Cmd msg )
    , view : model -> Document msg
    , update : msg -> model -> ( model, Cmd msg )
    , subscriptions : model -> Sub msg
    , onUrlRequest : UrlRequest -> msg
    , onUrlChange : Url -> msg
    }
    -> Program flags model msg

documentと比較すると、initの型にUrlKeyの引数、
onUrlRequestonUrlChangeが追加されています。
これらにより、URLの変更を管理できるアプリケーションを作成します。

init では、アプリ起動時に最初のURLを取得します。
onUrlRequest では、リンクが押下された際に実行する関数をupdate関数内に設定できます。
onUrlChenge では、Urlが変更されたときに実行する関数をupdate関数内に設定できます。

終わり

プロジェクト、フロントの構成ってどうしよ、とか考えながら見ていたのですが、
モジュールの構造化で面白いことが書かれてました。

防衛本能
The Life of a Fileで私は、その人がElmへと入ってきたときに迷子にさせてしまうような、JavaScriptの神話的知識について指摘しました。

『ファイルは短くしよう』 JavaScriptでは、とてもやっかいなバグを引き起こす、隠れた状態変化が起きやすいです。しかしElmでは、そのようなことは起こりえません!ファイルは2000行の長さになってもいいですし、それでもそのようなバグは起きないのです。

『最初から正しいアーキテクチャにしよう』 JavaScriptではリファクタリングはとてつもなく危険で、最初からすべて書き直したほうが手っ取り早いでしょう。でもElmでは、リファクタリングは簡単で安全です!20の異なるファイルでも自信を持って変更することができます。

これらの『防衛本能』は問題からあなたを守ってくれると思うかもしれませんが、そんな問題はそもそもElmには存在しないのです。これらの神話を頭の片隅に置いたままにしても、防衛本能に振り回されるよりはいいでしょう。でも、ファイルが400行、600行、800行と増えていくのを見た時に、このJavaScriptの神話はとても落ち着かない気持ちにさせてくるのがわかりました。行数の限界をもっと押し上げておくのをお勧めします! どこまで行けるのか試してみてください。コメントヘッダを使ってみたり、補助関数を作ってみましょう。でもそれらをすべてひとつのファイルに入れたままにしておいてください。これは自分自身で経験する価値があります!

1000行越えのmain.elm一刀流でもいい…ってコト!?

10
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
0