フリップのように機能するが、はるかに一般的で、関数の任意のパラメーターを最後のパラメーターにすることができる 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 の型システムがラムダ式を書くことさえできることを考えると、これを行う方法があるに違いないとあえて言います。