Комбинатор с лямбда-исчислением S , Sabc = (a <*> b) c = a c $ b c
,
uncurry f (x,y) = f (fst (x,y)) (snd (x,y))
= (f . fst <*> snd) (x,y)
uncurry f = (<*> snd) (f . fst)
= (<*> snd) . (. fst) $ f
, следовательно,
uncurry :: (a -> b -> c) -> (a, b) -> c
uncurry = (<*> snd) . (. fst)
( edit: )
Тем не менее, это гораздо более читабельно (и несколько проясняет) с одним явным аргументом, оставленным там, как показано выше:
uncurry f = f . fst <*> snd
Но то этот вариант, показанный Jon Purdy в комментариях ,
uncurry f = liftA2 f fst snd
, может быть самым ясным.
Это потому, что для функций , монада и аппликатив эквивалентны по мощности,
(k =<< f) x = k (f x) x = flip k x (f x) = (flip k <*> f) x
-- i.e., uncurry f = flip (f . fst) =<< snd
и liftA2 f fst snd
означает, по определению,
= [ f a b | a <- fst ; b <- snd ]
=
do { a <- fst ;
b <- snd ;
return (f a b)
}
= \x -> let
{ a = fst x ;
b = snd x ;
}
in const (f a b) x
(первое, написанное с помощью Monad Computing). Таким образом,
uncurry f x = liftA2 f fst snd x
= let
{ a = fst x ;
b = snd x ;
}
in f a b
=
f (fst x) (snd x)
=
(f . fst <*> snd) x
=
(flip (f . fst) =<< snd) x
=
flip (f . fst) (snd x) x
=
(flip (f . fst) . snd) x x
=
join (flip (f . fst) . snd) x
=
join (flip (f . fst) <$> snd) x
, следуя хорошо известной эквивалентности , k =<< m = join (fmap k m)
(а для функций (<$>) = fmap = (.)
).
Итак, мы нашли еще одно выражение здесь
uncurry f x = join (flip (f . fst) . snd)
= liftA2 f fst snd
= f . fst <*> snd
= flip (f . fst) =<< snd
Один liftA2
может быть самым четким и наименее шумным.