LoginSignup
4

More than 3 years have passed since last update.

[Elm] 関数合成を理解する

モチベーション

  • |> は使えるけど >> がいまいち使いこなせない
  • なんとなくで .member >> .id とか使ってるけど、もやもやする

パイプと関数合成の違い

図で違いを見てみる

パイプは、一つ処理をしてその結果を次の処理に渡して〜という実行を繋げていく。

関数合成は、複数の処理を処理を1つにまとめて一気に実行する。

コードで違いを見てみる

ここではサンプルとして、ユーザーが18才以上かどうかで出力メッセージを分ける関数の実装を考えてみる。

type alias User =
    { name : String
    , age : Int
    }

user1 : User
user1 =
    { name = "Yuna"
    , age = 22
    }

user2 : User
user2 =
    { name = "Taro"
    , age = 15
    }

helloMessage : Bool -> String
helloMessage isAdult =
    if isAdult then
        "ようこそ!"

    else
        "未成年は入れません。"

まず、パイプを使った実装が hello1
以下のように順々に処理が流れていると捉えることができる。

  1. userのageを取ってくる
  2. 1の結果(userのage)を18以上かチェック
  3. 2の結果(18以上かどうか)をhelloMessage関数に渡し、出力テキストを振り分ける
hello1 : User -> String
hello1 user =
    user |> .age |> (<=) 18 |> helloMessage
    -- 向きを逆にしても内容は同じ
    -- helloMessage <| (<) 18 <| .age <| user

型を見てみてみよう。
パイプの実装は以下のようになっている。

(|>) : a -> (a -> b) -> b

これをhello1に当てはめてみると、以下の図のようになる。

次に、関数合成を使った実装が hello2
こちらの場合は、上記の1, 2, 3の処理を全てまとめて、「userのageを取ってきて18以上かチェックして出力テキストを振り分ける」関数として捉えることができる。

hello2 : User -> String
hello2 user =
    (.age >> (<=) 18 >> helloMessage) user
    -- 向きを逆にしても内容は同じ
    -- (helloMessage << (<=) 18 << .age) user

型を見てみよう。
関数合成の実装は以下のようになっている。

(>>) : (a -> b) -> (b -> c) -> a -> c

これをhello2に当てはめてみると、以下の図のようになる。

さらに関数合成では以下の hello3 ように書ける。

hello3 : User -> String
hello3 =
    .age >> (<=) 18 >> helloMessage

この場合では、userを引数に取っていない。この書き方がElm始めた時からすごい不思議だったんだけど、一度わかったら全然難しいことじゃなかったよ〜
関数の捉え方がわかると、これはすぐに理解できると思う。hello1, hello2は「userを受け取ってStringを返す関数」として捉えているのに対し、hello3は「User -> Stringという値」として捉えている。関数型言語でよく出てくる全ての関数は値だ〜みたいなのはこういうことなんだなぁと、Elmで初めて関数型言語を学んでしっくりきた。ちなみにこの書き方は、Haskellではポイントフリースタイルと言われている。

関数合成の実開発での使われ方

プロダクトの実開発では、やたらめったら関数合成が使われている訳ではなく、基本はパイプラインが多い。ただ、関数を値として扱える性質から、関数合成でまとめた関数を別の関数の引数に渡したり、パイプラインの途中の処理で使ったりといった用途で活躍しているように思う。
あと関数合成を使うと短くスマートに書けるので、単純にかっこいい!
ただ、Elm初心者、関数型言語初心者には、パイプラインの方がコードを追いやすいと思う。上級者・初心者とも関数合成に対する理解レベルを合わせて利用する、関数合成は乱用せず基本的にパイプラインを使うようにするなど、チームのガイドラインを設けてもいいかも?

まとめ

  • パイプも関数合成も、同じ処理が書ける。順にやるかまとめてやるかの違い。
  • パイプも関数合成も、向きを逆にしても処理の内容は変わらない。
  • 関数合成を使うと複数の処理を1つの関数としてまとめられる。
  • Elmでは関数は値にもなるので、関数合成でまとめた関数を別の関数の引数に渡したりすることができる。ポイントフリースタイル化もできる。
  • 初心者にはパイプの方がコードを追いやすいので乱用は禁物かも。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
4