Elmで湯婆婆を実装してみる

# はじめに

少し前に流行った[湯婆婆](https://qiita.com/Nemesis/items/c7192a7c510788d2cba2)に便乗してみました。ついでに２年前に[入門すると言って何もやらなかったElm](https://qiita.com/ufoo68/items/52c5b04e349df527541f)で書いてみようと思いました。

# まずは完成品

![スクリーンショット 2020-11-19 13.06.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209689/d38620a2-206f-e1fa-66c5-5589c39dbea9.png)

- [コード全体](https://github.com/ufoo68/yubaba-elm/blob/master/src/Main.elm)
- [DEMO](https://hardcore-swartz-b9e7ab.netlify.app/)

# 実装をする

今回の要件は、**入力された文字列から１文字を選択してそれを出力する**ことですね。というわけでElmっぽく`model`と`update`と`view`を考えてみます。

## model

まず必要と考えたのは、**贅沢な名前（入力した名前）**ですね。これは`luxuriousName`として、あとは`hidden`を使って「今からお前の名前は(ry」の表示を切り替えるための`hideNewName`を用意します。

```elm
type alias Model =
    { luxuriousName : String, hideNewName : Bool, positionOfName : Int }


init : ( Model, Cmd Msg )
init =
    ( { luxuriousName = "", hideNewName = True, positionOfName = 0 }, Cmd.none )
```

あと本当は用意するつもりはなかった`positionOfName`ですが、これは`luxuriousName`からランダムに選んだ文字の位置になります。[Elmの乱数ライブラリ](https://package.elm-lang.org/packages/elm/random/latest/)を使う関係で用意したものです。これについては`update`のところで触れたいと思います。

## update

Elm初心者なので、もしかしたら突っ込みどころのある実装かもしれませんがこんな感じに書きました。`ChangeName`はHtmlの`input`要素から入力された文字で、`Send`は`button`が押されたかどうかのメッセージです。

```elm
type Msg
    = ChangeName String
    | Send
    | YubabaSelect Int


update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    case msg of
        ChangeName inputName ->
            ( { model | luxuriousName = inputName }, Cmd.none )

        Send ->
            ( { model | hideNewName = False }, Random.generate YubabaSelect (Random.int 0 (String.length model.luxuriousName - 1)) )

        YubabaSelect randomValue ->
            ( { model | positionOfName = randomValue }, Cmd.none )
```

ここで`YubabaSelect`という適当なメッセージを定義しました。最初は乱数生成は`rondom 0 N`のような形で実現すると考えていたのですが、[この記事](https://qiita.com/Keck/items/106c3d14e2b078acdc59)をみる感じそんな単純に実装できないことがわかったので、用意したものです。このメッセージは、最初に`Send`というメッセージを受け取った時に

```elm
Random.generate YubabaSelect (Random.int 0 (String.length model.luxuriousName - 1))
```

が実行されてその結果が`YubabaSelect randomValue`という形で受け取ります。また、任意の１文字を抽出するという処理のために[elm-community/list-extra](https://package.elm-lang.org/packages/elm-community/list-extra/latest/)を使いました。

```elm
convertNewName : Int -> String -> String
convertNewName index oldName =
    Maybe.withDefault "" (List.Extra.getAt index (String.split "" oldName))
```

一旦`String`を`List String`に変換します。`List.Extra.getAt`を使って`positionOfName`の位置の文字を取り出せば１文字の抽出ができるのですが、~~この`String`は`Maybe String`になるので`Nothing`だった場合の処理をしました。~~←`Maybe`の処理方法は、@ababup1192さんの指摘により`Maybe.withDefault`を使うことで`case of`を書くことなく処理ができました。

## view

あとは何も考えずに`view`を書きます。

```elm
view : Model -> Html Msg
view model =
    div []
        [ div [] [ text "契約書だよ。そこに名前を書きな。" ]
        , div []
            [ span []
                [ input [ class "input", placeholder "名前", value model.luxuriousName, onInput ChangeName ] []
                , button [ onClick Send ] [ text "書いた" ]
                ]
            ]
        , div [ hidden model.hideNewName ]
            [ text
                ("フン。"
                    ++ model.luxuriousName
                    ++ "というのかい。贅沢な名だねぇ。"
                )
            ]
        , div [ hidden model.hideNewName ]
            [ text
                ("今からお前の名前は"
                    ++ convertNewName model.positionOfName model.luxuriousName
                    ++ "だ。いいかい、"
                    ++ convertNewName model.positionOfName model.luxuriousName
                    ++ "だよ。分かったら返事をするんだ、"
                    ++ convertNewName model.positionOfName model.luxuriousName
                    ++ "!!"
                )
            ]
        ]
```

これで**湯婆婆変換された名前**が表示されるフォームが実装できました！！

# さいごに

Elmで**List操作をする**ことや**乱数を使う**ことが思ったより簡単ではなかったのですが、とりあえず書くことはできました。しかし慣例だと未入力の時の`Exception`表示をするみたいですが、Elmでやるのはちょっと難しそうな気がしたので断念しました。。。
自分でElmアプリを書いたのはこれが初めてなので指摘あればコメントくださると嬉しいです。