【发布时间】:2017-01-21 08:16:33
【问题描述】:
假设我们有一个像这样的(人为的)函数:
import Data.List (sort)
contrived :: Ord a => [a] -> [a] -> [a]
contrived a b = (sort a) ++ b
我们将其部分应用到其他地方,例如:
map (contrived [3,2,1]) [[4],[5],[6]]
从表面上看,这符合预期:
[[1,2,3,4],[1,2,3,5],[1,2,3,6]]
但是,如果我们将一些 traces 放入:
import Debug.Trace (trace)
contrived :: Ord a => [a] -> [a] -> [a]
contrived a b = (trace "sorted" $ sort a) ++ b
map (contrived $ trace "a value" [3,2,1]) [[4],[5],[6]]
我们看到传递给contrived 的第一个列表只被评估一次,但它为[4,5,6] 中的每个项目排序:
[sorted
a value
[1,2,3,4],sorted
[1,2,3,5],sorted
[1,2,3,6]]
现在,contrived 可以很简单地转换为无点样式:
contrived :: Ord a => [a] -> [a] -> [a]
contrived a = (++) (sort a)
当部分应用时:
map (contrived [3,2,1]) [4,5,6]
仍然像我们预期的那样工作:
[[1,2,3,4],[1,2,3,5],[1,2,3,6]]
但是如果我们再次添加traces:
contrived :: Ord a => [a] -> [a] -> [a]
contrived a = (++) (trace "sorted" $ sort a)
map (contrived $ trace "a value" [3,2,1]) [[4],[5],[6]]
我们看到现在传入contrived 的第一个列表只被评估和排序一次:
[sorted
a value
[1,2,3,4],[1,2,3,5],[1,2,3,6]]
为什么会这样?既然翻译成pointfree风格那么琐碎,为什么GHC不能推断出在contrived的第一版中它只需要对a进行一次排序呢?
注意:我知道对于这个相当简单的示例,使用 pointfree 样式可能更可取。这是一个人为的例子,我已经简化了很多。当以无点风格表达时,我遇到问题的真正功能不太清楚(在我看来):
realFunction a b = conditionOne && conditionTwo
where conditionOne = map (something a) b
conditionTwo = somethingElse a b
在无点风格中,这需要在 (&&) 周围编写一个丑陋的包装器 (both):
realFunction a = both conditionOne conditionTwo
where conditionOne = map (something a)
conditionTwo = somethingElse a
both f g x = (f x) && (g x)
顺便说一句,我也不确定both 包装器为什么起作用; realFunction 的 pointfree 样式的行为类似于 contrived 的 pointfree 样式版本,因为部分应用程序只评估一次(即,如果 something 排序 a 它只会这样做一次)。看来,由于both 不是无点的,Haskell 应该有与非无点的contrived 相同的问题。
【问题讨论】:
标签: haskell ghc currying pointfree partial-application