関数

8.3 部分適用

セクションについて

セクションと言われると、…何だか難しく聞こえますけど、でもそんなことはありません。
二項演算子の部分適用のことなんですね。
たとえば

Prelude> :t (+ 4)
(+ 4) :: (Num a) => a -> a
Prelude> :t (* 2)
(* 2) :: (Num a) => a -> a
Prelude> :t (4 /)
(4 /) :: (Fractional a) => a -> a


のように、二項演算子を部分適用すると、通常の関数と同じように別の関数が作られます。
もちろん組み合わせて使うことも出来るようです。

Prelude> :t (2 * ((* 3)(4 *)))
(2 * ((* 3)(4 *))) :: (Num (a -> a), Num a) => a -> a

う〜ん、カッコがたくさんあって分かりにくいですね。

subtract関数について

本文中には、「-」演算子のセクションに対応する関数を作るためにsubstractを使う、とありました。
ちょっと気になったのでsubtract関数について調べてみました。
Preludeモジュールの定義によると…

subtract         :: (Num a) => a -> a -> a
subtract         =  flip (-)


とあります。
(=>は考えないとして)二つの引数をとって同じ型の値を戻す関数です。
中身は(-)にflip関数を適用しています。
…?(-)って何だ?ということで調べてみると、

Prelude> :i (-)
class (Eq a, Show a) => Num a where
  ...
  (-) :: a -> a -> a
  ...
        -- Defined in GHC.Num
infixl 6 -


あぁ、そうか二項演算子の「-」のことですね。
では最後にflip関数はこんな感じでした。

-- flip f  takes its (first) two arguments in the reverse order of f.

flip             :: (a -> b -> c) -> b -> a -> c
flip f x y       =  f y x


コメントによると、「flip f」は引数を元々のfの二つの引数を逆転して取るとのこと。
flipの型もそうなっています。


以上をもって、こう解釈しました。
subtract関数は「-」演算子の引数を逆転して適用する関数である、と。
そうか、Haskellでは、

Prelude> :t (1 -)
(1 -) :: (Num a) => a -> a


はセクションとして認識されるけど、

Prelude> :t (-1)
(-1) :: (Num a) => a


はセクションとして認識されずに数値として認識されてしまう。
これが問題でした。
だったら、全て前者に合わしてしまえ、という考えなんですね。

変数削減について

これが自分的にはもっとも合点がいく部分適用の事例ですね。
※別に他の事例が納得いかないってわけじゃないですよ。


始めに汎用的な関数を用意しておき、
部分適用することで、より専門的な関数を作っていくという考え方。
関数プログラミングしているなぁ、と感じてしまいます(錯覚?)。


簡単な例ではこんな感じ。

main = do print $ myPlus [1,2,3]

{--
  引数のリストの各要素に2を足す
 --}
myPlus :: [Int] -> [Int]
myPlus = map (+ 2)