関数
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)