Що таке приклад продовження, яке не реалізується як процедура?


15

Цікава дискусія про відмінність між зворотними дзвінками та продовженням роботи в режимі SO викликала це питання. За визначенням, продовження - це абстрактне подання логіки, необхідної для завершення обчислення. У більшості мов це проявляється як одна процедура аргументу, якій ви передаєте будь-яке значення, необхідне для подальшої обробки.

Чисто функціональною мовою (де всі функції є чистими та першокласними громадянами), я думаю, що продовження може бути повністю змодельоване як функція. Це врешті-решт, як я раніше розумів продовження до цього моменту. Однак світ сповнений стану (зітхання ..), тому загальне визначення не вимагає, щоб стан програми продовження захоплення - воно повинно лише охоплювати наміри.

Щоб допомогти мені зрозуміти, чи можна навести приклад функціональною мовою, де продовження виражається більш абстрактно, ніж функція? Я знаю, що схема дозволяє схопити поточне продовження в першокласному порядку (call / cc), але навіть так, здається, що одна процедура аргументу, передана call / cc, просто надається поточному продовженню у вигляді іншого процедура аргументу, до якої функція call / cc'd може застосувати свій результат.


Можливо, перетин континуацій та дефункціоналізація як: продовження можна перетворити на структури даних за допомогою дефункціоналізації; Можливо, буде цікавою областю для погляду.
Dan D.

@DanD. чи є у вас якісь пропозиції щодо цікавої літератури, яку я можу прочитати? Ця тема звучить корисно.
Девід Кауден

Відповіді:


11

tl; dr; Типом є головною абстракцією над продовженням


Продовженням є тип його входів та виходів

Найближче, що ви знайдете до непроцедурного продовження, ймовірно, монада продовження в Haskell, оскільки вона виражається як тип, для якого багато функцій можуть використовуватися для взаємодії з типом для переривання, відновлення, зворотного відстеження тощо.

Ви можете інкапсулювати це закриття у такий тип, як Contтип у Haskell, де ви отримуєте абстракцію монади як "абстракцію вищого рівня", і існують інші форми абстракції над продовженнями, які ви отримуєте, коли дивитесь на продовження як на тип, а не на просто процедура , наприклад

  • Ви можете взяти два продовження і зробити альтернативу між ними, якщо тип дотримується законів, що є моноїдом
  • Ви можете абстрагуватися над типом, щоб змінити вхідні або вихідні типи продовження, якщо інкапсулювати закриття у тип, який дотримується законів функтора
  • Ви можете довільно та частково застосувати або прикрасити своє продовження з такими функціями, як перевірка введення чи перетворення входу, якщо інкапсулювати закриття типом, який відповідає законам прикладного функтора

Закриття проти процедури

Зрештою, ви в основному праві; продовження - це "процедура", хоча я б швидше вважав це закриттям. Часто тривалість часу найкраще виражається як закриття першого класу, які уклали зв'язане середовище. Чисто функціональною мовою ви можете сказати, що це не особливо розумно, оскільки вам не вистачає посилань; це правда, але ви можете долучити значення, і одне призначення призначає, що додає значення порівняно з посиланням точно те саме. Це породжує Хаскелл:

(\x -> \y -> insideYIcanAccess x (and y))

Мові, якій не вистачає можливості вкласти середовище зв'язування, технічно може не вистачати закриття першого класу, але навіть тоді є якесь середовище (як правило, глобальне), яке доступне для закриття.

Тож я б сказав, що більш точно описати продовження як: Закриття використовується певним чином.


Висновок

На питання "Чи можливе продовження будь-яким іншим способом, крім процедури?" Ні. Якщо у вас немає функцій першого класу, ви дійсно не можете мати продовження як таке (так, покажчики функцій вважаються функціями першого класу, тому альтернативно може бути довільний доступ до пам'яті).

Тепер до питання "Чи є способи висловити продовження більш абстрактним способом, ніж процедура?" Висловлення його як типу дає набагато більшу абстракцію, що дозволяє розглядати продовження дуже загальними способами, щоб ви могли взаємодіяти з продовженням набагато більше способів, ніж просто виконати його.


1
Це узагальнює "Продовження може бути просто будь-чим, що дозволить вам отримати результат решти програми". Оскільки це, як правило, вимагає деякого коду (решта програми), більшість мов використовують функції. Теоретично ви можете побудувати продовження з усього, що завгодно. Під час продовження перетворення у своїх компіляторах я використовував частково заповнені дерева.
Даніель Гратцер

1
@jozefg частково заповнені дерева є належним поданням обчислень як виразів, але наприкінці дня власне код, що записується, є висловом, яке не може бути ідентично відмінне від процедури, як я це розумію. Окрім того, частково заповнені дерева є представником компілятора; представлення розробників очікує вираження обчислень, нормативне з синтаксисом та семантикою мови, яке 99% розробників представлятиметься як "процедура", "функція" чи іншим чином. Ти думаєш з точки зору розробника компілятора, так
Джиммі Хоффа

2

Один із прикладів, який вам може сподобатися, - це супроводи. Наприклад, Coroutines від Lua або ітератори / генератори з Python або C # за потужністю схожі на продовження одного зйомок (продовження, до якого вам дозволено дзвонити лише один раз), але продовження явно не перетворюється на функцію. Натомість у вас є способи перейти до програми до наступного твердження "вихід".

Наприклад, розглянемо таку програму Python:

def my_iterator():
   yield 1
   yield 2
   yield 3

def main():
   it = my_iterator()
   x = it.next()
   y = it.next()
   z = it.next()
   print x + y + z

Його вид схожий на наступну програму Javascript з явними зворотними зворотами:

function my_iterator()
  return function(cb1){
    cb1(1, function(cb2){
      cb2(2, function(cb3){
        cb3(3, function(cb4){
          throw "stop iteration";
        });
      });
    });
  });
}

function main(){
   var getNext1 = my_iterator();
   getNext1(function(x, getNext2){
      getNext2(function(y, getNext3){
         getNext3(function(z, getNext4){
            console.log(x + y + z);
         });
      });
   });
}

Приклад Javascript - шумний, тому що кожен крок потребує повернення наступного продовження на додаток до повернення вихідного значення (у Python відслідковує продовження всередині ite

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.