Что такое суперкомбинаторы и постоянные аппликативные формы? - PullRequest
22 голосов
/ 30 ноября 2011

Я борюсь с тем, что Суперкомбинаторы :

Суперкомбинатор - это либо константа, либо комбинатор, который содержит только суперкомбинаторы в качестве подвыражений.*

А также с какими постоянными аппликативными формами являются:

Любой суперкомбинатор, который не является лямбда-абстракцией.Это включает в себя действительно константные выражения, такие как 12, ((+) 1 2), [1,2,3], а также частично применяемые функции, такие как ((+) 4).Обратите внимание, что этот последний пример эквивалентен по принципу eta abstraction \ x -> (+) 4 x, который не является CAF.

Это просто не имеет никакого смысла для меня!Разве ((+) 4) не настолько «по-настоящему постоянен», как 12?CAFs звучат как ценности для моего простого ума.

Ответы [ 2 ]

29 голосов
/ 30 ноября 2011

Эти вики-страницы на Haskell, на которые вы ссылаетесь, старые, и я думаю, что, к сожалению, они написаны.Особенно прискорбно, что они смешивают CAF и суперкомбинаторы.Суперкомбинаторы интересны, но не связаны с GHC.CAF по-прежнему являются частью GHC и могут быть поняты без ссылки на суперкомбинаторы.


Итак, давайте начнем с суперкомбинаторов .Комбинаторы являются производными от комбинаторной логики и, при использовании здесь, состоят из функций, которые только применяют передаваемые друг к другу значения в той или иной форме, то есть объединяют свои аргументы.Самый известный набор комбинаторов - это S, K и I , которые вместе взяты по Тьюрингу.В этом контексте суперкомбинаторы - это функции, построенные только из переданных значений, комбинаторов и других суперкомбинаторов.Следовательно, любой суперкомбинатор может быть расширен путем замены в обычный старый комбинатор.

Некоторые компиляторы для функциональных языков (не GHC!) Используют комбинаторы и суперкомбинаторы в качестве промежуточных этапов компиляции.Как и с любой подобной технологией компилятора, причина для этого состоит в том, чтобы допустить оптимизационный анализ, который легче выполнять на таком упрощенном, минимальном языке.Одним из таких базовых языков, построенных на суперкомбинаторах, является epic .


константных аппликативных форм Эдвина Брейди, которые являются чем-то совершенно другим.Они немного более тонкие и имеют несколько ошибок.Их можно представить как аспект реализации компилятора, который не имеет отдельного семантического значения, но потенциально может оказать существенное влияние на производительность во время выполнения.Нижеследующее, возможно, не является идеальным описанием CAF, но оно попытается передать мою интуицию о том, чем он является, поскольку я нигде больше не видел хорошего описания действительно , чтобы я мог бы использовать его. чистое «авторитетное» описание в Вики-комментариях GHC гласит:

Постоянные аппликативные формы, или сокращенно 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?В основном потому, что иногда мы действительно не хотим пересчитывать что-либо (например, памятную записку!) И поэтому хотим убедиться, что это правильно передано.В других случаях мы действительно делаем хотим что-то пересчитать (например, огромный скучный, легко генерируемый список - такой как натуральные объекты - по которому мы просто прогуливаемся) и не оставлять его в памяти навсегда.Комбинация именования вещей и связывания их с помощью let или записи в строке и т. Д. Обычно позволяет нам определять такие вещи естественным, интуитивно понятным способом.Иногда, однако, компилятор оказывается умнее или тупее, чем мы ожидаем, и то, что мы считаем нужным вычислять только один раз, всегда пересчитывается или что-то, за что мы не хотим держаться, выдается за CAF.Затем нам нужно продумать вещи более тщательно.Посмотрите это обсуждение, чтобы получить представление о некоторых хитростях: Хороший способ избежать «обмена»?

[Между прочим, я не чувствую этого, но любой, кто хочет, может свободно взять столько ответа, сколько захочет, и попытаться интегрировать его с существующими страницами Haskell Wiki и улучшить / обновить их]

3 голосов
/ 01 декабря 2011

Мэтт прав в том, что определение сбивает с толку.Это даже противоречиво.CAF определяется как:

Любой суперкомбинатор, который не является лямбда-абстракцией.Это включает действительно константные выражения, такие как 12, ((+) 1 2), [1,2,3], а также частично примененные функции, такие как ((+) 4).

Следовательно, ((+) 4) рассматривается как CAF.Но в следующем предложении нам говорят, что это эквивалентно чему-то, что не является CAF:

этот последний пример эквивалентен по eta абстракциям \ x -> (+) 4 x, который не является CAF.

Было бы яснее исключить частично примененные функции на том основании, что они эквивалентны лямбда-абстракциям.

...