我正在努力解决Super Combinators的问题:
超级组合器是一个常量,或者只包含超级组合子作为子表达式的组合子。
还有Constant Applicative Forms的内容:
任何不是lambda抽象的超级组合子。这包括真正的常量表达式,如12,((+)1 2),[1,2,3]以及部分应用的函数,如((+)4)。请注意,最后一个示例在eta抽象下等效于\ x - > (+)4 x不是CAF。
这对我没有任何意义! {12}与{12}一样“真正不变”吗? CAF听起来像是我简单心灵的价值观。
答案 0 :(得分:29)
你引用的这些Haskell wiki页面很旧,我想不幸写了。特别不幸的是,他们混淆了CAF和超级组合者。超级组合器很有趣但与GHC无关。 CAF仍然是GHC的一部分,可以在不参考超级组合者的情况下理解。
让我们从超级组合器开始吧。组合器派生自combinatory logic,并且在这里的用法中,由仅以一种或另一种形式应用传递给彼此的值的函数组成 - 即它们组合它们的参数。最着名的组合子是S, K, and I,这些组合在一起是图灵完成的。在这种情况下,超级组合器是仅由传入的值,组合器和其他超级组合器构成的功能。因此,任何超级组合器都可以通过替换扩展为普通的旧组合器。
一些函数式语言编译器(不是GHC!)使用组合器和超级组合器作为编译的中间步骤。与任何类似的编译器技术一样,这样做的原因是允许以这种简化的最小语言更容易地执行的优化分析。建立在超级组合者基础上的一种核心语言是Edwin Brady的epic。
常量适用表格完全是另一回事。他们有点微妙,并有一些陷阱。思考它们的方式是编译器实现的一个方面,没有单独的语义含义,但对运行时性能有潜在的深远影响。以下可能不是对CAF的完美描述,但它会试图表达我对一个人的直觉,因为我还没有在其他任何地方看到真正的良好的描述。 。 clean "authoritative" description in the GHC Commentary Wiki内容如下:
常量适用表格(简称CAF)是顶级值 在程序中定义。从本质上讲,它们是不是的对象 在运行时动态分配,但是,它是静态的一部分 该计划的数据。
这是一个好的开始。纯粹的,功能性的,懒惰的语言在某种意义上可以被认为是图形缩减机器。第一次请求节点的值时,会强制进行评估,而后者又可以请求子节点的值等。一个节点被评估,结果值保持不变(尽管它没有具有要坚持下去 - 因为这是一种纯语言,我们可以始终保持子节点的实时和重新计算而不会产生语义效应。 CAF确实只是一个价值。但是,在上下文中,一种特殊的值 - 编译器可以确定的值具有完全依赖于其子节点的含义。也就是说:
foo x = ...
where thisIsACaf = [1..10::Int]
thisIsNotACaf = [1..x::Int]
thisIsAlsoNotACaf :: Num a => [a]
thisIsAlsoNotACaf = [1..10] -- oops, polymorphic! the "num" dictionary is implicitly a parameter.
thisCouldBeACaf = const [1..10::Int] x -- requires a sufficiently smart compiler
thisAlsoCouldBeACaf _ = [1..10::Int] -- also requires a sufficiently smart compiler
那么,为什么我们关心的是CAF是什么?基本上是因为有时我们真的不想重新计算某些东西(例如,一个可记忆的!),所以想要确保它被正确共享。其他时候我们真的做想要重新计算某些东西(例如一个很容易生成的无聊列表 - 比如自然界 - 我们只是走过去)而不是永远留在记忆中。命名事物和将它们绑定在内,或者将它们写入内联等组合通常可以让我们以自然,直观的方式指定这些事物。然而,偶尔,编译器比我们期望的更聪明或更笨,我们认为应该只计算一次总是重新计算,或者我们不想挂起的东西被作为CAF取消。然后,我们需要更仔细地思考问题。请参阅此讨论,以了解所涉及的一些棘手问题:A good way to avoid "sharing"?
[顺便说一句,我并不满足于此,但是任何想要自由的人都应该尽可能多地接受这个答案,因为他们想要尝试将其与现有的Haskell Wiki页面集成并改进/更新他们]
答案 1 :(得分:3)
马特是正确的,因为这个定义令人困惑。这甚至是矛盾的。 CAF定义为:
任何不是lambda抽象的超级组合子。这包括 真正的常量表达式,例如
12
,((+) 1 2)
,[1,2,3]
以及部分应用的功能,例如((+) 4)
。
因此,((+) 4)
被视为CAF。但在接下来的句子中,我们被告知它等同于不是CAF的东西:
这个最后一个例子在eta抽象下等同于
\ x -> (+) 4 x
,而不是CAF。
在本质上排除部分应用函数它们等同于lambda抽象将更为清晰。