用于将两个函数应用于单个输入的Pointfree(或库)函数

时间:2013-01-16 03:21:46

标签: haskell pointfree

我一直在重复使用lambda表达式,例如

\x -> (f x, g x)

其中我将相同的输入应用于两个函数并将结果封装在一对中。我可以写一个捕捉这个

的函数
combine :: (a -> b) -> (a -> c) -> a -> (b,c)
combine f g x = (f x, g x)

现在上面的lambda表达式只是combine f g。我有两个问题。

  1. 我很想知道是否有一个标准的库函数可以解决这个问题。
  2. 出于好奇,我想以无点的方式重写这个功能,但我遇到了很多麻烦。

3 个答案:

答案 0 :(得分:12)

  1. Control.Arrow具有此功能(&&&)。它有一个“更一般”的类型,不幸的是,这意味着Hoogle没有找到它(可能这应该被认为是Hoogle中的一个错误?)。

  2. 您通常可以使用pointfree自动计算此类内容,lambdabot中的#haskell作为插件。

  3. 例如:

    <shachaf> @pl combine f g x = (f x, g x)
    <lambdabot> combine = liftM2 (,)
    

    liftM2 (r ->) Monad实例的(a -> b -> c) -> (r -> a) -> (r -> b) -> r -> c类型为{{1}}。当然,还有许多其他方法可以根据您允许的基元来编写这一点。

答案 1 :(得分:12)

  

我很想知道是否有一个标准的库函数可以解决这个问题。

由于类型类很容易错过,但是look at Control.Arrow。普通Arrow无法进行计算或应用,因此Arrow组合器必须是无点的。如果你将它们专门化为(->),你会发现你想要的是:

(&&&) :: (Arrow a) => a b c -> a b c' -> a b (c, c')

还有其他类似的功能,例如Either的等效操作,专门用于(->),如下所示:

(|||) :: (a -> c) -> (b -> c) -> Either a b -> c

either相同。

  

出于好奇,我想以无点的方式重写这个功能,但我遇到了很多麻烦。

由于您正在复制输入,因此您需要某种方式来执行此操作 - 最常见的方法是通过Applicative的{​​{1}}或Monad实例,例如{ {1}}。这本质上是一个隐式的内联(->) monad,被拆分的参数是“环境”值。使用此方法,\f g -> (,) <$> f <*> g变为(,)Readerjoin f x变为f x xpure变为return,{{1 }成为S combinator const

答案 2 :(得分:6)

实际上有很多方法可以做到这一点。最常见的方法是使用(&&&)中的Control.Arrow函数:

f &&& g

但是,通常你有更多的功能或需要将结果传递给另一个函数,在这种情况下使用applicative样式会更方便。然后

uncurry (+) . (f &&& g)

变为

liftA2 (+) f g

如上所述,这可以与多个功能一起使用:

liftA3 zip3 f g h