如何在没有应用程序的情况下在Haskell中实现uncurry point-free?
How to implement uncurry point-free in Haskell without app?
我一直想知道不同的标准 Haskell 函数如何实现无点。目前,我对 uncurry
很感兴趣,我觉得这个很重要。
主要问题是我们无法(或者在我看来)对参数进行分组。如果我们使用 uncurry
(事实上,uncurry ($)
就足够了),解决方案将非常简单:
- 制作一个元组
(f, (x, y))
。
- 将
assoc1 :: (a, (b, c)) -> ((a, b), c)
应用于元组并得到((f, x), y)
。
- 将未柯里化的
($)
应用于该对的第一个元素并得到 (f x, y)
.
- 将未柯里化的
($)
应用于对本身并得到 f x y
.
如果没有 uncurried ($)
,我们将不得不分别提取该对的两个元素。例如:
uncurry f pair = f (fst pair) (snd pair)
我不认为这是实现无点的顺利方式。
事实上,我们已经按照我们的要求得到了这个未柯里化的($)
:Control.Arrow.apply
(其他对解决方案组合器有用的东西也可以从Control.Arrow
导入)。因此:
import Control.Arrow ((>>>), (&&&), first, app)
myUncurry = let myAssoc1 = (fst &&& (fst . snd)) &&& (snd . snd)
in (,) >>> (>>> myAssoc1 >>> first app >>> app)
然而,这感觉有点像作弊。
是否有其他解决此问题的方法不需要 app
?
join
在函数上给你 (a -> a -> b) -> a -> b
,所以:
myUncurry f = join (\x y -> f (fst x) (snd y))
myUncurry f = join (\x -> f (fst x) . snd)
myUncurry f = join ((.snd) . f . fst)
myUncurry f = join ((.fst) ((.snd) . f))
myUncurry f = join ((.fst) ((.) (.snd) f))
myUncurry = join . (.fst) . \f -> (.) (.snd) f
myUncurry = join . (.fst) . ((.snd).)
join . (.fst) . ((.snd).)
确实非常可读
使用 Lambda 微积分的 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)
(编辑:)
它仍然更具可读性(并且在某种程度上阐明了),但保留了一个明确的论点,如上所示:
uncurry f = f . fst <*> snd
然后这个变体,显示为 Jon Purdy in ,
uncurry f = liftA2 f fst snd
可能是最清楚的了。
这是因为对于函数来说,monad 和 applicative 是等价的,
(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 Comprehensions写的)。因此,
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
遵循众所周知的 equivalence、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
可能是最清晰、噪音最小的。
.
的朴实机械解决方案
uncurry f (x,y) = f x y
uncurry f p = f (fst p) (snd p)
uncurry f = \p -> f (fst p) (snd p)
uncurry f = (<*>) (\p -> f (fst p)) (\p -> snd p)
uncurry f = (<*>) (f . fst) snd
uncurry = \f -> (<*>) (f . fst) snd
uncurry = flip (\f -> (<*>) (f . fst)) snd
uncurry = flip ((<*>) . (\f -> f . fst)) snd
uncurry = flip ((<*>) . (. fst)) snd
我一直想知道不同的标准 Haskell 函数如何实现无点。目前,我对 uncurry
很感兴趣,我觉得这个很重要。
主要问题是我们无法(或者在我看来)对参数进行分组。如果我们使用 uncurry
(事实上,uncurry ($)
就足够了),解决方案将非常简单:
- 制作一个元组
(f, (x, y))
。 - 将
assoc1 :: (a, (b, c)) -> ((a, b), c)
应用于元组并得到((f, x), y)
。 - 将未柯里化的
($)
应用于该对的第一个元素并得到(f x, y)
. - 将未柯里化的
($)
应用于对本身并得到f x y
.
如果没有 uncurried ($)
,我们将不得不分别提取该对的两个元素。例如:
uncurry f pair = f (fst pair) (snd pair)
我不认为这是实现无点的顺利方式。
事实上,我们已经按照我们的要求得到了这个未柯里化的($)
:Control.Arrow.apply
(其他对解决方案组合器有用的东西也可以从Control.Arrow
导入)。因此:
import Control.Arrow ((>>>), (&&&), first, app)
myUncurry = let myAssoc1 = (fst &&& (fst . snd)) &&& (snd . snd)
in (,) >>> (>>> myAssoc1 >>> first app >>> app)
然而,这感觉有点像作弊。
是否有其他解决此问题的方法不需要 app
?
join
在函数上给你 (a -> a -> b) -> a -> b
,所以:
myUncurry f = join (\x y -> f (fst x) (snd y))
myUncurry f = join (\x -> f (fst x) . snd)
myUncurry f = join ((.snd) . f . fst)
myUncurry f = join ((.fst) ((.snd) . f))
myUncurry f = join ((.fst) ((.) (.snd) f))
myUncurry = join . (.fst) . \f -> (.) (.snd) f
myUncurry = join . (.fst) . ((.snd).)
join . (.fst) . ((.snd).)
确实非常可读
使用 Lambda 微积分的 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)
(编辑:)
它仍然更具可读性(并且在某种程度上阐明了),但保留了一个明确的论点,如上所示:
uncurry f = f . fst <*> snd
然后这个变体,显示为 Jon Purdy in
uncurry f = liftA2 f fst snd
可能是最清楚的了。
这是因为对于函数来说,monad 和 applicative 是等价的,
(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 Comprehensions写的)。因此,
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
遵循众所周知的 equivalence、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
可能是最清晰、噪音最小的。
uncurry f (x,y) = f x y
uncurry f p = f (fst p) (snd p)
uncurry f = \p -> f (fst p) (snd p)
uncurry f = (<*>) (\p -> f (fst p)) (\p -> snd p)
uncurry f = (<*>) (f . fst) snd
uncurry = \f -> (<*>) (f . fst) snd
uncurry = flip (\f -> (<*>) (f . fst)) snd
uncurry = flip ((<*>) . (\f -> f . fst)) snd
uncurry = flip ((<*>) . (. fst)) snd