考虑a -> b -> c
类型的函数和应用值a1, a2 :: (Applicative f) => f a
。
我希望构造一个函数,该函数可以应用于a -> b -> c
类型的函数,以获取类型Applicative f :: f c
的值。我可以通过以下方式做到这一点:
g :: (Applicative f) => (a -> b -> c) -> f c
g = \f -> f <$> a1 <*> a2
(显式lambda是故意的,因为我正在考虑在任何级别构建此函数,而不仅仅是顶层)。
如果我尝试以无点样式编写g
:
g = (<$> a1 <*> a2)
我收到以下编译错误:
The operator `<$>' [infixl 4] of a section
must have lower precedence than that of the operand,
namely `<*>' [infixl 4]
in the section: `<$> gen1 <*> gen2'
我可以编写这个无点实现:
g = flip (flip liftA2 a1) a2
但是我觉得这样的可读性较差,并且将基于中缀函数的实现重构为例如添加另一个参数比将上述内容更改为使用liftA3
更简单。
可以写出一系列作品:
g = (<*> a2) . (<$> a1)
这实现了无点样式,并且添加参数很简单 - 但它们在左侧得到前置而不是在右侧附加,所以你失去了与函数类型{{1}的对应关系}}。此外,有了更多的参数,你最终会得到比在第一个实现中使用lambda更长的表达式。
那么,是否有很好的,简洁的方式来编写我想要的部分,或者我是否坚持使用lambda?
答案 0 :(得分:8)
<*>
对<$>
的结果进行操作,因此:
g = (<*> a2) . (<$> a1)
答案 1 :(得分:6)
我真的不相信自由点比使用明确的论点更好,但还有更多的想法:
您可以使用flip
infix:
g = liftA2 `flip` a1 `flip` a2
您可以使用>>>
中的Control.Category
.
翻转:
g = (<$> a1) >>> (<*> a2)