6

フリップのように機能するが、はるかに一般的で、関数の任意のパラメーターを最後のパラメーターにすることができる Haskell 関数を作成したいと考えています。便宜上、 を使用pullして表します。

次のコードを書くのは簡単です。

Prelude> :t flip          --we just call this function a swap
flip :: (a -> b -> c) -> b -> a -> c
Prelude> :t (flip.)       --we just call this function a swap
(flip.) :: (a -> a1 -> b -> c) -> a -> b -> a1 -> c
Prelude> :t ((flip.).)    --we just call this function a swap
((flip.).) :: (a -> a1 -> a2 -> b -> c) -> a -> a1 -> b -> a2 -> c
Prelude> :t (((flip.).).) --we just call this function a swap
(((flip.).).)
  :: (a -> a1 -> a2 -> a3 -> b -> c) -> a -> a1 -> a2 -> b -> a3 -> c

さらに (.) をフリップに適用すると、任意の隣接するパラメーターのペアを交換できることがわかります。上記の結果を使用して、次のように記述できます。

Prelude> :t flip
flip :: (a -> b -> c) -> b -> a -> c
Prelude> :t (flip.) . flip
(flip.) . flip :: (a1 -> a -> b -> c) -> a -> b -> a1 -> c
Prelude> :t ((flip.).) . (flip.) . flip
((flip.).) . (flip.) . flip
  :: (a2 -> a -> a1 -> b -> c) -> a -> a1 -> b -> a2 -> c
Prelude> :t (((flip.).).) . ((flip.).) . (flip.) . flip
(((flip.).).) . ((flip.).) . (flip.) . flip
  :: (a3 -> a -> a1 -> a2 -> b -> c) -> a -> a1 -> a2 -> b -> a3 -> c

より多くのスワップを構成すると、任意のパラメーターを最後の場所に引っ張ることができることがわかります。したがって、多くの場合、ラムダ式を取り除くことができます。しかし、上記のエクスプレスは非常に肥大化しています。

私の主なアイデアはpull、上記の関数を一般化する関数を作成することです。pull大まかに以下のように動作します。

let f = undefined               --For convenience, we let f be undefined.

:t pull 0 (f::a->b->z)          --the type variable z is not a function type.
>pull 0 (f::a->b->z) :: b->a->z --pull is just like flip for 0 and a function of this type.
:t pull 0 (f::a->b->c->z)       --the type variable z is not a function type.
>pull 0 (f::a->b->c->z) :: b->c->a->z

:t pull 1 (f::a->b->c->z)       --the type variable z is not a function type.
>pull 1 (f::a->b->c->z) :: a->c->b->z
:t pull 1 (f::a->b->c->d->z)    --the type variable z is not a function type.
>pull 1 (f::a->b->c->d->z) :: a->c->d->b->z

:t pull 2 (f::a->b->c->d->z)    --the type variable z is not a function type.
>pull 2 (f::a->b->c->d->z) :: a->b->d->c->z
:t pull 2 (f::a->b->c->d->e->z) --the type variable z is not a function type.
>pull 2 (f::a->b->c->d->e->z) :: a->b->d->e->c->z

これを行うために多くの方法を試しました。最も単純なものは次のとおりです。

swap :: Word -> a -> a
swap 0 = flip
swap n = dot $ swap (n-1)

そしてghcは怒鳴り声のように不平を言いました、そして私はその理由を理解しています:

-- Prelude> :reload
-- [1 of 1] Compiling Main             ( ModifyArbitrayParameterOfAFunction.hs, interpreted )
--
-- ModifyArbitrayParameterOfAFunction.hs:4:21:
--     Occurs check: cannot construct the infinite type: c0 = a1 -> c0
--     Expected type: (a1 -> c0) -> c0
--       Actual type: (a1 -> c0) -> a1 -> c0
--     In the return type of a call of `modify'
--     Probable cause: `modify' is applied to too few arguments
--     In the first argument of `(.)', namely `(modify (n - 1) modi)'
--     In the expression: (modify (n - 1) modi) . f1
--
-- ModifyArbitrayParameterOfAFunction.hs:4:42:
--     Occurs check: cannot construct the infinite type: c0 = a1 -> c0
--     Expected type: a1 -> a1 -> c0
--       Actual type: a1 -> c0
--     In the second argument of `(.)', namely `f1'
--     In the expression: (modify (n - 1) modi) . f1
--     In an equation for `modify':
--         modify n modi f1 = (modify (n - 1) modi) . f1
-- Failed, modules loaded: none.

私の目標は希望的観測にすぎないのかもしれませんが、Haskell の型システムがラムダ式を書くことさえできることを考えると、これを行う方法があるに違いないとあえて言います。

4

3 に答える 3

4

関数のタイプは入力によって異なるため、これを通常の関数として行うことはできません。型クラスと関数の依存関係によって導入されたアドホックなポリモーフィズムでそれを行うことができます。ただし、その場合でも、Oleg のようなものを許可するには、多数の拡張機能が必要になりますIsFunction( http://okmij.org/ftp/Haskell/isFunction.lhsを参照)。これは、型クラスの再帰の基本ケースに到達したかどうかを識別できる欠落部分です。

于 2013-06-17T14:38:30.310 に答える