haskell - 右应用合成运算符的使用是否暗示可以使用 contramap?
问题描述
长话短说
contramap
当我发现自己编写类似的代码时,如果我应该考虑使用,我正在徘徊,实际上在(. f) . g
哪里预处理第二个参数。f
g
长话短说
我将描述我是如何想出让我想到标题中的问题的代码的。
最初,我有两个输入a1 :: In
和a2 :: In
,包裹在一对(a1, a2) :: (In,In)
中,我需要对这些输入进行两次交互处理。具体来说,我有一个binOp :: In -> In -> Mid
生成“临时”结果的函数,以及一个输入fun :: Mid -> In -> In -> Out
和输出的binOp
函数。
鉴于上面“由另一个函数的输入和输出提供的函数”部分,我想使用函数 monad,所以我想出了这个,
finalFun = uncurry . fun =<< uncurry binOp
读起来并不复杂:binOp
将输入作为一对,然后将其输出和其输入传递给fun
,它也将输入作为一对。
但是,我注意到在实现中fun
我实际上只使用了输入的“简化”版本,即我有一个类似的定义fun a b c = fun' a (reduce b) (reduce c)
,所以我认为fun
我可以在;的fun'
定义中使用 我想出了reduce
finalFun
finalFun = (. both reduce) . uncurry . fun' =<< uncurry binOp
我相信,这远不那么容易阅读,尤其是因为它具有不自然的部分顺序。我只能考虑使用一些更具描述性的名称,例如
finalFun = preReduce . uncurry . fun' =<< uncurry binOp
where preReduce = (. both reduce)
由于preReduce
实际上是对 的第二个和第三个参数进行预处理fun'
,因此我在徘徊是否适合使用contramap
.
解决方案
lmap f . g
(而不是contramap
,因为这也需要一个Op
包装器)在清晰度方面可能确实是一个改进(. f) . g
。如果您的读者熟悉Profunctor
,seeinglmap
会立即提示对某些内容的输入正在修改,而无需他们在头脑中执行点探测。请注意,虽然它还不是一个普遍的习语。(作为参考,这里是版本和点部分的lmap
Serokell Hackage Search 查询。)
至于您更长的示例,惯用的做法可能不是无意义地编写它。fun
也就是说,我们可以通过更改/的参数顺序来获得更易读的无点版本fun'
,这样您就可以使用等效 Applicative
实例而不是Monad
一个:
binOp :: In -> In -> Mid
fun' :: In -> In -> Mid -> Out
reduce :: In -> In
both f = bimap f f
finalFun :: (In, In) -> Out
finalFun = uncurry fun' . both reduce <*> uncurry binOp
Pointfree function(<*>)
可以说比 pointfree function 更容易理解(=<<)
,因为它的两个参数是相关函数仿函数中的计算。此外,此更改消除了对点部分技巧的需要。最后,既然(.)
是fmap
函数,我们可以进一步改写finalFun
为......
finalFun = uncurry fun' <$> both reduce <*> uncurry binOp
...因此获得了一种应用风格的表达方式,在我(不是那么流行!)看来,这是一种使用函数 applicative 的合理可读的方式。(我们可能会使用 进一步简化它liftA2
,但我觉得在这种特定情况下,发生的事情不太明显。)
推荐阅读
- voltdb - 如何使用 voltdb 运行具有不同节点的两个服务
- php - 如何在php中使用magick连续调整两个图像后放置多个图像
- javascript - 从日期列表中确定第二天
- java - 什么是 com.android.tools.profiler.support.network.httpurl.HttpURLConnection?
- python - 如何读取 Abaqus .mtx 文件?Scipy.io.mmread 给出“没有足够的值来解包”错误
- php - 如何使日期(“W”)从星期日开始并在星期六结束?
- reactjs - React-Native:在单个 View 组件中居中两个文本组件
- rust - Cap'n Proto 如何处理千兆字节级别的数据发送?
- angular - 错误类型错误:无法在 Object.eval [as updateRenderer] 处读取 null 的属性“电话”
- c++ - ECS框架中如何更新组件数据并通知系统?