読者です 読者をやめる 読者になる 読者になる

Elmのエラーメッセージが分かりやすくなっている

だいぶ前にElm触ってゲーム作った時はElmに色々と不満があったんだけど、あれからElmもだいぶバージョンアップして0.15.1ではエラーが人フレンドリーになったらしい。前回の不満がどれだけ解決されているか調べてみよう。

関数の引数の数間違いが謎の型エラーになったりする

module Test where

import Html exposing (text)

message2: String -> String -> String
message2 world world2 = "Hello, " ++ world ++ " " ++ world2

main = text (message2 "World?" "World!")

というメッセージを表示するだけのコードを書いて、

main = text (message2 "World?")

と引数の数を間違うと、

The 1st argument to function `text` has an unexpected type.

8|        text (message2 "World?")
                ^^^^^^^^^^^^^^^^^
As I infer the type of values flowing through your program, I see a conflict
between these two types:

    String

    String -> String

8行目のここの引数がStringで無くString -> Stringだぞと出る。素晴らしい。抜群に分かりやすくなっとる。

レコードのアップデートと追加構文を間違った時のパーサーのエラーが謎だった

main関数を

main =
  let
     world = {str = "World??"}
     world2 = {world | str2 = "World!!"}
  in text (message2 world2.str world2.str2)

とし、

     world2 = {world | str2 = "World!!", str3 = "World?!"}

と許可されていない複数のフィールド追加を行うと、

11|      world2 = {world | str2 = "World!!", str3 = "World?!"}
                                           ^
I am looking for one of the following things:

    a closing bracket '}'
    an expression
    an infix operator like (+)
    whitespace

とちゃんとここで'}'を閉じろと言う。素晴らしい。

型の宣言でのエラーや変数名の重複でその行番号を教えてくれない

message2: String -> String -> String2

とすると

Cannot find type `String2`

5| message2: String -> String -> String2
                                 ^^^^^^^
Maybe you want one of the following?

    String

こう。

     world = {str = "World??"}
     world = {world | str2 = "World!!"}

とすると

Naming multiple values `world` in a single let-expression makes
things ambiguous. When you say `world` which one do you want?

11|      world = {world | str2 = "World!!"}
         ^^^^^
Find all the values named `world` in this let-expression and
do some renaming. Make sure the names are distinct!

こう。完璧ではないか。

乱数シードに現在時刻が欲しい

これはエラーメッセージとは関係ないのだが、乱数の初期化のために現在時刻が欲しかったのだが、その方法が良く分からなかった。

Elmの中だけでなんとかしようと思わないでportを使ってJavaScriptから供給すればいいらいいぞ。

portはElmとJavaScriptの間で値をやりとりする仕組みで、JavaScriptからsendされた値をElmのSignalとして受け取ることができる。

Elm.fullscreen(Elm.Test, {beginTime: Date.now()});

JavaScriptからElmを起動する時に引数で与えた値を、

port beginTime: Float

main =
  let
     world = {str = toString beginTime}

とportを介してもらえば良い。portってSignalじゃなくてもいいのね。たぶん不変な値限定だけど。

requestAnimationFrame対応

ElmのPongの例とかだと画面の更新はSignal.map inSeconds (fps 35)とか使っていてタイマーベースだけど、できればこれはrequestAnimationFrameにしたい。

それにはelm-animation-frameを使えば良い。elm package install jwmerrill/elm-animation-frameでパッケージをインストールし、

main =
  let
     frameNumSignal = Signal.map toString 
       (Signal.foldp (+) beginTime AnimationFrame.frame)
     messageSignal = Signal.map (message2 "World?!") frameNumSignal
  in
    Signal.map text messageSignal

のようにAnimationFrame.frameをトリガに画面を更新すれば良いらしいぞ。

Elmイケているではないか

バージョンアップで着実に欠点が埋められている感じ。portを使ったJavaScriptとの連携がうまく使えれば、ゲーム作りにもいいかも。あとは良いIDEが欲しいよな。