我有一个a -> b -> Bool
类型的函数列表,我尝试将它们应用于两个输入,并将结果与All
或Any
合并。我使用了一个变量的函数:
mconcat (map (All . ) [(<7),(>7),(==7)]) $ 6
但我无法弄清楚如何用两个变量函数做同样的事情。
这有效:
mconcat (map (All . ) (map (uncurry) [(<),(>),(==)])) $ (,) 6 7
但它看起来像一个丑陋的解决方法。
有更好的方法吗?
答案 0 :(得分:3)
这是自己写的代码 - 你只需要使类型连接起来。但是有一些标准工具(即Applicative
和Traversable
)可以用来缩短代码。
在Data.Traversable
模块中sequenceA :: (Traversable t, Applicative f) => t (f a) -> f (t a)
。如果专门针对[]
的{{1}}实例以及函数应用Traversable
,我们会得到:
(->) r
因此sequenceA :: [r -> a] -> r -> [a]
从sequenceA
中抽出->
,将列表中的每个函数应用于固定参数。
[]
所以你的第一个函数可以写成
sequenceA [(< 7), (> 7), (== 7)] :: (Num n, Ord n) => n -> [Bool]
-- equivalent to:
\n -> map ($ n) [(< 7), (> 7), (== 7)]
我使用and
代替f :: (Num n, Ord n) => n -> Bool
f = and . sequenceA [(< 7), (> 7), (== 7)]
。
对于您的第二个功能,mconcat . map (All .)
是正确的工具。我们必须将uncurry
映射到二元函数列表上以获取元组的一元函数列表,以便我们可以使用uncurry
将单个参数提升出来。因为sequenceA
我们可以将其写为:
traverse f = sequenceA . map f
(注意,对于任何正确实现的g :: Ord n => n -> n -> Bool
g = curry $ and . traverse uncurry [(<), (>), (==)]
实例,Ord
和>
应该互斥。因此,这两个函数都将始终返回<
。)
答案 1 :(得分:1)
原始代码的替代选择:
mconcat (map (\f a b -> All (f a b)) [(<),(<=)]) 3 4
可以用无意义的方式进一步重写\f a b -> All (f a b)
:
mconcat (map ((.) (All .)) [(<),(<=)]) 3 4