34

数学的には、関数合成操作は連想的です。したがって:

f . (g . h) = (f . g) . h

したがって、関数合成操作は、左結合または右結合のいずれかとして定義できます。

私の意見では、Haskell での通常の関数の適用 (つまり、$操作ではなく用語の並置) は連想のままなので、関数の合成も連想のままにしておく必要があります。結局、世界のほとんどの人 (私を含む) は、左から右に読むことに慣れています。

それにもかかわらず、Haskell の関数合成は右結合です。

infixr 9 .

関数合成操作が左結合であるか右結合であるかは、実際には違いがないことを私は知っています。それにもかかわらず、なぜ連想のままにされないのか知りたいです。この設計上の決定について、次の 2 つの理由が頭に浮かびます。

  1. Haskell の作成者は、関数合成が演算と同じくらい論理的に似ていることを望んでいました$
  2. Haskell の作成者の 1 人は、関数合成を左結合ではなく右結合にする方が直感的であることに気付いた日本人でした。

冗談はさておき、関数合成が Haskell で右結合的であるという有益な理由はありますか? Haskell の関数合成を連想のままにしておくと、何か違いはありますか?

4

1 に答える 1

41

厳密でない評価がある場合は、右結合性が役立ちます。非常にばかげた例を見てみましょう。

foo :: Int -> Int
foo = const 5 . (+3) . (`div` 10)

では、 が のときにこの関数が 0 と評価される.とどうなりinfixrますか?

foo 0
=> (const 5 . ((+3) . (`div` 10))) 0
=> (\x -> const 5 (((+3) . (`div` 10)) x)) 0
=> const 5 (((+3) . (`div` 10)) 0)
=> 5

さて、だっ.たらinfixl

foo 0
=> ((const 5 . (+3)) . (`div` 10)) 0
=> (\x -> (const 5 . (+3)) (x `div` 10)) 0
=> (const 5 . (+3)) (0 `div` 10)
=> (\x -> const 5 (x + 3)) (0 `div` 10)
=> const 5 ((0 `div` 10) + 3)
=> 5

(私はちょっと疲れています。これらの削減手順で間違いを犯した場合は、私に知らせてください。または、単に修正してください..)

はい、同じ結果になります。しかし、削減ステップの数は同じではありません。が左結合の場合.、特に、チェーンの前の関数がネストされた計算の結果を必要としないようにショートカットすることを決定した場合、合成操作をさらに減らす必要がある場合があります。最悪のケースも同じですが、最良のケースでは、右結合性が勝つ可能性があります。したがって、時には悪い選択ではなく、時には良い選択をしてください。

于 2013-12-03T06:22:26.253 に答える