「Write Yourself a Scheme in 48 Hours」 を写経してみる(4) : ($) を使って関数を引数とする関数を作る。

Write Yourself a Scheme in 48 Hours/Evaluation, Part 1の中に次に示す apply 関数が定義してあります。
apply 関数は (+ 1 2 3) のような関数のときに func:"+"、 args:[Number 1,Number 2,Number 3] として、argsにfuncを適用させるための関数です。
lookup func primitives で 文字列 func:"+" から適用する関数 numericBinop (+)を探して来ます。その関数を args:[Number 1,Number 2,Number 3] に適用させたい訳です。で、該当する関数が見つからなかったときは評価の結果として(Bool False)を返す。

apply :: String -> [LispVal] -> LispVal
apply func args = maybe (Bool False) ($ args) $ lookup func primitives

maybe に渡される引数は以下の順序ですが apply 関数の中で使用されているmaybe関数は適用する関数とそれを適用されるデータの引数の位置が逆になっています。

> :i maybe
maybe :: b ->          -- Maybe a が Nothing のときに返すdefault値
        (a -> b) ->    -- Maybe a に適用する関数
        Maybe a  ->    -- (a -> b)が適用される値
        b 	       -- 関数の返す値の型

> maybe 0 (+1) (Just 10)     -- > 11
> maybe 0 (+1) Nothing       -- > 0
> maybe 0 ($ 10) (Just (+1)) -- > 11

なんと! 適用する関数と適用される値を逆に書けるのです。
($ 10) はどうなっているのでしょう。

> :i $
($) :: (a -> b) -> a -> b 	-- Defined in GHC.Base
infixr 0 $

> :t ($ 10)  -- > ($ 10) は(Num a)を引数にして b を返す関数が引数として、b を返す。
($ 10) :: (Num a) => (a -> b) -> b

> ($ [1..10]) (map (+1))    -- > [2,3,4,5,6,7,8,9,10,11]
> ($ [1..10]) (map show)    -- > ["1","2","3","4","5","6","7","8","9","10"]
> ($ [1..10]) (map even)    -- > [False,True,False,True,False,True,False,True,False,True]
> ($ [1..10]) (foldl (+) 0) -- > 55
> ($ [1..10]) (foldl (*) 1) -- > 3628800

($) を使って関数を引数とする関数を作っているのです。

 we apply it to the arguments using ($ args), an operator section of the function application operator.

引数の順序を入れ替えたapply関数を以下に示します。

apply :: String -> [LispVal] -> LispVal
apply func args = maybe (Bool False) ($ args) $ lookup func primitives

-- 順序を入れ替えたapply関数
apply :: String -> [LispVal] -> LispVal
apply func args = maybe (Bool False) 
                        -- fromMaybe で Just を取る。
                        -- Nothing のときは(numericBinop (const)) 
                        (fromMaybe (numericBinop (const)) $ lookup func primitives) 
                        (Just args) -- こっちにはJustを付ける。

下段の定義でも正常に動作します。