17
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Elm初心者がElm 0.19でライフゲームを作ってハマったところ

Last updated at Posted at 2019-01-08

Elm 0.19 に入門してみました。
前に参加したElm入門ハンズオンで「Elmを勉強するならライフゲームを作るのが良いらしい」と聞いた気がしたのでライフゲームを作りました。

作ったもの

途中で止めたり、手動で次の世代を表示できたりします。

life-game-animation.gif

Dependency

Elm 0.19

実装

ソースコードはこちらです。
https://github.com/amanoese/elm-life-game

デザインはBootstrap ( Elm Bootstrap )を採用しました。

またライフゲームの描画にはSVG( SVG in Elm )を採用しました。

Canvs(elm-canvas)を利用するのが楽そう?ですが、
調べたところ elm-canvas は何度か仕様が大きく変更されているらしく、
参考にできるコードが存在するのか不安だったため採用を見送りました。

デプロイ(Netlify)

Netlify にデプロイします。
NetlifyでElmが直接サポートされているわけではありませんが、
Node.js と yarn がサポートされているため Elm のコードも Build&Deploy できます。

packege.json の dependency に elm を登録します。
また、Build スクリプトを記載します。

package.json
{
  ・・・省略
  "devDependencies": {
    "elm": "^0.19.0-bugfix2"
  },
  "scripts": {
    "build": "elm make src/Main.elm --output dest/index.html",
  }
}

あとは Netlify の Build Command に、yarn buildを記載すればデプロイできました。
Screenshot_2019-01-08 Build deploy Settings(1).png

ハマったところ

Elm 0.19 の Browser.element について情報が少ない

副作用を伴う処理はElm 0.19ではHtml.programではなくBrowser.elementを利用する必要があります。
一応解説してくれている方がいるのですが、初心者なのでハマりました。

Elm 0.18 で使われていたHtml.programは以下のような型です。

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

Elm 0.19での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

以前のHtml.programではinitが値だったのですが、Browser.elementでは関数になっており引数が必須になっています。*

初期化するための関数initを定義しないといけないのですが、
私は、Browser.sandboxで大枠を実装した後にBrowser.elementに移行しようしたため下記のようなエラーを出してしまいました。

Error
-- TYPE MISMATCH -------------------------------------------------- src\Main.elm

The 1st argument to `element` is not what I expect:

133|   Browser.element  { init = init , update = update, view = view ,subscriptions = subscriptions }
                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This argument is a record of type:

    { init : ( Model, Cmd Msg )
    , subscriptions : Model -> Sub Msg
    , update : Msg -> Model -> ( Model, Cmd Msg )
    , view : Model -> Html Msg
    }

But `element` needs the 1st argument to be:

    { init : flags -> ( Model, Cmd Msg )
    , subscriptions : Model -> Sub Msg
    , update : Msg -> Model -> ( Model, Cmd Msg )
    , view : Model -> Html Msg
    }

いや、わかってますよ……
この型引数 flag は下記のような初期化したいときのためにjavascript側から値を渡すために用意されてる引数らしいです。

sample.js
Elm.Main.init(0);
sample.elm
init : Int -> model
init num =  ( {num = num} , Cmd.none)

今回のアプリケーションでは初期値は不要なので引数を渡さない方法が知りたかったのですが、
1~2時間悩んだ結果下記のようなコードでいけるとわかりました。

init : () -> model
init _ = ( { num = 0 } , Cmd.none )

入力が存在しない場合は空タプルを渡すとよいのでしょうか?

Random(副作用) 難しい問題

Msg のパターンマッチを利用が必要になることは、解説無しには理解できなかったと思います。
下記の記事がとても参考になりました。@sandさんに感謝です。

それでも、ネストしたリストにランダムな値を与える綺麗な方法がわからなかったため、
今回は下記のように、一度ランダムな数をすべて生成した後、分割するようにして解決しています。

random-sample.elm
type Msg
  =  Init
  | RandomList (List Int)

init : () -> (Model, Cmd Msg)
init _ =
  ( { cells = initCells 0 , numberOfCells = 30 } ,Cmd.none)

update: Msg -> Model-> (Model , Cmd Msg)
update msg model =
  let { cells, numberOfCells } = model
  in
  case msg of
    Init ->
      (model, Random.generate RandomList (Random.list (numberOfCells ^ 2) (Random.int 0 1))) --フラットなrandamのintのリスト
    RandomList randomInts ->
      ( { model | cells = flattenCells <| split numberOfCells randomInts } -- リストを n * n のネストしたリストに分割
        , Cmd.none
      )

なんか遅い

最初は List だけで実装していたのですが、パフォーマンスが悪すぎて調べたところ実装がLinked-Listらしいです。
下記の記事が参考になりました。しかし、下記の記事は古いため直和型の部分については無視したほうが良い?と思います。

Listで書いていた部分をDictで書き直すと爆速になりました。@philoponさんに感謝です。

subscriptionsについて

Randomを理解した後だとハマらずに実装できました。

感想

今の知識でスクラッチしたらもっと綺麗に書けるなーと思うほど結構勉強になりました。

「Elmが凄い言語だよ!」って話は3~4年前から聞いていたのですが、
純粋関数型言語でフロントエンドが実装できるって触れ込みの誇大広告ぽさと、
若い言語で廃れていくんだろうな……という理由から勝手に避けていました。

しかし、2019年になった今でも人気のある言語の一つであり、
Elm入門ハンズオンに参加した結果かなり実践的な言語と実感しました。

Elm楽しいですね。

17
7
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
17
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?