Elm を書いていて、いい感じにリファクタできた部分を説明する。
やりたいこと
文字列のリストを受け取って、先頭の文字列を数値に変えた物を返す関数を作る。
この際、途中で Nothing になった場合は 0 を返す。
はじめに実装したもの
getFirstNum : List String -> Int
getFirstNum strs = maybeStringToInt <| List.head strs
maybeStringToInt : Maybe String -> Int
maybeStringToInt s =
case s of
Nothing ->
0
Just str ->
stringToInt str
stringToInt : String -> Int
stringToInt s =
let
n = String.toInt s
in
case n of
Nothing ->
0
Just num ->
num
とりあえず動くものを最初に作ることはよくあるが、これはひどい...。
リファクタしてみる
文字列から数値への変換
Elm の公式ドキュメントをみてみる。
https://package.elm-lang.org/packages/elm/core/latest/Maybe
なるほど Maybe.withDefault というものがあるのか。
> Maybe.withDefault
<function> : a -> Maybe a -> a
それを使うと
stringToInt s = Maybe.withDefault 0 <| String.toInt s
case を書くことなく記述できる。
リストの先頭を取得する際の注意
リストから先頭要素をとる時に、空のリストを渡された時は Nothing となる。
つまり List.head は Maybe 型で返却される。
> List.head
<function> : List a -> Maybe a
返り値の型が Maybe である関数を合成したとき
今回やりたいことを実現するには、
- 文字列リストの先頭を取得する
- 文字列を数値に変換する
の二つの関数を合成すればよい。
ここで注意したいのは、両方の処理の返り値の型は Maybe である。
その時 Maybe.andThen というものを使うといい感じにかける。
> Maybe.andThen
<function> : (a -> Maybe b) -> Maybe a -> Maybe b
この関数は Maybe 型を返す関数と Maybe 型の値を受け取っていい感じに処理をしてくれる関数である。
今回の場合は String.toInt という Maybe 型を返す関数と、リストの先頭を取得した値 Maybe String を渡す。
Maybe.andThen String.toInt <| List.head strs
という感じで書く。
もし、リストの先頭が取れなかった時 (Nothing) は String.toInt の処理ではそのまま Nothing が返却される。リストの先頭が取得できた時 (Just "hoge") は String.toInt の処理で "hoge" が渡される。
これを使うことによって Maybe 型が返却される関数の合成が非常に楽になる。
それらを組み合わせる
Maybe.withDefault と Maybe.andThen を使って書き直してみる。
getFirstNum : List String -> Int
getFirstNum strs = Maybe.withDefault 0 <| Maybe.andThen String.toInt <| List.head strs
そうすることによって一行で書けるようになった!!
さらにリファクタ
<| を使うよりも |> の方が処理の順番通りに書けるので読みやすくなる。
getFirstNum : List String -> Int
getFirstNum strs = List.head strs |> Maybe.andThen String.toInt |> Maybe.withDefault 0
また、いちいちパスを書くのは面倒なので import して exposing させる。
import Maybe exposing (andThen, withDefault)
import String exposing (toInt)
import List exposing (head)
getFirstNum : List String -> Int
getFirstNum strs = head strs |> andThen toInt |> withDefault 0
これによってだいぶ読みやすくなった。
まとめ
withDefault に相当する関数は絶対あるだろうと思って調べたらあったので、ちゃんと調べることは大切だなと感じた(小並感)。