8

私は現在、 20 の中級 Haskell 演習に取り組んでいますが、これは非常に楽しい演習です。Functorこれには、型クラスand Monad(およびFunctors とs を引数として取る関数)のさまざまなインスタンスを実装する必要がありますが、andMonadのようなかわいらしい名前を付けて、実行していることを偽装します (興味深いコードを作成します)。FurryMisty

私はこれのいくつかをポイントフリー スタイルで実行しようとしてきましたが、ポイントフル (?) 定義をポイントフリー定義に変換するための一般的なスキームがあるかどうか疑問に思いました。たとえば、 の型クラスは次のとおりですMisty

class Misty m where
  unicorn :: a -> m a
  banana :: (a -> m b) -> m a -> m b

(関数unicornbananareturn>>=であり、明らかでない場合に備えて)、これがapple( に相当するflip ap)の実装です。

apple :: (Misty m) => m a -> m (a -> b) -> m b
apple x f = banana (\g -> banana (unicorn . g) x) f

演習の後半ではliftMliftM2などのバージョンを実装します。私の解決策は次のとおりです。

appleTurnover :: (Misty m) => m (a -> b) -> m a -> m b
appleTurnover = flip apple

banana1 :: (Misty m) => (a -> b) -> m a -> m b
banana1 =  appleTurnover . unicorn

banana2 :: (Misty m) => (a -> b -> c) -> m a -> m b -> m c
banana2 f = appleTurnover . banana1 f

banana3 :: (Misty m) => (a -> b -> c -> d) -> m a -> m b -> m c -> m d
banana3 f x = appleTurnover . banana2 f x

banana4 :: (Misty m) => (a -> b -> c -> d -> e) -> m a -> m b -> m c -> m d -> m e
banana4 f x y = appleTurnover . banana3 f x y

さて、 (またはbanana1と同等) の適切な定義により、無意味なスタイルで実装することができました。しかし、他の 3 つの関数では、パラメーターを使用する必要がありました。liftMfmapappleTurnover

私の質問は次のとおりです。これらのような定義を無意味な定義に変えるためのレシピはありますか?

4

5 に答える 5

11

ユーティリティで示されているpointfreeように、そのような変換は自動的に行うことができます。ただし、結果は改善されるよりも難読化されることがよくあります。読みやすさを破壊するのではなく、向上させることが目標である場合、最初の目標は、式が特定の構造を持っている理由を特定し、適切な抽象化を見つけ、そのように物事を構築することです。

最も単純な構造は、単純な関数構成である線形パイプラインで物事を連鎖させるだけです。これだけでかなりのことができますが、お気づきのように、すべてを処理できるわけではありません。

1 つの一般化は、インクリメンタルに構築できる追加の引数を持つ関数に対するものです。一例を次に示しますonResult = (. (.))。ここで、onResultの初期値に n 回適用するidと、n 項関数の結果を含む関数合成が得られます。したがって、 を定義してからcomp2 = onResult (.)書き込みcomp2 not (&&)、NAND 演算を定義できます。

もう 1 つの一般化 (実際には上記を含む) は、より大きな値のコンポーネントに関数を適用する演算子を定義することです。ここでの例はfirstsecondin でControl.Arrow、2 タプルで動作します。Conal Elliott のSemantic Editor Combinatorsは、このアプローチに基づいています。

少し異なるケースは、いくつかの typebと function に複数引数の関数a -> bがあり、 を使用してそれらを複数引数の関数に結合する必要がある場合aです。2 項関数の一般的なケースでは、モジュールData.Functiononコンビネータを提供します。これを使用しcompare `on` fstて、最初の要素で 2 タプルを比較するような式を記述できます。

1 つの引数が複数回使用されている場合は、より厄介な問題ですが、抽出できる意味のある繰り返しパターンがここにあります。ここでの一般的なケースは、複数の関数を 1 つの引数に適用してから、別の関数で結果を収集することです。これはたまたまApplicative関数のインスタンスに対応しており(&&) <$> (> 3) <*> (< 9)、数値が特定の範囲内にあるかどうかをチェックするような式を記述できます。

これらのいずれかを実際のコードで使用する場合、重要なことは、式が何を意味し、それが構造にどのように反映されるかを考えることです。それを行ってから、意味のあるコンビネータを使用してポイントフリー スタイルにリファクタリングすると、通常の出力とは異なり、通常よりもコードの意図が明確pointfreeになります。

于 2011-12-30T17:26:25.857 に答える
5

はい!秘訣の 1 つは、ドットを中置記法ではなく前置記法で書くことです。そうすれば、関数合成のように見える新しいものを見つけることができるはずです。次に例を示します。

banana2 f = appleTurnover . banana1 f
          = (.) appleTurnover (banana1 f)
          = ((.) appleTurnOver) . banana1 $ f
banana2 = (appleTurnover .) . banana1

pointfree ユーティリティのソース コードにはさらに多くのものが含まれていますが、これは多くのケースを処理します。

banana4 f x y = appleTurnover . banana3 f x y
              = (.) appleTurnover ((banana3 f x) y)
              = ((.) appleTurnover) . (banana3 f x) $ y
banana4 f x = ((.) appleTurnover) . (banana3 f x)
            = (.) ((.) appleTurnover) (banana3 f x)
            = ((.) ((.) appleTurnover)) ((banana3 f) x)
            = ((.) ((.) appleTurnover)) . (banana3 f) $ x
banana4 f = ((.) ((.) appleTurnover)) . (banana3 f)
          = (.) ((.) ((.) appleTurnover)) (banana3 f)
          = ((.) ((.) ((.) appleTurnover))) (banana3 f)
          = ((.) ((.) ((.) appleTurnover))) . banana3 $ f
banana4 = ((.) ((.) ((.) appleTurnover))) . banana3
        = (((appleTurnover .) .) .) . banana3
于 2011-12-30T16:33:26.310 に答える
3

私は次の用語書き換えシステムを使用します。

\x -> f x ------> f 
f y x ----------> flip f x y
\x -> f (g x) --> f . g

不完全ですが (組み合わせ論理に関する本で理由を読んでください)、それで十分です:

ここにバナナ2があります:

banana2 f = appleTurnover . banana1 f

ラムダとして書き直します。

banana2 = \f -> appleTurnover . banana1 f

(.) を接頭辞スタイルで書く:

banana2 = \f -> (.) appleTurnover (banana1 f)

ご了承ください

banana2 = \f -> ((.) appleTurnover) (banana1 f)

したがって、ルール 3 を適用できます。fとは: (.) appleTurnover_gbanana

banana2 = ((.) appleTurnover) . banana1
于 2011-12-30T17:31:02.663 に答える
2

Haskell 関数定義を受け取り、それをスタイルpointfreeで書き直そうとするパッケージがあります。pointfree新しいアイデアを得るために、それを試してみることをお勧めします。詳細については、このページを参照してください。パッケージはこちらから入手できます。

于 2011-12-30T17:02:30.107 に答える