CCS制作発表会オンライン〜冷蔵庫の中身等を管理するWebAppの途中経過〜

はじめに

巷ではCOVID-19で外出"自粛"がなされていますね。サークル活動ももちろん影響を受けています。そのため千葉大学電子計算機研究会(以下CCS)のオンライン制作発表会の中間発表が行われる下りになった模様(企画してくれたゆんらべくんに感謝)。ということで作りかけですが、作品の説明を出そうと思います。

さてCCSではWeb系のことをやってる人がほとんどいないです。もっとみんなやってほしい。ということでWebアプリを作って布教しようとおもいなにか作ってみることにしました。

時間がなかったので中間発表ということで今回はフロントエンド(ブラウザで表示される側)を実装していきたいと思います。フロントエンドの言語としては色々ありますが、今回はElmを採用しました。Elmは公式サイト(https://elm-lang.org)では「A delightful language for reliable webapps.(堅牢なウェブアプリケーションのための楽しい言語)」と書かれています。書いてみるとほんとにそのとおりでまったくストレスがありません。特徴としてはできるかぎり言語をシンプルに保とうとしていること、そのために関数型言語のアプローチを採用していることがあげられます。そして、エラーメッセージが非常に丁寧です。そして多くのフロントエンドの言語と同様にJavaScriptに最終的にはコンパイルされます。

なにを作るかということですが、日常的に困ってることを解決できるものができればいいなと思い考え始めました。困っていることといえば買い物と冷蔵庫の中身の管理。買い物は下記の図のように延々とTwitterの@のうしろが増え続けます(しかもかいもののときみわすれて残り続ける)。

f:id:ikaro1192:20200420013522p:plain

そして冷蔵庫の中身の管理はおなじものを何個も買ってしまったり、逆にあるとおもってなかったり...これらが確認できるアプリがあれば便利ですね。ということで冷蔵庫管理アプリを作っていきましょう!

実装とか

ソースコードは下記で公開しています。src/Main.elmがelmの本体です。

github.com

動作動画は下記です。

動画投稿してから削除機能がないことに気づいたけどもういいや...こんな感じで動的なWebアプリケーションをつくることが可能です。制作時間は6時間ぐらい?

ソースコードを解説していこうと思ったのですが、そんなことは色んな人がたくさん記事をかかれているのでElmつかってここまでで思ったことをかいていきます。わたしの解説を読むより公式ドキュメントを読んだほうが114514倍勉強になると思います。 guide.elm-lang.org

英語が苦手な方は有志が日本語翻訳してくれています。

guide.elm-lang.jp

まずはエラーメッセージが丁寧であるということです。たとえば selectedRefrigeratorNumをselected_refrigerator_numしてしまった場合下記のようなエラーメッセージが表示されました。

-- TYPE MISMATCH -------------------------------------------------- src/Main.elm

The `model` record does not have a `selected_refrigerator_num` field:

77|             { model | selected_refrigerator_num = String.toInt selectedRefrigerator |> Maybe.withDefault 0 }
                          ^^^^^^^^^^^^^^^^^^^^^^^^^
This is usually a typo. Here are the `model` fields that are most similar:

    { selectedRefrigeratorNum : Int
    , refrigerators : List Refrigerator
    , userName : String
    }

So maybe selected_refrigerator_num should be selectedRefrigeratorNum?

約すと「modelはselected_refrigerator_numをもってないよ。これは普通typoであることがおおい。にてるフィールドは下記の通り。だからもしかしてselected_refrigerator_numはselectedRefrigeratorNumじゃないかな?」というような感じですね。よむだけでtypoだとわかるだけでなくどうすればいいかまで教えてくれてありがたいですね。  もう一つの例を下記に示します。

-- TYPE MISMATCH -------------------------------------------------- src/Main.elm

I cannot update the `selected_refrigerator_num` field like this:

77|             { model | selected_refrigerator_num = String.toInt selectedRefrigerator }
                                                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This `toInt` call produces:

    Maybe.Maybe Int

But it should be:

    Int

Note: The record update syntax does not allow you to change the type of fields.
You can achieve that with record constructors or the record literal syntax.

Hint: Use Maybe.withDefault to handle possible errors. Longer term, it is
usually better to write out the full `case` though!

toIntはMaybe Intを返すけど、ここではIntが要求されているよという話ですね。Maybeは失敗するかもしれないことを示しています。 String.toIntはIntに変換する関数ですが、Intに変換できない可能性があるのでMaybe Intを返すのですね。他の言語だったらここで実行時に例外が発生したりnullが返ってきてプログラムがクラッシュするところですが、Elmの場合はそれをコンパイル時に防いでくれます。そしてどうすればいいかかかれているHintをよんでみると、「Maybe.withDefaultをつかえばエラーに対処できるけど、caseをつかって分岐したほうが長期的にはよりよい」といっていますね。ここでは考えてみた結果、デフォルト値をうまく設定できないと思ったので下記のようにcaseをつかったパターンマッチにして解決しました。

        AddFood food ->
            let
                getedValue =
                    MyUtil.getValue model.selectedRefrigeratorNum model.refrigerators
            in
            case getedValue of
                Nothing ->
                    model

                Just refrigerator ->
                    { model | refrigerators = MyUtil.changeValue model.selectedRefrigeratorNum { refrigerator | foods = food :: refrigerator.foods } model.refrigerators }

こんな感じで型をうまくつかうことで適切なエラーメッセージやプログラムの仕様の詰めが甘い部分を指摘してくれます。

あとは状態がModelに集中している、関数型言語の特徴で至るところで書き換わったりしないってのが嬉しいポイントかなーっておもいます。

だんだんねむくなってきた....

今後

現状永続化されてないのでバックエンドをどうにかしたいですね。こちらも勉強がてらGoLangで書きたいと思っていましたが、Pythonでかくようなきがします。あとはCSSをちゃんとつけてきれいにしていきたい。テストもちゃんとしたい...

まとめ

CCSのみんな軽率にWebアプリケーションつくってこうな!そしてフロントエンドやるときはElmつかうと楽しいよ!わたしも勉強中なので勉強会しましょう!(雑