` の中に限られていますので、もしこれが他のたくさんの要素に囲まれた大きなアプリケーションの中だったとしても心配は要りません! 次に、Elmを初期化するための`
32 |
33 |
34 |
35 |
36 |
42 |
43 |
44 | ```
45 |
46 |
49 | この例では現在時刻をミリ秒として渡していますが、JSONとしてデコードできるものであればどんなJavaScriptの値でもフラグに使うことができます。
50 |
51 |
54 |
55 | > **Note:** この追加のデータが『フラグ』と呼ばれているのは、それがコマンドラインフラグのようなものだからです。`elm make src/Main.elm`を実行するとき、`--optimize`や`--output=main.js`のようなフラグを追加して、その動作をカスタマイズすることができます。フラグもそれと似たようなものです。
56 |
57 |
60 | ## Elmでのフラグ
61 |
62 |
65 | Elmの側でフラグを扱うために、`init`関数に少し手を加える必要があります:
66 |
67 | ```elm
68 | module Main exposing (..)
69 |
70 | import Browser
71 | import Html exposing (Html, text)
72 |
73 |
74 | -- MAIN
75 |
76 | main : Program Int Model Msg
77 | main =
78 | Browser.element
79 | { init = init
80 | , view = view
81 | , update = update
82 | , subscriptions = subscriptions
83 | }
84 |
85 |
86 | -- MODEL
87 |
88 | type alias Model = { currentTime : Int }
89 |
90 | init : Int -> ( Model, Cmd Msg )
91 | init currentTime =
92 | ( { currentTime = currentTime }
93 | , Cmd.none
94 | )
95 |
96 |
97 | -- UPDATE
98 |
99 | type Msg = NoOp
100 |
101 | update : Msg -> Model -> ( Model, Cmd Msg )
102 | update _ model =
103 | ( model, Cmd.none )
104 |
105 |
106 | -- VIEW
107 |
108 | view : Model -> Html Msg
109 | view model =
110 | text (String.fromInt model.currentTime)
111 |
112 |
113 | -- SUBSCRIPTIONS
114 |
115 | subscriptions : Model -> Sub Msg
116 | subscriptions _ =
117 | Sub.none
118 | ```
119 |
120 |
123 | ここで重要なポイントはただ一つ、`init`関数が引数として`Int`を受け取っていることです。このように、JavaScriptの世界から渡されたフラグはElmからすぐにそのまま利用できます。フラグを受け取った後は、モデルに入れておいたり、コマンドを実行するのに使ったり、必要なら何にでも使うことができます。
124 |
125 |
128 | もっと面白いフラグの使い方を知りたいなら、[この`localStorage`の例](https://github.com/elm-community/js-integration-examples/tree/master/localStorage)を見てみてください!
129 |
130 |
133 |
134 | ## フラグの検証
135 |
136 |
139 |
140 | しかし、`Int`をフラグとして受け取るように`init`を定義したにも関わらず、`Elm.Main.init({ flags: "haha, what now?" })`というように初期化しようとするような人がいたら、いったい何が起こるのでしょうか。
141 |
142 |
145 |
146 | Elmはそのような場合に対してもチェックを行い、フラグの型が期待していた通りであることを保証してくれます。もしこのチェックがなければ、どんなデータでも渡すことができてしまい、Elm側では実行時エラーが起きてしまうでしょう!
147 |
148 |
151 |
152 | フラグとして渡すことのできる型には、次のように様々な型があります。
153 |
154 | - `Bool`
155 | - `Int`
156 | - `Float`
157 | - `String`
158 | - `Maybe`
159 | - `List`
160 | - `Array`
161 | - tuples
162 | - records
163 | - [`Json.Decode.Value`](https://package.elm-lang.org/packages/elm/json/latest/Json-Decode#Value)
164 |
165 |
168 |
169 | フラグを厳密に制御できるように、常に`Json.Decode.Value`を使うようにしている人もたくさんいます。どんな変な値であっても受け取り、それが予想外のデータであってもうまく修正することができるように、Elm側のコードでデコーダを書いているのです。
170 |
171 |
174 |
175 | `JSON.Decode.Value` 以外の型をフラグとして渡す機能は、実はJSONデコーダーを使う方法が発明されるよりも前の時代に使われていたものです。
176 | これらの型をフラグとして渡すには、いくつか注意しないといけないことがあります。次の例では、渡そうとしているフラグの型それぞれについて、いろいろなJavaScriptの値を渡すとそれぞれ何が起こるのかを示しています。
177 |
178 | - `init : Int -> ...`
179 | - `0` => `0`
180 | - `7` => `7`
181 | - `3.14` => error
182 | - `6.12` => error
183 |
184 | - `init : Maybe Int -> ...`
185 | - `null` => `Nothing`
186 | - `42` => `Just 42`
187 | - `"hi"` => error
188 |
189 | - `init : { x : Float, y : Float } -> ...`
190 | - `{ x: 3, y: 4, z: 50 }` => `{ x = 3, y = 4 }`
191 | - `{ x: 3, name: "Tom" }` => error
192 | - `{ x: 360, y: "why?" }` => error
193 |
194 | - `init : (String, Int) -> ...`
195 | - `["Tom", 42]` => `("Tom", 42)`
196 | - `["Sue", 33]` => `("Sue", 33)`
197 | - `["Bob", "4"]` => error
198 | - `["Joe", 9, 9]` => error
199 |
200 |
203 |
204 | もしこのような変換がひとつでもうまくいかない場合は、**JavaScript側でエラーが起こる**ことに注意してください! Elmでは『フェイルファスト』(fail fast)の原則をとっています。Elmコード側でエラーを起こすのではなく、可能な限り早く問題を報告するということです。これはフラグに`Json.Decode.Value`を使うのを好む人がいる理由のひとつにもなっています。JavaScript側でエラーが起きるより、デコーダでこの変な値を受け取ることで、何らかのフォールバックが実装されているのを保証するほうがいいということです。
205 |
--------------------------------------------------------------------------------
/book/next_steps.md:
--------------------------------------------------------------------------------
1 |
4 |
5 | # 次への一歩
6 |
7 |
10 |
11 | 私たちは今、多くの基礎知識を持っています。次への一歩は、経験を積み、関係を構築することです。
12 |
13 |
16 |
17 | ## 何かを作成すること
18 |
19 |
22 |
23 | 経験は素晴らしい教師なので、興味のある**アプリケーション**を作成することをお勧めします。あなたが何かしたいことがなければ、以下のようなものがあります:
24 |
25 |
31 |
32 | - **例を発展させる** — このガイドからいくつか例をとり、それらに追加しましょう。コードは[ここ](https://github.com/evancz/elm-architecture-tutorial/)にありますに手を出しましょう。
33 | - **仕事から何か** — たぶん、あなたが仕事でやっていることがあり、それがElmでどのように動くのかを見たいと思っています。あなた自身でそれを試してみて、それがどうなるか見てみましょうのアドバイスを参考にすれば上手くいくと思います。
34 | - **データの可視化** — 興味のあるデータを表示するには、[`terezka/line-charts`](https://terezka.github.io/line-charts/)のようなパッケージを使用しましょう。私だったら偽のデータでチャートを作り始めて、徐々にどこかから取ってきたデータを表示するように試みるでしょう。より良い可視化をすることでうまみのある[経済](https://data.bls.gov/timeseries/LNS11300000)と[健康](https://wonder.cdc.gov/)についてのデータが大量に利用可能です!
35 | - **ゲーム** — 私は、ポンゲーム(卓球)、ブロック崩し、スペースインベーダーのようなゲームを作ってプログラミングを始めました。おそらく、あなたもそのようなことを好きになるでしょうで絵を描くことから始めましょう。そこから、[`onKeyDown`](https://package.elm-lang.org/packages/elm/browser/latest/Browser-Events#onKeyDown)や[`onMouseMove`](https://package.elm-lang.org/packages/elm/browser/latest/Browser-Events#onMouseMove)、[`onAnimationFrame`](https://package.elm-lang.org/packages/elm/browser/latest/Browser-Events#onAnimationFrame)などのイベントに応じて画面が反応するようにしてみてください。いずれ[`elm-explorations/webgl`](https://package.elm-lang.org/packages/elm-explorations/webgl/latest/)で3Dグラフィックスに熱中するようになるでしょう!
36 |
37 |
40 |
41 | ## 質問をする
42 |
43 |
46 |
47 | [Slack](https://elmlang.herokuapp.com/)と[Discourse](https://discourse.elm-lang.org/)には、フレンドリーで知識豊かな人々がたくさんいます。あなたがプログラミング初心者であろうと業界で20年以上の経験を持っているとしても、Slackの#beginnersチャンネルは、Elmでのプログラミング初心者にとって素晴らしいものです![^1]もしかしてあなたは何らかのエラーメッセージが解決できなくてつまづいてしまっているのではないですか?あるいはJSONデコーダを理解するのに苦労していませんか?またもしかしたら`Task`型につまずいていませんか?他にも自分で定義したカスタム型についてフィードバックが欲しいと思っていませんか?**問題が何であれ、いつでも助けを求めることができます!**
48 |
49 |
52 |
53 | ## 人々に会う
54 |
55 |
58 |
59 | [世界中](https://www.meetup.com/topics/elm-programming/all/)にミートアップがあります。集まった人たちで何か企画をしたり、知り合いを増やすために開催する[code nights](https://blog.noredink.com/post/142283641812/designing-meetups-to-build-better-communities)という形式のミートアップが特にお勧めです。明白な利点は、あなたが取り組んでいるものであれば何でも助けてもらえることですが、その地域でElmを使っている人たちと会うこともできます。誰かがあなたにインスピレーションを与える何かクールなものに取り組んでいるかもしれません。あなたがその存在自体を知らなかったようなテクニックを学べるかもしれません。誰かが求人を持っているかもしれません。地元のElmのプログラマーがあなたが現在取り掛かっている問題について助けてくれるかもしれません。おそらくそれは楽しい時間です。プログラマーはこのような人付き合いの価値を低く見積もりがちですが、健全なプログラミング言語コミュニティの中で最も重要な部分の1つです!
60 |
61 | [^1]: 訳注:Elm-jp コミュニティは、日本語で Elm について気軽に相談できる場所として [discord](https://discordapp.com/invite/4j2MxCg) を提供しています。 ぜひこちらをご活用ください。
62 |
63 | [^2]: 訳注: 東京でのミートアップは不定期に [connpass](https://elm-jp.connpass.com/) 上で告知されています。また、前述の [discord](https://discordapp.com/invite/4j2MxCg) でもイベントの告知がされますので、ぜひチェックしてください!
64 |
--------------------------------------------------------------------------------
/book/optimization/README.md:
--------------------------------------------------------------------------------
1 |
4 |
5 | # 最適化
6 |
7 |
10 |
11 | Elmの最適化は、大きくふたつのタイプにわかれます。パフォーマンスについての最適化と、アセットサイズについての最適化です。
12 |
13 |
16 |
17 | **パフォーマンス** — ブラウザで一番遅いのはDOMです。ここには大きな最適化の余地があります。私はElmアプリケーションを高速化するためにプロファイリングをたくさんしてきましたが、ほとんどはあまり目に見える効果がありませんでした。もっと良いデータ型を使うのはどうでしょうか?モデルに応じて、計算の結果をキャッシュするのは?ほとんど効果はなく、コードが悪化するだけでした。唯一大きな違いがあったのは、`Html.Lazy`と`Html.Keyed`を使ってDOM操作を減らすことだけでした。
18 |
19 |
22 |
23 | **アセットサイズ** — ブラウザで実行する以上、ダウンロード時間についても注意しなくてはなりません。アセットを小さくすることができれば、モバイルデバイスや遅いインターネット接続でも素早く読み込むことができます。これはおそらくどんなパフォーマンスの最適化よりも重要だといえるでしょう!幸運なことに、Elmコンパイラは本当に良い仕事をしてくれて、コードを可能な限り小さくすることができます。ちゃんとした結果を得ようとコードをめちゃくちゃにする必要はないのです。
24 |
25 |
28 |
29 | どちらも重要ですが、この章ではこれらがどのような仕組みなのかを見て行きましょう!
30 |
31 |
--------------------------------------------------------------------------------
/book/optimization/asset_size.md:
--------------------------------------------------------------------------------
1 |
4 |
5 | # アセットサイズの縮小
6 |
7 |
10 |
11 | DOMを操作するよりも更に遅い処理があるとしたら、それはサーバからのデータの取得だけでしょう。遅いネットワーク回線にあるモバイル端末においては更に深刻です。`Html.Lazy`や`Html.Keyed`による最適化が常に有効なのは確かですが、もし読み込みが遅いのならアプリケーションは遅いままだと感じられるはずです!
12 |
13 |
16 |
17 | これを改善する良い方法は、送信するデータをもっと少なくすることです。たとえば、122キロバイトのアセット(サーバからブラウザに送られるファイル)を9キロバイトにすることができれば、読み込みはずっと早くなります! 次のような手法を使うことで、そのような結果を得ることができます。
18 |
19 |
24 |
25 | - **コンパイル。** Elmコンパイラは、デッドコード除去やレコードフィールドの名前変更のような、パフォーマンス最適化を実施することができます。つまり、生成されたコードの使われていないコードを取り除いたり、`userStatus`のようなレコードフィールド名を短くしたりすることができます。
26 | - **ミニファイ。** JavaScriptの世界には、いろいろな変換を行う『ミニファイア』(minifiers)と呼ばれるツールがあります。これは、変数名を短くしたり、インライン化をしたり、`if`文を3項演算子へと置き換えたり、`'\u0041'` を `'A'` に置き換えたりします。いずれもデータ量を削減するためです!
27 | - **圧縮。** デッドコード除去やミニファイによってコードを可能な限り小さくしたら、そのあとgzipのようなアルゴリズムを使うことでコードを更に圧縮することができます。それ自身を単純に取り除くことが難しい`function`や`return`のような予約語に対して特に有効です。
28 |
29 |
32 |
33 | Elmではこれらの最適化をプロジェクトに対してとても簡単に設定することができます。複雑なビルドシステムは必要ありません。たったふたつのターミナルコマンドだけでできるのです!
34 |
35 |
36 |
39 |
40 | ## 最適化のやりかた
41 |
42 |
45 |
46 | 最初のステップは、`--optimize` フラグを付けてコンパイルすることです。これはレコードフィールドの名前を短くするようなことを行います。
47 |
48 |
51 |
52 | 次のステップは、出力されたJavaScriptコードをミニファイすることです。私は`uglifyjs`というミニファイアを使っていますが、別のあなたが好きなものを使っても構いません。`uglifyjs`のいいところは、その特別なフラグにあります。そのようなフラグを付けると、通常のJSコードに対して実施しても正しく動くかわからない高度な最適化が有効になりますが、Elmのデザインのお陰でこれらの最適化はまったく安全です!
53 |
54 |
57 |
58 | これらを合わせると、`src/Main.elm`を次のようなふたつのターミナルコマンドで最適化することができます。
59 |
60 | ```bash
61 | elm make src/Main.elm --optimize --output=elm.js
62 | uglifyjs elm.js --compress 'pure_funcs=[F2,F3,F4,F5,F6,F7,F8,F9,A2,A3,A4,A5,A6,A7,A8,A9],pure_getters,keep_fargs=false,unsafe_comps,unsafe' | uglifyjs --mangle --output elm.min.js
63 | ```
64 |
65 |
68 |
69 | コマンドの実行が完了すると、`elm.js`とそれがもっと小さくなった`elm.min.js`ファイルのふたつが生成されるでしょう!
70 |
71 |
76 |
77 | > **Note 1:** `uglifyjs` は、最初の`--compress`とふたつめの`--mangle`の2回呼び出されます。これは必要なことです! さもなければ、`uglifyjs`は`pure_funcs`フラグを無視してしまいます。
78 | >
79 | > **Note 2:** もし`uglifyjs`コマンドがターミナルで使えない場合は、`npm install uglify-js --global`を実行して`uglifyjs`をダウンロードしてください。もし`npm`もないようであれば、[nodejs](https://nodejs.org/)から入手してください。
80 |
81 |
84 |
85 | ## スクリプト
86 |
87 |
90 |
91 | あのような`uglifyjs`のフラグをみんな覚えるのは大変ですので、おそらくそれを実行するスクリプトを書いたほうがいいでしょう。
92 |
93 |
96 |
97 | `elm.js`と`elm.min.js`ファイルを生成するbashスクリプトが欲しい場合もあると思います。MacかLinuxなら、`optimize.sh`を次のように定義すればいいでしょう。
98 |
99 | ```bash
100 | #!/bin/sh
101 |
102 | set -e
103 |
104 | js="elm.js"
105 | min="elm.min.js"
106 |
107 | elm make --optimize --output=$js "$@"
108 |
109 | uglifyjs $js --compress 'pure_funcs=[F2,F3,F4,F5,F6,F7,F8,F9,A2,A3,A4,A5,A6,A7,A8,A9],pure_getters,keep_fargs=false,unsafe_comps,unsafe' | uglifyjs --mangle --output $min
110 |
111 | echo "Compiled size:$(wc $js -c) bytes ($js)"
112 | echo "Minified size:$(wc $min -c) bytes ($min)"
113 | echo "Gzipped size: $(gzip $min -c | wc -c) bytes"
114 | ```
115 |
116 |
119 |
120 | これで、もし[TodoMVC](https://github.com/evancz/elm-todomvc)のプロジェクト上で`./optimize.sh src/Main.elm`を実行すれば、次のようなものがターミナルに出力されるのを見ることができるでしょう。
121 |
122 | ```
123 | Compiled size: 122297 bytes (elm.js)
124 | Minified size: 24123 bytes (elm.min.js)
125 | Gzipped size: 9148 bytes
126 | ```
127 |
128 |
131 |
132 | なかなかいいですね! このプログラムをユーザに渡すときは、たった9キロバイトほどを送るだけでいいのです!
133 |
134 |
137 |
138 | この`elm`と`uglifyjs`はどのプラットフォームでも動く重要なコマンドですので、同じようなことをWindowsで行うのはとても大変というほどでもありません。
139 |
140 |
143 |
144 | ## アドバイス
145 |
146 |
149 |
150 | これまで見てきたように、`Browser.application`を使い、単一のJavaScriptファイルへとコンパイルするのを書くのをお勧めします。そのJavaScriptファイルは、ユーザがページを最初に訪れたときにダウンロード(とキャッシュ)されるでしょう。JavaScriptを生成する他の有名なプログラミング言語と比較しても、Elmはずっと小さなファイルを作成しますので、[ここ](https://elm-lang.org/blog/small-assets-without-the-headache)で見ていただいたように、この戦略に従えばよりいっそう良い結果を得られるでしょう。
151 |
152 |
155 |
156 | > **Note:** 理論的には、Elmでこれよりもさらに小さなアセットにすることも可能です。これは現段階では不可能なのですが、もしあなたがElmで5万行以上のコードを書いているなら、ユーザの使用状況の調査のひとつとして、あなたの状況を教えていただけたらと思います。くわしくは[こちら](https://gist.github.com/evancz/fc6ff4995395a1643155593a182e2de7)をご覧ください!
157 |
--------------------------------------------------------------------------------
/book/optimization/diagrams/dom.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/book/optimization/diagrams/patch.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/book/optimization/keyed.md:
--------------------------------------------------------------------------------
1 | # `Html.Keyed`
2 |
3 |
6 |
7 | 前のページでは、仮想DOMがどのように動くのかと、`Html.Lazy`モジュールを使って多くの処理を減らすことができるのを学びました。次は[`Html.Keyed`](https://package.elm-lang.org/packages/elm/html/latest/Html-Keyed/)を導入し、さらに多くの処理を省く方法を紹介していきます。
8 |
9 |
12 |
13 | データのリストに対して**挿入**や**削除**、**並び替え**といった操作が可能なアプリケーションであるとき、この最適化は特に役に立ちます。
14 |
15 |
18 |
19 | ## 問題
20 |
21 |
24 |
25 | アメリカ合衆国のすべての大統領の一覧があるとしましょう。また、それを名前順、[学歴順](https://en.wikipedia.org/wiki/List_of_Presidents_of_the_United_States_by_education)、[資産額の順](https://en.wikipedia.org/wiki/List_of_Presidents_of_the_United_States_by_net_worth)、[出身地順](https://en.wikipedia.org/wiki/List_of_Presidents_of_the_United_States_by_home_state)に基いて並び替えができるようにします。
26 |
27 |
30 |
31 | (前のページで説明したような)差分アルゴリズムが長いリストにとりかかると、次のように現在のリストと次のリストから同じ位置の要素を組にして処理を行っていきます。
32 |
33 |
38 |
39 | - 現在のリストのひとつめの要素と、次のリストのひとつめの要素の差分をとる
40 | - 現在のリストのふたつめの要素と、次のリストのふたつめの要素の差分をとる
41 | - ...
42 |
43 |
44 |
47 |
48 | しかし、並び替えの順序を変更したときは、これらの組のほとんどが違っていてそれぞれ差分処理が必要になるでしょう! そして、ノードをシャッフルしたときなどでは、DOMに対して大量の操作を行うはめになります。
49 |
50 |
53 |
54 | 項目の挿入や削除についても同じような問題があります。100項目あるうちの最初の項目を削除するとしましょう。すべてひとつずつずれていくことになるので、差分を比較する組はいずれも違うものになります。そのため99回の差分を処理し、最後のひとつが削除されることになります。これは良くありません!
55 |
56 |
59 |
60 | ## 解決策
61 |
62 |
65 |
66 | これらすべての問題から救ってくれるのが、[`Html.Keyed.node`](https://package.elm-lang.org/packages/elm/html/latest/Html-Keyed#node)です。これは、『キー』(Key)に従って組を作るようにして、それぞれの要素を他のものと簡単に区別できるようにしてくれるのです。
67 |
68 |
71 |
72 | 大統領のサンプルでいうと、コードを次のように書くことができます。
73 |
74 | ```elm
75 | import Html exposing (..)
76 | import Html.Keyed as Keyed
77 | import Html.Lazy exposing (lazy)
78 |
79 | viewPresidents : List President -> Html msg
80 | viewPresidents presidents =
81 | Keyed.node "ul" [] (List.map viewKeyedPresident presidents)
82 |
83 | viewKeyedPresident : President -> (String, Html msg)
84 | viewKeyedPresident president =
85 | ( president.name, lazy viewPresident president )
86 |
87 | viewPresident : President -> Html msg
88 | viewPresident president =
89 | li [] [ ... ]
90 | ```
91 |
92 |
95 |
96 | どの子のノードもキーに関連付けられています。要素の順序に基いた組で差分をとる代わりに、キーによる照合に基いた組で比較することができるのです!
97 |
98 |
101 |
102 | これでElmの仮想DOM実装は、リストが並び替えられたことを認識できるようになります。まずElmは、大統領をキーに基づいて照合します。それから、それらの組の差分を処理します。ここではそれぞれの項目に`lazy`を使っていますので、それらのすべての処理を省くことができます。素晴らしいでしょう! それからElmは、指定した順序で表示するにはDOMノードをどのように入れ替えればいいかを見つけ出します。結果として、キーを付けたときの処理量はキーを使わなかったときよりも遥かに少なくなるのです。
103 |
104 |
107 |
108 | 並び替えでキーが役に立つのはわかりましたが、この最適化が本当に必要になる最もよくある場面というのは他にあります。**キーが付けられたノードは、項目の挿入や削除においてなにより重要です。**100要素の最初の要素を削除するとき、キー付きのノードを使えばElmの仮想DOM実装がそれを直ちに認識できるようになるのでした。そのため、99回も差分を処理することなく、ひとつの要素を削除するだけで済むのです。
109 |
110 |
111 |
114 |
115 | ## まとめ
116 |
117 |
120 |
121 | 通常のアプリケーションで起こる様々な計算と比較すると、DOMを操作するのはとてつもなく遅いです。**まずは常に`Html.Lazy`と`Html.keyed`を使うようにしましょう。**可能な限りプロファイルをとってみることもお勧めします。[このように](https://developers.google.com/web/tools/chrome-devtools/evaluate-performance/reference)、プログラムのタイムラインビューを見ることができるブラウザもあります。ページの読み込み、スクリプトの実行、ページのレンダリング、ページの描画などのうち、いったいどこにどれくらい時間を費やしているのかを大まかに教えてくれます。もし時間の10%しかスクリプトの実行に費やされていないのなら、Elmコードそのものが2倍の早さになったとしても、あまり目立った効果は得られないでしょう。単に`lazy`やキー付きのノードを追加するだけでDOMの操作が減り、残りの90%の時間という大きな部分から処理を減らすことができるかもしれないのです。
122 |
--------------------------------------------------------------------------------
/book/robots.txt:
--------------------------------------------------------------------------------
1 | User-agent: *
2 | Disallow: *
3 |
4 | Allow: /about_translation.html$
5 | Allow: /$
6 | Allow: /core_language.html$
7 | Allow: /architecture/$
8 | Allow: /architecture/buttons.html$
9 | Allow: /architecture/text_fields.html$
10 | Allow: /architecture/forms.html$
11 | Allow: /types/$
12 | Allow: /types/reading_types.html$
13 | Allow: /types/type_aliases.html$
14 | Allow: /types/custom_types.html$
15 | Allow: /types/pattern_matching.html$
16 | Allow: /error_handling/$
17 | Allow: /error_handling/maybe.html$
18 | Allow: /error_handling/result.html$
19 | Allow: /effects/$
20 | Allow: /effects/http.html$
21 | Allow: /effects/json.html$
22 | Allow: /effects/random.html$
23 | Allow: /effects/time.html$
24 | Allow: /install/$
25 | Allow: /install/editor.html$
26 | Allow: /install/elm.html$
27 | Allow: /interop/$
28 | Allow: /interop/flags.html$
29 | Allow: /interop/ports.html$
30 | Allow: /interop/custom_elements.html$
31 | Allow: /interop/limits.html$
32 | Allow: /webapps/$
33 | Allow: /webapps/navigation.html$
34 | Allow: /webapps/url_parsing.html$
35 | Allow: /webapps/modules.html$
36 | Allow: /webapps/structure.html$
37 | Allow: /optimization/$
38 | Allow: /optimization/lazy.html$
39 | Allow: /optimization/keyed.html$
40 | Allow: /optimization/asset_size.html$
41 | Allow: /next_steps.html$
42 | Allow: /appendix/types_as_sets.html$
43 | Allow: /appendix/types_as_bits.html$
44 | Allow: /appendix/function_types.html$
45 | Allow: /gitbook/*
46 | Allow: /sitemap.xml$
47 |
48 | # Sitemap
49 | Sitemap: https://guide.elm-lang.jp/sitemap.xml
50 |
--------------------------------------------------------------------------------
/book/styles/website.css:
--------------------------------------------------------------------------------
1 | .book-header h1 { display: none; }
2 | .book-summary { border: none; }
3 | .divider { display: none; }
4 | .gitbook-link { display: none !important; }
5 |
6 |
7 | /* REFORMAT BLOCKQUOTES */
8 |
9 | .markdown-section blockquote {
10 | background: none;
11 | color: inherit;
12 | border: 1px solid rgb(51,51,51);
13 | padding: 12px 16px 2px 16px;
14 | margin: 2em;
15 | }
16 |
17 |
18 | /* EDIT LINK */
19 |
20 | .edit-link { position: relative; }
21 |
22 | .edit-link a {
23 | position: absolute;
24 | top: .85em;
25 | right: 1em;
26 | padding: .5em 1em;
27 | color: white !important;
28 | background: #1293D8 !important;
29 | text-decoration: none !important;
30 | font-family: Consolas,"Liberation Mono",Menlo,Courier,monospace;
31 | }
32 |
33 |
34 | /* CORRECT SIDEBAR COLORS */
35 |
36 | .book.color-theme-1 .book-summary { background: #D0C396; }
37 | .book.color-theme-2 .book-summary { background: #0A0E1D; }
38 |
39 |
40 |
41 | /* CORRECT SEPIA MODE DETAILS */
42 |
43 | .book.color-theme-1 .book-summary { border-right: none; }
44 | .book.color-theme-1 .book-summary ul.summary li.active > a { background: #f3eacb; }
45 |
46 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal a { color: #4183c4; }
47 | .color-theme-1 .dropdown-menu { background-color: #FFFEFB; border-color: #FFFBEC; }
48 | .color-theme-1 .dropdown-menu .buttons { border-color: #FFFBEC; }
49 | .color-theme-1 .dropdown-menu .dropdown-caret .caret-inner { border-bottom: 9px solid #FFFEFB; }
50 |
51 | .book.color-theme-1 .book-summary ul.summary li a,
52 | .book.color-theme-1 .book-summary ul.summary li span
53 | {
54 | color: rgb(112, 66, 20);
55 | }
56 |
57 |
58 |
59 | /* CORRECT NIGHT MODE DETAILS */
60 |
61 | .book.color-theme-2 .book-summary ul.summary li a,
62 | .book.color-theme-2 .book-summary ul.summary li a:hover,
63 | .book.color-theme-2 .book-summary ul.summary li.active > a,
64 | .book.color-theme-2 .book-summary ul.summary li span
65 | {
66 | font-weight: normal;
67 | }
68 |
69 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h1,
70 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h2,
71 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h3,
72 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h4,
73 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h5,
74 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h6
75 | {
76 | color: inherit;
77 | }
78 |
79 |
80 | /* CORRECT SEARCH BAR FORMAT */
81 |
82 | #book-search-input {
83 | background: none;
84 | border: none;
85 | padding: 0;
86 | margin: 0 0 10px 0;
87 | }
88 |
89 | #book-search-input input,
90 | #book-search-input input:focus,
91 | #book-search-input input:hover
92 | {
93 | padding: 10px 15px;
94 | background: none;
95 | border-bottom: 1px solid rgba(0,0,0,.07);
96 | }
97 |
98 | .color-theme-1 #book-search-input input,
99 | .color-theme-1 #book-search-input input:focus,
100 | .color-theme-1 #book-search-input input:hover
101 | {
102 | color: #877f6a;
103 | }
104 |
105 | .color-theme-2 #book-search-input input,
106 | .color-theme-2 #book-search-input input:focus,
107 | .color-theme-2 #book-search-input input:hover
108 | {
109 | color: #f4f4f5;
110 | border-bottom-color: #272a3a;
111 | }
112 |
--------------------------------------------------------------------------------
/book/types/README.md:
--------------------------------------------------------------------------------
1 |
4 |
5 | # 型について
6 |
7 |
10 |
11 | Elm の主な利点の 1 つは、**ランタイムエラーが実際に起きないことです**。 Elm コンパイラが非常に素早くソースコードを分析して、値がプログラム中でどのように使われているか解析できるためです。 値を間違った方法で使用すると、コンパイラがフレンドリーなエラーメッセージで警告してくれます。 これは _型推論_ と呼ばれています。 コンパイラは、全ての関数の引数と返り値の型を解析します。
12 |
13 |
16 |
17 | ## 型推論の例
18 |
19 |
22 |
23 | 次のコードはフルネームを文字列で返す `toFullName` 関数を定義しています:
24 |
25 | ```elm
26 | toFullName person =
27 | person.firstName ++ " " ++ person.lastName
28 |
29 | fullName =
30 | toFullName { fistName = "Hermann", lastName = "Hesse" }
31 | ```
32 |
33 |
36 |
37 | JavaScript や Python のように、余分なものなしにこのコードは書けます。 だけどバグがあることに気づきましたか?
38 |
39 |
42 |
43 | JavaScript で同等のコードは`"undefined Hesse"`を吐き出します。 エラーですらありません! うまくいけばユーザーがこのバグを見つけたときに報告してくれるかもしれません。 対照的に、Elm コンパイラは単にソースコードを解析するだけで警告してくれます:
44 |
45 | ```
46 | -- TYPE MISMATCH ---------------------------------------------------------------
47 |
48 | The argument to function `toFullName` is causing a mismatch.
49 | # `toFullName` 関数の引数の型が合いません
50 |
51 | 6│ toFullName { fistName = "Hermann", lastName = "Hesse" }
52 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
53 | Function `toFullName` is expecting the argument to be:
54 | # `toFullName`関数は以下の型の引数を期待しています:
55 |
56 | { …, firstName : … }
57 |
58 | But it is:
59 | # しかし、実際は:
60 |
61 | { …, fistName : … }
62 |
63 | Hint: I compared the record fields and found some potential typos.
64 | # 解決のヒント: レコードのフィールドを比べたところ、typoらしきものが見つかりました。
65 |
66 | firstName <-> fistName
67 | ```
68 |
69 |
72 |
73 | `toFullName` が引数の _型_ を間違って取得していることがわかります。 エラーメッセージのヒントのように、誰かが誤って `first`の代わりに `fist`を書きました。
74 |
75 |
78 |
79 | このような単純ミスを防ぐための仕組みは素晴らしいものですが、何百ものファイルや、変更を加えるたくさんのコラボレーターがいる場合、より価値のあるものになります。 どんなに大きく複雑なものでも、Elm コンパイラはソースコードに基づいて `全て` が適切かをチェックします。
80 |
81 |
84 |
85 | 型についてより理解が深まると、コンパイラはフレンドリーなアシスタントのように感じられます。 だからもっと型について学びましょう!
86 |
--------------------------------------------------------------------------------
/book/types/pattern_matching.md:
--------------------------------------------------------------------------------
1 |
4 | # パターンマッチ
5 |
6 |
9 | 前のページで、`type` キーワードを用いた[カスタム型](/types/custom_types.html)の書き方について学びました。
10 | 主要な例としてチャットルームの `User` 型を定義しました:
11 |
12 | ```elm
13 | type User
14 | = Regular String Int
15 | | Visitor String
16 | ```
17 |
18 |
21 |
22 | `Regular` ユーザーは名前と年齢を持っているのに対し、`Visitor` ユーザーは名前だけを持っているのでした。
23 | このようにカスタム型の作り方は分かりましたが、実際にはどのように使えば良いのでしょうか?
24 |
25 |
28 | ## `case`
29 |
30 |
33 | 各 `User` から表示する名前を返す関数 `toName` を定義したいとします。
34 | `case` 式を使う必要があるでしょう:
35 |
36 | ```elm
37 | toName : User -> String
38 | toName user =
39 | case user of
40 | Regular name age ->
41 | name
42 |
43 | Visitor name ->
44 | name
45 | -- toName (Regular "Thomas" 44) == "Thomas"
46 | -- toName (Visitor "kate95") == "kate95"
47 | ```
48 |
49 |
52 | `case`式は渡されたバリアントごとに処理を分岐できます。なので`Regular`のトーマスが来ようが`Visitor`のケイトが来ようが名前の表示の仕方は常にわかっています。
53 |
54 |
57 | そしてもし、`toName (Visitar "kate95")` や `toName Anonymous` のような不正な引数を与えた場合には、コンパイラがそのことについてすぐに教えてくれるでしょう。
58 | これによって多くの単純なミスが数秒で修正でき、プログラマーが自分でミスを見つけないといけないような言語と比べて全体の開発時間を大いに削減できるのです。
59 |
60 |
63 | ## ワイルドカード
64 |
65 |
68 | 上で定義した`toName`関数はうまく動きますが、`age`の値を実装内で使っていませんよね?使っていない関連データには名前を付ける代わりに「ワイルドカード」を使うのが普通です。
69 |
70 | ```elm
71 | toName : User -> String
72 | toName user =
73 | case user of
74 | Regular name _ ->
75 | name
76 |
77 | Visitor name ->
78 | name
79 | ```
80 |
81 |
84 | `_`はそこになんらかの値があることを示しつつも、実際にはその値が特に使われていないことを表現しています。
85 |
--------------------------------------------------------------------------------
/book/types/type_aliases.md:
--------------------------------------------------------------------------------
1 |
4 | # 型エイリアス
5 |
6 |
9 | 型注釈は時として長ったらしくなってしまうものです。例えば、フィールドがいくつもあるレコードを使っているような場合に実際に困ったことになります。主にこのような問題を解決するために、型エイリアスが存在します。**型エイリアス**とはある型につけた短い別名のことです。例えば、`User`という型エイリアスを以下のように作成したりします。
10 |
11 | ```elm
12 | type alias User =
13 | { name : String
14 | , age : Int
15 | }
16 | ```
17 |
18 |
21 | 毎度レコード型の内容を全部ズラーッと書かなくても、単に`User`とだけ書けば良いのです。これを活用すれば、読みやすい型注釈を書けます。
22 |
23 | ```elm
24 | -- WITH ALIAS
25 |
26 | isOldEnoughToVote : User -> Bool
27 | isOldEnoughToVote user =
28 | user.age >= 18
29 |
30 |
31 | -- WITHOUT ALIAS
32 |
33 | isOldEnoughToVote : { name : String, age : Int } -> Bool
34 | isOldEnoughToVote user =
35 | user.age >= 18
36 | ```
37 |
38 |
41 | 上記の例はどちらも等価なものですが、型エイリアスを使っているものの方が型注釈が短く読みやすくなっています。このように、型エイリアスでやっているのは、そのまま書いたら長い型に対して**別名(エイリアス)**を作っているというだけのことです。
42 |
43 |
46 | ## モデル
47 |
48 |
51 | 型エイリアスの典型的な使いみちとして、モデルを設計するときが挙げられます。The Elm Architectureについて学んだ際に、以下のようなモデルをあつかいました。
52 |
53 | ```
54 | type alias Model =
55 | { name : String
56 | , password : String
57 | , passwordAgain : String
58 | }
59 | ```
60 |
61 |
64 | このようなモデル型に対して型エイリアスを使うと、`update`関数や`view`関数の型注釈を書く際にとてもいいことがあります。全部フィールドを漏れなく書くのに比べて、`Msg -> Model -> Model`と書くほうがずっといい感じです! 単に見た目がスッキリするだけでなく、モデルにフィールドを追加しても型注釈を全く変更しなくていいという別の嬉しさもあります。
65 |
66 |
69 | ## レコードコンストラクター
70 |
71 |
74 | レコード用に型エイリアスを作成すると、 **レコードコンストラクター**も一緒に生成されます。 つまり`elm repl`で`User`型を定義したら、このようにレコードを作れます:
75 |
76 | {% replWithTypes %}
77 | [
78 | {
79 | "add-type": "User",
80 | "input": "type alias User = { name : String, age : Int }"
81 | },
82 | {
83 | "input": "User",
84 | "value": "\u001b[36m
\u001b[0m",
85 | "type_": "String -> Int -> User"
86 | },
87 | {
88 | "input": "User \"Sue\" 58",
89 | "value": "{ \u001b[37mname\u001b[0m = \u001b[93m\"Sue\"\u001b[0m, \u001b[37mage\u001b[0m = \u001b[95m58\u001b[0m }",
90 | "type_": "User"
91 | },
92 | {
93 | "input": "User \"Tom\" 31",
94 | "value": "{ \u001b[37mname\u001b[0m = \u001b[93m\"Tom\"\u001b[0m, \u001b[37mage\u001b[0m = \u001b[95m31\u001b[0m }",
95 | "type_": "User"
96 | }
97 | ]
98 | {% endreplWithTypes %}
99 |
100 | 別のユーザーを作成したり、自分で型エイリアスを作ってみたりしてください⬆️
101 |
102 |
105 | なお、レコードコンストラクターを使う際は、与える引数の順番が型エイリアスを定義したときのフィールドの順番と一致している必要があります。
106 |
107 |
110 | 念のため再度の確認ですが、**これはレコードの場合に限った話です**。レコード以外の型に対して型エイリアスを作っても、コンストラクターは作成されません。
111 |
--------------------------------------------------------------------------------
/book/webapps/README.md:
--------------------------------------------------------------------------------
1 |
4 |
5 | # Web アプリケーション
6 |
7 |
10 |
11 | これまでは、大きなアプリケーションのなかのひとつのノードをだけを専有する`Browser.element`を使って Elm プログラムを作ってきました。([こちら](https://elm-lang.org/blog/how-to-use-elm-at-work)で述べたように)これは現在すでに稼働しているサービスに Elm を**導入**するのには最適ですが、このあとはどうなるのでしょうか。より広い範囲で Elm を使うには、どのようにすればよいのでしょうか。
12 |
13 |
16 |
17 | この章では、いろいろなページを互いにうまく組み合わせた「Web アプリケーション」をどのように作るのかを学んでいきます。でもまずはひとつのページだけを扱うことから始めるのがいいでしょう。
18 |
19 |
22 |
23 | ## ドキュメントの操作
24 |
25 |
28 |
29 | 最初の一歩は、[`Browser.document`](https://package.elm-lang.org/packages/elm/browser/latest/Browser#document)を使うようにプログラムを変えることです。
30 |
31 | ```elm
32 | document :
33 | { init : flags -> ( model, Cmd msg )
34 | , view : model -> Document msg
35 | , update : msg -> model -> ( model, Cmd msg )
36 | , subscriptions : model -> Sub msg
37 | }
38 | -> Program flags model msg
39 | ```
40 |
41 |
44 |
45 | `view`関数を除けば、引数は`Browser.element`とほとんど同じです。`view`関数は`Html`を返すのではなく、次のような[`Document`](https://package.elm-lang.org/packages/elm/browser/latest/Browser#Document)を返すようになります。
46 |
47 | ```elm
48 | type alias Document msg =
49 | { title : String
50 | , body : List (Html msg)
51 | }
52 | ```
53 |
54 |
57 |
58 | これでドキュメントの ``と``について制御できるようになります。何かのデータをダウンロードして、それを元により詳細なタイトルを決定するようなプログラムもあるでしょう。そのようなときは、この`view`関数のなかで`title`を変えるだけでです!
59 |
60 |
63 |
64 | ## ページをサーバーから提供する
65 |
66 |
69 |
70 | コンパイラはデフォルトで HTML を出力しますので、次のようにするとコードをコンパイルできます。
71 |
72 | ```bash
73 | elm make src/Main.elm
74 | ```
75 |
76 |
79 |
80 | `index.html`という名前のファイルが出力されますが、これは他の HTML ファイルと同じようにサーバから送信できます。これでもうまく動きますが、(1) Elm を JavaScript へとコンパイルする、(2) HTML をカスタマイズするなど、もうちょっと柔軟に扱うこともできます。パスを与えて、次のようにコンパイルしてみます。
81 |
82 | ```bash
83 | elm make src/Main.elm --output=main.js
84 | ```
85 |
86 |
89 |
90 | こうすると`main.js`が生成され、次のような HTML ファイルから読み込むことができます。
91 |
92 | ```html
93 |
94 |
95 |
96 |
97 | Main
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 | ```
106 |
107 |
110 |
111 | この HTML はとてもシンプルですね。``で必要なものを読み込み、``で Elm プログラムを初期化しています。Elm プログラムはここから読み込まれ、すべてがレンダリングされます。
112 |
113 |
116 |
117 | どちらにせよ、ブラウザに送信する HTML を用意することができました。[GitHub Pages](https://pages.github.com/)や[Netlify](https://www.netlify.com/)のようなフリーのサービスを使えばこの HTML を提供することができますし、あるいは自分のサーバを用意して[Digital Ocean](https://m.do.co/c/c47faa1916d2)のようなサービスを使って VPS を実行するのもいいでしょう。どんなやり方でも大丈夫です!HTML をブラウザへと送信できさえすればいいのです。
118 |
119 |
124 |
125 | > **Note 1:** もしあなたが CSS で何らかのカスタマイズをしたいのなら、カスタム HTML を作るといいでしょう。多くのユーザは、[`rtfeldman/elm-css`](https://package.elm-lang.org/packages/rtfeldman/elm-css/latest/)のようなプロジェクト使って Elm のなかでスタイルを扱っていますが、あなたはチームを組んで作業しており、事前に書かれたたくさんの CSS があるかもしれません。チームでは何らかの CSS プリプロセッサを使っていることもあるでしょう。その場合もあなたの HTML ファイルの``で、最終的な CSS ファイルを読み込むだけです。
126 |
127 | > **Note 2:** 先述の Digital Ocean へのリンクは紹介料付きのリンクで、もしこちらを通じてサインアップしてサービスを使っていただければ、`elm-lang.org`と`package.elm-lang.org`へ 25 ドルのホスティング料が支払われます。
128 |
--------------------------------------------------------------------------------
/book/webapps/url_parsing.md:
--------------------------------------------------------------------------------
1 |
4 |
5 | # URLのパース
6 |
7 |
10 |
11 | 実際のウェブアプリケーションでは、異なるURLごとに異なる内容を表示したいでしょう。
12 |
13 | - `/search`
14 | - `/search?q=seiza`
15 | - `/settings`
16 |
17 |
20 |
21 | これはどのようにすればいいのでしょうか? ここでは[`elm/url`](https://package.elm-lang.org/packages/elm/url/latest/)を使って、生の文字列をElmの素敵なデータ構造へとパースしていきます。例を見ていくだけで、このパッケージが何より役に立つことがわかると思います。私たちがやろうとしていることが、まさにそれですから!
22 |
23 |
26 |
27 | ## 例 1
28 |
29 |
32 |
33 | 美術のウェブサイトがあり、次のようなURLのページがあるとしましょう。
34 |
35 | - `/topic/architecture`
36 | - `/topic/painting`
37 | - `/topic/sculpture`
38 | - `/blog/42`
39 | - `/blog/123`
40 | - `/blog/451`
41 | - `/user/tom`
42 | - `/user/sue`
43 | - `/user/sue/comment/11`
44 | - `/user/sue/comment/51`
45 |
46 |
49 |
50 | トピックのページ、ブログの投稿、ユーザ情報のページがあり、それぞれのユーザのコメントを見ることもできます。[`Url.Parser`](https://package.elm-lang.org/packages/elm/url/latest/Url-Parser) モジュールを使って、次のようにURLパーサを書くといいでしょう。
51 |
52 | ```elm
53 | import Url.Parser exposing (Parser, (>), int, map, oneOf, s, string)
54 |
55 | type Route
56 | = Topic String
57 | | Blog Int
58 | | User String
59 | | Comment String Int
60 |
61 | routeParser : Parser (Route -> a) a
62 | routeParser =
63 | oneOf
64 | [ map Topic (s "topic" > string)
65 | , map Blog (s "blog" > int)
66 | , map User (s "user" > string)
67 | , map Comment (s "user" > string > s "comment" > int)
68 | ]
69 |
70 | -- /topic/pottery ==> Just (Topic "pottery")
71 | -- /topic/collage ==> Just (Topic "collage")
72 | -- /topic/ ==> Nothing
73 |
74 | -- /blog/42 ==> Just (Blog 42)
75 | -- /blog/123 ==> Just (Blog 123)
76 | -- /blog/mosaic ==> Nothing
77 |
78 | -- /user/tom/ ==> Just (User "tom")
79 | -- /user/sue/ ==> Just (User "sue")
80 | -- /user/bob/comment/42 ==> Just (Comment "bob" 42)
81 | -- /user/sam/comment/35 ==> Just (Comment "sam" 35)
82 | -- /user/sam/comment/ ==> Nothing
83 | -- /user/ ==> Nothing
84 | ```
85 |
86 |
89 |
90 | この`URL.Parser`パーサモジュールを使えば、URLをElmの素敵なデータへと完全に変換するコードを、とても簡潔に書くことができます!
91 |
92 |
93 |
96 |
97 | ## 例 2
98 |
99 |
102 |
103 | 個人用のブログで、次のようなURLのページがあるとしましょう。
104 |
105 | - `/blog/12/the-history-of-chairs`
106 | - `/blog/13/the-endless-september`
107 | - `/blog/14/whale-facts`
108 | - `/blog/`
109 | - `/blog?q=whales`
110 | - `/blog?q=seiza`
111 |
112 |
115 |
116 | ここで、それぞれのブログ投稿のページがあり、またオプショナルなクエリパラメータとしてブログの概要を渡すこともできるものとします。このとき、URLパーサを書くために、[`Url.Parser.Query`](https://package.elm-lang.org/packages/elm/url/latest/Url-Parser-Query)モジュールを追加しておく必要があります。
117 |
118 | ```elm
119 | import Url.Parser exposing (Parser, (>), (>), int, map, oneOf, s, string)
120 | import Url.Parser.Query as Query
121 |
122 | type Route
123 | = BlogPost Int String
124 | | BlogQuery (Maybe String)
125 |
126 | routeParser : Parser (Route -> a) a
127 | routeParser =
128 | oneOf
129 | [ map BlogPost (s "blog" > int > string)
130 | , map BlogQuery (s "blog" > Query.string "q")
131 | ]
132 |
133 | -- /blog/14/whale-facts ==> Just (BlogPost 14 "whale-facts")
134 | -- /blog/14 ==> Nothing
135 | -- /blog/whale-facts ==> Nothing
136 | -- /blog/ ==> Just (BlogQuery Nothing)
137 | -- /blog ==> Just (BlogQuery Nothing)
138 | -- /blog?q=chabudai ==> Just (BlogQuery (Just "chabudai"))
139 | -- /blog/?q=whales ==> Just (BlogQuery (Just "whales"))
140 | -- /blog/?query=whales ==> Just (BlogQuery Nothing)
141 | ```
142 |
143 |
146 |
147 | `>`演算子や`>`演算子を使うと、パースしようとしている実際のURLによく似た形で、パーサを書くことができます。`Url.Parser.Query`を追加すると、`?q=seiza`というようなクエリパラメータを扱うことができるようになります。
148 |
149 |
152 |
153 | ## 例 3
154 |
155 |
158 |
159 | 今度は、次のようなURLのページを持つ、ドキュメンテーションのウェブサイトを考えてみましょう。
160 |
161 | - `/Basics`
162 | - `/Maybe`
163 | - `/List`
164 | - `/List#map`
165 | - `/List#filter`
166 | - `/List#foldl`
167 |
168 |
171 |
172 | `Url.Parser`モジュールにある[`fragment`](https://package.elm-lang.org/packages/elm/url/latest/Url-Parser#fragment)パーサを使うと、これらのアドレスのパーサを次のように書くことができます。
173 |
174 | ```elm
175 | type alias Docs =
176 | (String, Maybe String)
177 |
178 | docsParser : Parser (Docs -> a) a
179 | docsParser =
180 | map Tuple.pair (string > fragment identity)
181 |
182 | -- /Basics ==> Just ("Basics", Nothing)
183 | -- /Maybe ==> Just ("Maybe", Nothing)
184 | -- /List ==> Just ("List", Nothing)
185 | -- /List#map ==> Just ("List", Just "map")
186 | -- /List# ==> Just ("List", Just "")
187 | -- /List/map ==> Nothing
188 | -- / ==> Nothing
189 | ```
190 |
191 |
194 |
195 | 同じように、URLフラグメントも扱うことができているのがわかります。
196 |
197 |
198 |
201 |
202 | ## 統合
203 |
204 |
207 |
208 | ここまでいくつかのパーサを見てきましたが、`Browser.application`プログラムにこれをどのように組み入れればいいのかを見ていく必要があります。先ほどのように現在のURLを単に保持するのではなく、URLを役に立つデータへとパースして、それを表示することができるでしょうか?
209 |
210 | ```elm
211 | TODO
212 | ```
213 |
214 |
217 |
218 | ここで重要な新しい要素は次の2点です。
219 |
220 |
224 |
225 | 1. `UrlChanged`メッセージを受け取ったときに、`update`はそのURLをパースします。
226 | 2. `view`関数は異なるアドレスそれぞれについて異なる内容を表示します!
227 |
228 |
231 |
232 | 風変わりすぎるということはまったくありませんね。素晴らしいです!
233 |
234 |
237 |
238 | しかし、10や20、あるいは100も異なるページがあるときはどうなるのでしょうか。ひとつの`view`関数にすべてを詰め込むのでしょうか?確かに、ひとつのファイルにすべてを書くというのは不可能です。いくつのファイルにわければいいのでしょうか? デイレクトリ構造はどうすべきでしょうか? これについては次の章で議論します!
239 |
240 |
--------------------------------------------------------------------------------
/docs/CNAME:
--------------------------------------------------------------------------------
1 | guide.elm-lang.jp
--------------------------------------------------------------------------------
/docs/architecture/hints.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elm-jp/guide/04969e546a0af71f5e0b8a37a57cbcd8a0990ee7/docs/architecture/hints.mp4
--------------------------------------------------------------------------------
/docs/architecture/try.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elm-jp/guide/04969e546a0af71f5e0b8a37a57cbcd8a0990ee7/docs/architecture/try.png
--------------------------------------------------------------------------------
/docs/get_started.html:
--------------------------------------------------------------------------------
1 | Redirecting... Page movedRedirecting... Page moved...
Click here if you are not redirected
--------------------------------------------------------------------------------
/docs/gitbook/fonts/fontawesome/FontAwesome.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elm-jp/guide/04969e546a0af71f5e0b8a37a57cbcd8a0990ee7/docs/gitbook/fonts/fontawesome/FontAwesome.otf
--------------------------------------------------------------------------------
/docs/gitbook/fonts/fontawesome/fontawesome-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elm-jp/guide/04969e546a0af71f5e0b8a37a57cbcd8a0990ee7/docs/gitbook/fonts/fontawesome/fontawesome-webfont.eot
--------------------------------------------------------------------------------
/docs/gitbook/fonts/fontawesome/fontawesome-webfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elm-jp/guide/04969e546a0af71f5e0b8a37a57cbcd8a0990ee7/docs/gitbook/fonts/fontawesome/fontawesome-webfont.ttf
--------------------------------------------------------------------------------
/docs/gitbook/fonts/fontawesome/fontawesome-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elm-jp/guide/04969e546a0af71f5e0b8a37a57cbcd8a0990ee7/docs/gitbook/fonts/fontawesome/fontawesome-webfont.woff
--------------------------------------------------------------------------------
/docs/gitbook/fonts/fontawesome/fontawesome-webfont.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elm-jp/guide/04969e546a0af71f5e0b8a37a57cbcd8a0990ee7/docs/gitbook/fonts/fontawesome/fontawesome-webfont.woff2
--------------------------------------------------------------------------------
/docs/gitbook/gitbook-plugin-fontsettings/fontsettings.js:
--------------------------------------------------------------------------------
1 | require(['gitbook', 'jquery'], function(gitbook, $) {
2 | // Configuration
3 | var MAX_SIZE = 4,
4 | MIN_SIZE = 0,
5 | BUTTON_ID;
6 |
7 | // Current fontsettings state
8 | var fontState;
9 |
10 | // Default themes
11 | var THEMES = [
12 | {
13 | config: 'white',
14 | text: 'White',
15 | id: 0
16 | },
17 | {
18 | config: 'sepia',
19 | text: 'Sepia',
20 | id: 1
21 | },
22 | {
23 | config: 'night',
24 | text: 'Night',
25 | id: 2
26 | }
27 | ];
28 |
29 | // Default font families
30 | var FAMILIES = [
31 | {
32 | config: 'serif',
33 | text: 'Serif',
34 | id: 0
35 | },
36 | {
37 | config: 'sans',
38 | text: 'Sans',
39 | id: 1
40 | }
41 | ];
42 |
43 | // Return configured themes
44 | function getThemes() {
45 | return THEMES;
46 | }
47 |
48 | // Modify configured themes
49 | function setThemes(themes) {
50 | THEMES = themes;
51 | updateButtons();
52 | }
53 |
54 | // Return configured font families
55 | function getFamilies() {
56 | return FAMILIES;
57 | }
58 |
59 | // Modify configured font families
60 | function setFamilies(families) {
61 | FAMILIES = families;
62 | updateButtons();
63 | }
64 |
65 | // Save current font settings
66 | function saveFontSettings() {
67 | gitbook.storage.set('fontState', fontState);
68 | update();
69 | }
70 |
71 | // Increase font size
72 | function enlargeFontSize(e) {
73 | e.preventDefault();
74 | if (fontState.size >= MAX_SIZE) return;
75 |
76 | fontState.size++;
77 | saveFontSettings();
78 | }
79 |
80 | // Decrease font size
81 | function reduceFontSize(e) {
82 | e.preventDefault();
83 | if (fontState.size <= MIN_SIZE) return;
84 |
85 | fontState.size--;
86 | saveFontSettings();
87 | }
88 |
89 | // Change font family
90 | function changeFontFamily(configName, e) {
91 | if (e && e instanceof Event) {
92 | e.preventDefault();
93 | }
94 |
95 | var familyId = getFontFamilyId(configName);
96 | fontState.family = familyId;
97 | saveFontSettings();
98 | }
99 |
100 | // Change type of color theme
101 | function changeColorTheme(configName, e) {
102 | if (e && e instanceof Event) {
103 | e.preventDefault();
104 | }
105 |
106 | var $book = gitbook.state.$book;
107 |
108 | // Remove currently applied color theme
109 | if (fontState.theme !== 0)
110 | $book.removeClass('color-theme-'+fontState.theme);
111 |
112 | // Set new color theme
113 | var themeId = getThemeId(configName);
114 | fontState.theme = themeId;
115 | if (fontState.theme !== 0)
116 | $book.addClass('color-theme-'+fontState.theme);
117 |
118 | saveFontSettings();
119 | }
120 |
121 | // Return the correct id for a font-family config key
122 | // Default to first font-family
123 | function getFontFamilyId(configName) {
124 | // Search for plugin configured font family
125 | var configFamily = $.grep(FAMILIES, function(family) {
126 | return family.config == configName;
127 | })[0];
128 | // Fallback to default font family
129 | return (!!configFamily)? configFamily.id : 0;
130 | }
131 |
132 | // Return the correct id for a theme config key
133 | // Default to first theme
134 | function getThemeId(configName) {
135 | // Search for plugin configured theme
136 | var configTheme = $.grep(THEMES, function(theme) {
137 | return theme.config == configName;
138 | })[0];
139 | // Fallback to default theme
140 | return (!!configTheme)? configTheme.id : 0;
141 | }
142 |
143 | function update() {
144 | var $book = gitbook.state.$book;
145 |
146 | $('.font-settings .font-family-list li').removeClass('active');
147 | $('.font-settings .font-family-list li:nth-child('+(fontState.family+1)+')').addClass('active');
148 |
149 | $book[0].className = $book[0].className.replace(/\bfont-\S+/g, '');
150 | $book.addClass('font-size-'+fontState.size);
151 | $book.addClass('font-family-'+fontState.family);
152 |
153 | if(fontState.theme !== 0) {
154 | $book[0].className = $book[0].className.replace(/\bcolor-theme-\S+/g, '');
155 | $book.addClass('color-theme-'+fontState.theme);
156 | }
157 | }
158 |
159 | function init(config) {
160 | // Search for plugin configured font family
161 | var configFamily = getFontFamilyId(config.family),
162 | configTheme = getThemeId(config.theme);
163 |
164 | // Instantiate font state object
165 | fontState = gitbook.storage.get('fontState', {
166 | size: config.size || 2,
167 | family: configFamily,
168 | theme: configTheme
169 | });
170 |
171 | update();
172 | }
173 |
174 | function updateButtons() {
175 | // Remove existing fontsettings buttons
176 | if (!!BUTTON_ID) {
177 | gitbook.toolbar.removeButton(BUTTON_ID);
178 | }
179 |
180 | // Create buttons in toolbar
181 | BUTTON_ID = gitbook.toolbar.createButton({
182 | icon: 'fa fa-font',
183 | label: 'Font Settings',
184 | className: 'font-settings',
185 | dropdown: [
186 | [
187 | {
188 | text: 'A',
189 | className: 'font-reduce',
190 | onClick: reduceFontSize
191 | },
192 | {
193 | text: 'A',
194 | className: 'font-enlarge',
195 | onClick: enlargeFontSize
196 | }
197 | ],
198 | $.map(FAMILIES, function(family) {
199 | family.onClick = function(e) {
200 | return changeFontFamily(family.config, e);
201 | };
202 |
203 | return family;
204 | }),
205 | $.map(THEMES, function(theme) {
206 | theme.onClick = function(e) {
207 | return changeColorTheme(theme.config, e);
208 | };
209 |
210 | return theme;
211 | })
212 | ]
213 | });
214 | }
215 |
216 | // Init configuration at start
217 | gitbook.events.bind('start', function(e, config) {
218 | var opts = config.fontsettings;
219 |
220 | // Generate buttons at start
221 | updateButtons();
222 |
223 | // Init current settings
224 | init(opts);
225 | });
226 |
227 | // Expose API
228 | gitbook.fontsettings = {
229 | enlargeFontSize: enlargeFontSize,
230 | reduceFontSize: reduceFontSize,
231 | setTheme: changeColorTheme,
232 | setFamily: changeFontFamily,
233 | getThemes: getThemes,
234 | setThemes: setThemes,
235 | getFamilies: getFamilies,
236 | setFamilies: setFamilies
237 | };
238 | });
239 |
240 |
241 |
--------------------------------------------------------------------------------
/docs/gitbook/gitbook-plugin-ga/plugin.js:
--------------------------------------------------------------------------------
1 | require(["gitbook"], function(gitbook) {
2 | // Load analytics.js
3 | gitbook.events.bind("start", function(e, config) {
4 | (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
5 | (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
6 | m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
7 | })(window,document,'script','//www.google-analytics.com/analytics.js','ga');
8 |
9 | var cfg = config.ga;
10 | ga('create', cfg.token, cfg.configuration);
11 | });
12 |
13 | // Notify pageview
14 | gitbook.events.bind("page.change", function() {
15 | ga('send', 'pageview', window.location.pathname+window.location.search);
16 | });
17 | });
18 |
--------------------------------------------------------------------------------
/docs/gitbook/gitbook-plugin-highlight/ebook.css:
--------------------------------------------------------------------------------
1 | pre,
2 | code {
3 | /* http://jmblog.github.io/color-themes-for-highlightjs */
4 | /* Tomorrow Comment */
5 | /* Tomorrow Red */
6 | /* Tomorrow Orange */
7 | /* Tomorrow Yellow */
8 | /* Tomorrow Green */
9 | /* Tomorrow Aqua */
10 | /* Tomorrow Blue */
11 | /* Tomorrow Purple */
12 | }
13 | pre .hljs-comment,
14 | code .hljs-comment,
15 | pre .hljs-title,
16 | code .hljs-title {
17 | color: #8e908c;
18 | }
19 | pre .hljs-variable,
20 | code .hljs-variable,
21 | pre .hljs-attribute,
22 | code .hljs-attribute,
23 | pre .hljs-tag,
24 | code .hljs-tag,
25 | pre .hljs-regexp,
26 | code .hljs-regexp,
27 | pre .hljs-deletion,
28 | code .hljs-deletion,
29 | pre .ruby .hljs-constant,
30 | code .ruby .hljs-constant,
31 | pre .xml .hljs-tag .hljs-title,
32 | code .xml .hljs-tag .hljs-title,
33 | pre .xml .hljs-pi,
34 | code .xml .hljs-pi,
35 | pre .xml .hljs-doctype,
36 | code .xml .hljs-doctype,
37 | pre .html .hljs-doctype,
38 | code .html .hljs-doctype,
39 | pre .css .hljs-id,
40 | code .css .hljs-id,
41 | pre .css .hljs-class,
42 | code .css .hljs-class,
43 | pre .css .hljs-pseudo,
44 | code .css .hljs-pseudo {
45 | color: #c82829;
46 | }
47 | pre .hljs-number,
48 | code .hljs-number,
49 | pre .hljs-preprocessor,
50 | code .hljs-preprocessor,
51 | pre .hljs-pragma,
52 | code .hljs-pragma,
53 | pre .hljs-built_in,
54 | code .hljs-built_in,
55 | pre .hljs-literal,
56 | code .hljs-literal,
57 | pre .hljs-params,
58 | code .hljs-params,
59 | pre .hljs-constant,
60 | code .hljs-constant {
61 | color: #f5871f;
62 | }
63 | pre .ruby .hljs-class .hljs-title,
64 | code .ruby .hljs-class .hljs-title,
65 | pre .css .hljs-rules .hljs-attribute,
66 | code .css .hljs-rules .hljs-attribute {
67 | color: #eab700;
68 | }
69 | pre .hljs-string,
70 | code .hljs-string,
71 | pre .hljs-value,
72 | code .hljs-value,
73 | pre .hljs-inheritance,
74 | code .hljs-inheritance,
75 | pre .hljs-header,
76 | code .hljs-header,
77 | pre .hljs-addition,
78 | code .hljs-addition,
79 | pre .ruby .hljs-symbol,
80 | code .ruby .hljs-symbol,
81 | pre .xml .hljs-cdata,
82 | code .xml .hljs-cdata {
83 | color: #718c00;
84 | }
85 | pre .css .hljs-hexcolor,
86 | code .css .hljs-hexcolor {
87 | color: #3e999f;
88 | }
89 | pre .hljs-function,
90 | code .hljs-function,
91 | pre .python .hljs-decorator,
92 | code .python .hljs-decorator,
93 | pre .python .hljs-title,
94 | code .python .hljs-title,
95 | pre .ruby .hljs-function .hljs-title,
96 | code .ruby .hljs-function .hljs-title,
97 | pre .ruby .hljs-title .hljs-keyword,
98 | code .ruby .hljs-title .hljs-keyword,
99 | pre .perl .hljs-sub,
100 | code .perl .hljs-sub,
101 | pre .javascript .hljs-title,
102 | code .javascript .hljs-title,
103 | pre .coffeescript .hljs-title,
104 | code .coffeescript .hljs-title {
105 | color: #4271ae;
106 | }
107 | pre .hljs-keyword,
108 | code .hljs-keyword,
109 | pre .javascript .hljs-function,
110 | code .javascript .hljs-function {
111 | color: #8959a8;
112 | }
113 | pre .hljs,
114 | code .hljs {
115 | display: block;
116 | background: white;
117 | color: #4d4d4c;
118 | padding: 0.5em;
119 | }
120 | pre .coffeescript .javascript,
121 | code .coffeescript .javascript,
122 | pre .javascript .xml,
123 | code .javascript .xml,
124 | pre .tex .hljs-formula,
125 | code .tex .hljs-formula,
126 | pre .xml .javascript,
127 | code .xml .javascript,
128 | pre .xml .vbscript,
129 | code .xml .vbscript,
130 | pre .xml .css,
131 | code .xml .css,
132 | pre .xml .hljs-cdata,
133 | code .xml .hljs-cdata {
134 | opacity: 0.5;
135 | }
136 |
--------------------------------------------------------------------------------
/docs/gitbook/gitbook-plugin-lunr/search-lunr.js:
--------------------------------------------------------------------------------
1 | require([
2 | 'gitbook',
3 | 'jquery'
4 | ], function(gitbook, $) {
5 | // Define global search engine
6 | function LunrSearchEngine() {
7 | this.index = null;
8 | this.store = {};
9 | this.name = 'LunrSearchEngine';
10 | }
11 |
12 | // Initialize lunr by fetching the search index
13 | LunrSearchEngine.prototype.init = function() {
14 | var that = this;
15 | var d = $.Deferred();
16 |
17 | $.getJSON(gitbook.state.basePath+'/search_index.json')
18 | .then(function(data) {
19 | // eslint-disable-next-line no-undef
20 | that.index = lunr.Index.load(data.index);
21 | that.store = data.store;
22 | d.resolve();
23 | });
24 |
25 | return d.promise();
26 | };
27 |
28 | // Search for a term and return results
29 | LunrSearchEngine.prototype.search = function(q, offset, length) {
30 | var that = this;
31 | var results = [];
32 |
33 | if (this.index) {
34 | results = $.map(this.index.search(q), function(result) {
35 | var doc = that.store[result.ref];
36 |
37 | return {
38 | title: doc.title,
39 | url: doc.url,
40 | body: doc.summary || doc.body
41 | };
42 | });
43 | }
44 |
45 | return $.Deferred().resolve({
46 | query: q,
47 | results: results.slice(0, length),
48 | count: results.length
49 | }).promise();
50 | };
51 |
52 | // Set gitbook research
53 | gitbook.events.bind('start', function(e, config) {
54 | var engine = gitbook.search.getEngine();
55 | if (!engine) {
56 | gitbook.search.setEngine(LunrSearchEngine, config);
57 | }
58 | });
59 | });
60 |
--------------------------------------------------------------------------------
/docs/gitbook/gitbook-plugin-search/search-engine.js:
--------------------------------------------------------------------------------
1 | require([
2 | 'gitbook',
3 | 'jquery'
4 | ], function(gitbook, $) {
5 | // Global search objects
6 | var engine = null;
7 | var initialized = false;
8 |
9 | // Set a new search engine
10 | function setEngine(Engine, config) {
11 | initialized = false;
12 | engine = new Engine(config);
13 |
14 | init(config);
15 | }
16 |
17 | // Initialize search engine with config
18 | function init(config) {
19 | if (!engine) throw new Error('No engine set for research. Set an engine using gitbook.research.setEngine(Engine).');
20 |
21 | return engine.init(config)
22 | .then(function() {
23 | initialized = true;
24 | gitbook.events.trigger('search.ready');
25 | });
26 | }
27 |
28 | // Launch search for query q
29 | function query(q, offset, length) {
30 | if (!initialized) throw new Error('Search has not been initialized');
31 | return engine.search(q, offset, length);
32 | }
33 |
34 | // Get stats about search
35 | function getEngine() {
36 | return engine? engine.name : null;
37 | }
38 |
39 | function isInitialized() {
40 | return initialized;
41 | }
42 |
43 | // Initialize gitbook.search
44 | gitbook.search = {
45 | setEngine: setEngine,
46 | getEngine: getEngine,
47 | query: query,
48 | isInitialized: isInitialized
49 | };
50 | });
--------------------------------------------------------------------------------
/docs/gitbook/gitbook-plugin-search/search.css:
--------------------------------------------------------------------------------
1 | /*
2 | This CSS only styled the search results section, not the search input
3 | It defines the basic interraction to hide content when displaying results, etc
4 | */
5 | #book-search-results .search-results {
6 | display: none;
7 | }
8 | #book-search-results .search-results ul.search-results-list {
9 | list-style-type: none;
10 | padding-left: 0;
11 | }
12 | #book-search-results .search-results ul.search-results-list li {
13 | margin-bottom: 1.5rem;
14 | padding-bottom: 0.5rem;
15 | /* Highlight results */
16 | }
17 | #book-search-results .search-results ul.search-results-list li p em {
18 | background-color: rgba(255, 220, 0, 0.4);
19 | font-style: normal;
20 | }
21 | #book-search-results .search-results .no-results {
22 | display: none;
23 | }
24 | #book-search-results.open .search-results {
25 | display: block;
26 | }
27 | #book-search-results.open .search-noresults {
28 | display: none;
29 | }
30 | #book-search-results.no-results .search-results .has-results {
31 | display: none;
32 | }
33 | #book-search-results.no-results .search-results .no-results {
34 | display: block;
35 | }
36 |
--------------------------------------------------------------------------------
/docs/gitbook/gitbook-plugin-search/search.js:
--------------------------------------------------------------------------------
1 | require([
2 | 'gitbook',
3 | 'jquery'
4 | ], function(gitbook, $) {
5 | var MAX_RESULTS = 15;
6 | var MAX_DESCRIPTION_SIZE = 500;
7 |
8 | var usePushState = (typeof history.pushState !== 'undefined');
9 |
10 | // DOM Elements
11 | var $body = $('body');
12 | var $bookSearchResults;
13 | var $searchInput;
14 | var $searchList;
15 | var $searchTitle;
16 | var $searchResultsCount;
17 | var $searchQuery;
18 |
19 | // Throttle search
20 | function throttle(fn, wait) {
21 | var timeout;
22 |
23 | return function() {
24 | var ctx = this, args = arguments;
25 | if (!timeout) {
26 | timeout = setTimeout(function() {
27 | timeout = null;
28 | fn.apply(ctx, args);
29 | }, wait);
30 | }
31 | };
32 | }
33 |
34 | function displayResults(res) {
35 | $bookSearchResults.addClass('open');
36 |
37 | var noResults = res.count == 0;
38 | $bookSearchResults.toggleClass('no-results', noResults);
39 |
40 | // Clear old results
41 | $searchList.empty();
42 |
43 | // Display title for research
44 | $searchResultsCount.text(res.count);
45 | $searchQuery.text(res.query);
46 |
47 | // Create an element for each result
48 | res.results.forEach(function(res) {
49 | var $li = $('', {
50 | 'class': 'search-results-item'
51 | });
52 |
53 | var $title = $('