Elmで入力状態を保持するときにMaybeを使うメリット

以下の内容は「僕はこうしているよ」っていう提案なので常に Maybe を使ったほうがいいよっていう主張ではありません。
むしろ「こういうときには Maybe 使わないようがいいよ」みたいな情報をいただければ、よりよい内容に進化させられるのでぜひご協力くださいm(_ _)m

概要

Elm は input タグの value を取得する方法を持たない1ため、Model に input タグに入力された値を保持して、入力があるたびに input イベントや change イベントで Model の値を更新して管理します。

type alias Model =
    { inputEmail : String
    }


init : ( Model, Cmd Msg )
init =
    ( { inputEmail = ""
      }
    , Cmd.none
    )


type Msg
    = InputEmail String


update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    case msg of
        InputEmail email ->
            ( { model
                | inputEmail = email
              }
            , Cmd.none
            )


view : Model -> Html Msg
view model =
    div
        []
        [ text "email"
        , input
            [ type_ "email"
            , onInput InputEmail
            , value model.inputEmail
            ]
            []
        ]

この記事では、input の値を保持する際に Maybe を使うことの利点を示します。
つまり、上記の例を以下のように書き換えるとどんな嬉しいことがあるかをご説明します。

type alias Model =
    { inputEmail : Maybe String
    }


init : ( Model, Cmd Msg )
init =
    ( { inputEmail = Nothing
      }
    , Cmd.none
    )


type Msg
    = InputEmail String


update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    case msg of
        InputEmail email ->
            ( { model
                | inputEmail = Just email
              }
            , Cmd.none
            )


view : Model -> Html Msg
view model =
    div
        []
        [ text "email"
        , input
            [ type_ "email"
            , onInput InputEmail
            , value <| Maybe.withDefault "" model.inputEmail
            ]
            []
        ]

利点1: 未入力であることを明示できる

Maybe を使うことで、その入力欄が

  • まったく入力されていない状態なのか (Nothing)
  • 一度入力して、あとで空白に戻した状態なのか (Just "")

を別の値で表現できます。

これができると、いろいろな応用がしやすくなります。

応用例: 初期状態では必須項目に「入力されていません」と表示しないUI

必須項目に入力がないときに、「入力されていません」と表示されるUIはよく見かけますが、
ページを開いた瞬間から表示されていると
「宿題やろうと思ってたのに、お母さんが『宿題やったの?』って聞いてくるからやる気なくなったじゃんか!」
っていう小中学生のころのあの気持ちを思い出してしまいます。

そこで、EFO (Entry Form Optimization) などでよく推奨されるのが、

  • ページを開いた瞬間には必須入力のエラーを表示しない
  • 送信ボタンを押したり、一度入力して空白に戻した時にはエラーを表示する

という挙動です。

  • まだ何もいじっていない状態 (Nothing)
  • 一度入力して空白に戻した状態 (Just "")

の2つを Model の状態として明確に区別できていると、こういったUIが実現しやすくなります。

将来の拡張性

もちろん、必ずしも毎回そのようなUIになるとは限りませんし、
Elm は型とコンパイラに守られた言語なので、最初は Maybe を使わずに書いて、あとで必要になった時に Maybe を使うように書き換えてもデグレはほとんど起きないでしょう。

将来の拡張性のために今のうちに Maybe を使っておくか、YAGNI的にまずは Maybe なしで始めるか、状況によって使い分けるのが現実解になると思います。

利点2: lazy と併用しても正しく動く

inputtextarea はタグ自体が内部状態を持っているため lazy と合わせて使うと意図しない挙動を示します
詳しい説明はリンク先に譲りますが、全角で入力された内容を自動で半角に変更(僕はNormalizationと呼んでます)したりすると、入力欄に表示されている内容と Model に保持されている内容に齟齬が生じます。

リンク先の記事では独自に定義した専用の IString の仕様を提案していますが、実は単純に Maybe String に変更するだけでも IString と同じ効果があります。

記事への反応

「むしろ Maybe じゃ足りないこともある」という意見もいただきました。

Model はあくまでも現在の状態を保持するものなので、見た目上同じに 見える からといって同じ状態にしてしまうのではなく、Union Type を活用して Model 上はできるだけ informative にすることで、結果的に変なフラグなども不要になってシンプルに拡張性高く記述することができると思います。

sakura-chan.jpg

まとめ

lazy と併用し、Normalization を行う場合、「利点1」も加味して考えるのであれば、最初から Maybe String などを使うと便利です。
それ以外の場合は、状況に応じて Maybe を使う選択肢も視野に入れて設計するとあとあと困ることが少なくなると思います。


  1. 実際にはportを使ったりすればできますが、推奨するやり方ではないのでここでは無視します。 

Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account log in.