2015-10-21 10 views
14

私は関数(*~)を持っています。 x *~ yを評価するコストの大部分は、おおよそこれらの線に沿って第2引数を、検査から来ている:右手演算子セクションの部分評価

(*~) :: a -> b d -> c d a 
x *~ y = case y' of 
      Bar -> cheapFunction y' x 
      Baz -> cheapFunction2 y' x 
      Quux -> cheapFunction3 y' x 
    where 
    y' = expensive y 

は、部分的に(*~ y)のようなオペレータのセクションを評価するためにGHCを説得するためにいくつかの方法はありますか?

私はそれが好きで書き換えてみました:

(*~) = flip go 
    where 
    go y = let y' = expensive y 
      in case y' of 
       Bar -> cheapFunction y' 
       Baz -> cheapFunction2 y' 
       Quux -> cheapFunction3 y' 
が、助けていないようでした。 flipは、反転する前にすべての議論が必要なので、これは考えられますか?

オペレータ自体を反転させるのが単なる方法ですが、既存の表記法と並んでいるため、高価なオペランドが右側にある場合は、はるかに自然に読み込みます。

正しく製作された{-# RULE #-}はここで私を救うことができますか?もしそうなら、それは何を言いますか? (特に、ルールがマッチを探す前にセクション構文がどの程度離れているかは不明です)

+1

私は部分評価があなたを買うだろうか分かりません。 「y」は既に共有されます。あなたはメモをしたいですか?自分でメモを追加する必要があります。どのようなルールを書いておきたいですか? –

+1

カスタム「フリップ」を使用するとどうなりますか? 'flip 'f x = \ y - >インラインf y x' – dfeuer

+2

@ReinHenrichsすでに' y'は共有されますか?もしそうなら、私の理解はかなり壊れており、私はどのように答えを受け入れるのが大好きです。 'fmap(*〜y)someLongList'を実行すると毎回再計算されません。 –

答えて

5

このような最適化をトリガーするには、関数がインライン化されていることを確認する必要があります。 {-# INLINE (*~) #-}プラグマを(*~)関数の宣言の前に置きます。私はそれがあなたの問題を解決することを保証することはできませんが、私はそれが近づいているのを見る唯一の方法です。私は後で"ghc-core"のようなツールを使って、生成されたCoreコードを調べてみましょう。

ただし、問題は実際には不適切なコード構成の兆候です。あなたの機能は、無関係の複数のことをしています。 expensive yを単純に取り除く必要があります。その場合、問題はそのまま消去されます。つまり、使用パターンはx *~ yの代わりにx *~ expensive yである必要があります。

+1

これは良い点であり、問​​題を解決します。しかし、 'expensive 'の結果型は現在、隠された実装の詳細です。なぜなら'(*〜) 'を公開するライブラリのユーザにとっては本当に興味がないからです。 –

+2

実際には、あなたのケースでは隠れた実装の詳細ではありません。どちらかといえば、別の方法で公開するだけです。あなたはまたあなたの意図を難読化し、タイプシステムからこれを隠すので、そうでなければあなたの特別な機能がどのように使用されるべきかをユーザに知らせる必要があります。これは、ユーザビリティの観点からはあまりうまくいかないでしょう。そして、再び、それは懸念の分離に向かうだろう。あなたはあなたのアプローチが既に間違っているという兆候のリストを持っているので、2度考えてください。 –

+0

良い点があります。 –