LoginSignup
2
3

More than 5 years have passed since last update.

関数合成むずかしいです。

Posted at

関数プログラミングを学ぼうと思って数週間。関数合成の考え方、捉え方でいつもハマる。もっと周辺知識を蓄えるまで後回しにしようとも思ったが、現時点の自分の理解として整理することにした。

とにかくややこしい

まず、おさらいとして、これは、わかる。

f :: b -> c
g :: a -> b
f . g :: a -> c

なぜなら、このように

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

(.) 演算子が「 1引数渡すと、g の値を経由してf の値を返す関数」を返すことが単純にわかるから。

では、この場合は?

f :: a -> b
g :: c -> d -> f
f . g :: ??? 

うーん。やはり整理すらできない。幾つかのサイト・書籍を参考にさせていただき、考察する。


ヒントはカリー化による部分適用にあるようだ。g は、部分適用により 1引数渡すと、1引数関数を返す関数 であるとも言える。そのため、f.g といった合成をした場合、 1引数渡すと、g の返り値(1引数関数)を経由して、f の値を返す関数となる。つまり、f の1引数が関数になる事を強制していて、これを満たさない場合にはコンパイルできない。
よって、このような実装は実現しない。

let f = \x -> x + 2
let g = \x y -> x * y -- \x -> (\y -> x * y) カリー化されてこのように扱われる
(f . g) 3 4 -- 本当は14になってほしいが...
<interactive>:283:1:
    No instance for (Num (a0 -> a0)) arising from a use of ‘it’
    In a stmt of an interactive GHCi command: print it

思った通りに合成したいなら g の返り値が値となるような工夫が必要となる。
例えば、

g 3 :: Num a => a -> a
(f . (g 3)) 4

このようにすれば、14 が求まる。ただし、この式はf.gg が1引数関数であることを強調しすぎていて、本質的な意味では分かりにくい。

一方、次のようにすることで、引数(値) を渡すと、gの値(1引数関数)を経由して、1引数関数を返す関数を合成する こともできる。

((f.) . g) 3 4 -- 14が求まる

これは、(.)演算子にf を部分適用すると、(f.) :: Num c => (a -> c) -> a -> c となる性質を利用したものである。これを踏まえて上の例をもう一度見てみると、カリー化により((f.) . g) 3 が1引数関数を返すので、その関数に4 を渡すことでようやく値としての14 が求まる。

参考サイト・書籍

サイト

Haskell の関数合成でひっかかってたこと
Haskellの関数合成

書籍

関数プログラミング実践入門

2
3
2

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
  3. You can use dark theme
What you can do with signing up
2
3