旧gaaamiiのブログ

間違ったことを書いている時があります。コメントやTwitter、ブコメなどでご指摘ください

Elmでランタイムエラーを起こす方法を教えてもらった

※この記事の検証環境はElm 0.19.1です。

Elmでランタイムエラーを起こす方法をTwitterで尋ねたところ、すぐに教えてもらえました。Elmコミュニティすごい!ありがとうございます!!

確認

以下のコードを書いてみたところちゃんとエラーになりました。

コード

div [ attribute "" "" ] []

出てきたエラー

InvalidCharacterError: Failed to execute 'setAttribute' on 'Element': '' is not a valid attribute name.

エラーになった!やった!

なぜランタイムエラーを起こしたくなったのか

以上でこの記事終わりでいいんですが、どうしてランタイムエラーを起こしたくなったのかというのも書いておきます。

Elm製アプリケーションを運用していて、Sentryの導入を考えていました。で、JavaScript側にSentryをセットアップしたときに、Elm側で起きたエラーも拾ってくれるのだろうか...?という疑問がありました。おもむろにSentryをセットアップして、JavaScriptからエラーが飛ぶことを確認した後に、さてElm側でもエラーを起こして確認だと思ったものの、あれ、Elmってランタイムエラー起きないことを謳ってるよな。起こす方法あるの...?となりました。

なぜattribute関数でランタイムエラーを起こせたのか

これも蛇足ですが、どうしてランタイムエラーを起こせたのでしょうか。

エラーの理由は、エラー文に書いてあるとおりです。setAttributeを不正な引数で呼んだからですね。ただ、今日はせっかくの休日なのでソースコードを見てみたいと思います。https://github.com/elm/html/blob/master/src/Html.elm を見ると、DOMを吐き出す関数はこんな感じです。

node : String -> List (Attribute msg) -> List (Html msg) -> Html msg
node =
  VirtualDom.node

なので、VirtualDom.nodeを見てみます。

node : String -> List (Attribute msg) -> List (Node msg) -> Node msg
node tag =
  Elm.Kernel.VirtualDom.node (Elm.Kernel.VirtualDom.noScript tag)

なるほど...?

var _VirtualDom_nodeNS = F2(function(namespace, tag)
{
    return F2(function(factList, kidList)
    {
        for (var kids = [], descendantsCount = 0; kidList.b; kidList = kidList.b) // WHILE_CONS
        {
            var kid = kidList.a;
            descendantsCount += (kid.__descendantsCount || 0);
            kids.push(kid);
        }
        descendantsCount += kids.length;

        return {
            $: __2_NODE,
            __tag: tag,
            __facts: _VirtualDom_organizeFacts(factList),
            __kids: kids,
            __namespace: namespace,
            __descendantsCount: descendantsCount
        };
    });
});

なるほど!!(わからん)

深入りするとElmのVirtualDom実装理解という別テーマになってしまうので、完全理解は諦めて、このモジュールでsetAttributeしているところを見てみます。

function _VirtualDom_applyAttrs(domNode, attrs)
{
    for (var key in attrs)
    {
        var value = attrs[key];
        typeof value !== 'undefined'
            ? domNode.setAttribute(key, value)
            : domNode.removeAttribute(key);
    }
}

ここだけ見れば難しくないです。domNodeはきっとvirtualdomがつくってくれた生DOMで、attrsはきっと {'class': 'hoge', 'id': 'fuga', ...}みたいな連想配列でしょう。ということは、Elmのdiv [ attribute "" "" ] []というコードを書いたとき、実行時には以下のようなことが起きているはずです。

domNode.setAttribute('', ''); // domNodeはdocument.createElement('div');相当のオブジェクト
Uncaught DOMException: Failed to execute 'setAttribute' on 'Element': '' is not a valid attribute name.
    at <anonymous>:1:4

まとめ

ざっくり追って、どういうことかわかりました。VirtualDomに興味が出てきたので、時間があるときに今度はそちらを深追いしてみたいです。