Chcę napisać funkcję Haskella, która działa jak flip, ale jest znacznie bardziej ogólna i może uczynić dowolny parametr funkcji ostatnim parametrem. Dla wygody używamy pull
, aby go reprezentować.Chcę napisać funkcję podobną do `flip` w Haskell, aby pozbyć się wyrażeń lambda. Ale nie mogę sobie poradzić z tym typem:
Łatwo jest napisać następujący kod:
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
I okazuje się, że z bardziej stosowane odwrócić, może zamienić dowolną parę sąsiednich parametrów (.). I z powyższych wyników możemy napisać:
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
możemy stwierdzić, że z więcej swapy składzie, to może ciągnąć dowolny parametr na ostatnim miejscu. W wielu przypadkach możemy pozbyć się wyrażeń lambda. Ale powyższy ekspres jest bardzo rozdęty.
Moim głównym pomysłem jest wykonanie funkcji pull
w celu uogólnienia powyższych funkcji. Model pull
działa w przybliżeniu jak poniżej.
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
Próbowałem na wiele sposobów, aby to zrobić. Naivest jeden jest:
swap :: Word -> a -> a
swap 0 = flip
swap n = dot $ swap (n-1)
i ghc narzekali jak mieszka i rozumiem dlaczego:
-- 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.
Może moim celem jest tylko pobożne życzenie, ale biorąc pod uwagę system typu Haskell jest jeszcze możliwość zapisu wyrażeń lambda , Ośmielę się powiedzieć, że musi być jakiś sposób, aby to zrobić.
Przypuszczam, że jest to możliwe, ale zdecydowanie wymagałoby to łagodnej olegery. – Artyom
Nie możliwe w sposób, o którym wspomniałeś. Po prostu dlatego, że wartość parametru do pobrania (która jest dostępna) w czasie wykonywania nie może decydować o typie przyciągania (wymaganym w czasie kompilacji). – Ankur
Ale myślę, że funkcja pull może wybrać odpowiednią bazę typów w funkcji parametru, tylko jeśli jest sposobem na identyfikację funkcji parametru to funkcja jednoargumentowa, funkcja dwóch zmiennych lub funkcja wielu zmiennych. – TorosFanny