25

このuncurry関数は、次の2つの引数を取る関数に対してのみ機能します。

uncurry :: (a -> b -> c) -> (a, b) -> c

任意の数の引数を持つ関数をアンカリーしたい場合は、別々の関数を書くことができます。

uncurry2 f (a, b)          = f a b
uncurry3 f (a, b, c)       = f a b c
uncurry4 f (a, b, c, d)    = f a b c d
uncurry5 f (a, b, c, d, e) = f a b c d e

しかし、これはすぐに面倒になります。これを一般化する方法はありますか?1つの関数を書くだけで済みますか?

4

3 に答える 3

23

タプルパッケージuncurryNから試してください。オーバーロードのすべての形式と同様に、型クラスを使用して実装されます。この場合、最大15タプルのインスタンスを手動でスペルアウトします。これで十分です。

型クラスを使用して可変個引数関数も可能です。この一例はText.Printfです。この場合、それは関数型の構造的帰納法によって行われます。簡略化すると、次のように機能します。

class Foo t

instance Foo (IO a)
instance Foo b => Foo (a -> b)

foo :: Foo

fooタイプ、、などにインスタンス化できることIO aa -> IO b確認するのは難しいことではありませんa -> b -> IO cQuickCheckもこの手法を使用します。

ただし、nタプルはn + 1タプルとは完全に無関係であるため、構造的帰納法はタプルでは機能しません。そのため、インスタンスを手動でスペルアウトする必要があります。

于 2011-08-28T12:32:41.267 に答える
11

上書きされた型システムのトリックを使用してこの種のものを偽造する方法を見つけることは私の趣味の1つなので、結果がかなり醜いと言うときは私を信じてください。特に、タプルは再帰的に定義されていないため、タプルを直接抽象化する実際の方法はありません。Haskellの型システムに関する限り、すべてのタプルサイズは完全に異なります。

したがって、タプルを直接操作するための実行可能なアプローチでは、THを使用するか、tupleパッケージと同様に外部ツールを使用して、コードを生成する必要があります。

生成されたコードを使用せずにそれを偽造するには、最初に再帰的定義を使用する必要があります。通常、末尾をマークする「nil」値を持つ右ネストされたペア、またはそれら(,)()同等のものを使用します。(:)これは、およびの点でリストの定義に似ていることに気付くかもしれません[]。実際、この種の再帰的に定義されたフェイクタプルは、タイプレベルのデータ構造(タイプのリスト)または異種リストのいずれかとして見ることができます。 (たとえば、HListはこのように機能します)。

欠点には、この方法で構築されたものを実際に使用することは価値があるよりも厄介である可能性があるという事実が含まれますが、これに限定されません。型システムのトリックを実装するコードは通常、不可解で完全に移植性がなく、最終結果はそうではありません。とにかく必然的に同等です。たとえば、(a, (b, (c, ())))との間には重要な違いが複数あり(a, b, c)ます。

それがどれほど恐ろしいものになるかを知りたい場合は、GitHubにあるもの、特にここにあるものを見ることができます。

于 2011-08-28T19:19:41.993 に答える
2

uncurryさまざまな数の引数に対して機能する単一の定義を記述する簡単な方法はありません。

ただし、Template Haskellを使用して、手作業で作成する必要のあるさまざまなバリアントを生成すること可能です。

于 2011-08-28T12:32:16.743 に答える