Apa contoh kelanjutan yang tidak diterapkan sebagai prosedur?

15

Diskusi yang menarik tentang perbedaan antara callback dan kelanjutan pada SO telah mendorong pertanyaan ini. Menurut definisi, kelanjutan adalah representasi abstrak dari logika yang diperlukan untuk menyelesaikan perhitungan. Dalam sebagian besar bahasa, ini bermanifestasi sebagai prosedur satu argumen yang Anda berikan nilai apa pun yang perlu diproses lebih lanjut.

Dalam bahasa yang murni fungsional (di mana semua fungsi murni dan warga negara kelas satu), saya pikir kelanjutan bisa sepenuhnya dimodelkan sebagai fungsi. Bagaimanapun, ini adalah bagaimana saya sebelumnya memahami kelanjutan hingga titik ini. Namun, dunia ini penuh dengan keadaan (desah ..) dan dengan demikian definisi umum tidak mensyaratkan bahwa keadaan program penangkapan lanjutan - hanya perlu mencakup maksud.

Untuk membantu pemahaman saya, dapatkah contoh diberikan dalam bahasa fungsional di mana kelanjutan diekspresikan dalam cara yang lebih abstrak daripada fungsi? Saya tahu Skema memungkinkan Anda untuk mengambil kelanjutan saat ini dengan cara kelas satu (panggilan / cc), tetapi meskipun demikian, tampaknya prosedur satu argumen yang dilewati untuk menelepon / cc hanya diberikan kelanjutan saat ini dalam bentuk yang lain prosedur argumen yang fungsi panggilan / cc'd dapat menerapkan hasilnya.

David Cowden
sumber
Mungkin persimpangan kontinuitas dan defungsionalisasi sebagai: kelanjutan dapat dikonversi ke struktur data melalui defungsionalisasi; mungkin area yang menarik untuk dilihat.
Dan D.
@DanD. apakah Anda punya saran untuk literatur menarik yang bisa saya baca? Topik itu terdengar bermanfaat.
David Cowden

Jawaban:

11

tl; dr; The jenis adalah abstraksi menyeluruh atas kelanjutan


Kelanjutan adalah jenis input dan outputnya

Hal terdekat yang akan Anda temukan pada kelanjutan berbasis non-prosedur adalah kelanjutan monad di Haskell seperti yang dinyatakan sebagai tipe, di mana banyak fungsi dapat digunakan untuk berinteraksi dengan tipe untuk menyela, melanjutkan, mundur, dan lain-lain.

Anda dapat merangkum penutupan itu dalam suatu jenis seperti Contjenis di Haskell di mana Anda mendapatkan abstraksi monad sebagai "abstraksi tingkat yang lebih tinggi", dan ada bentuk abstraksi lain atas kelanjutan yang Anda dapatkan ketika Anda melihat kelanjutan sebagai jenis alih-alih sebagai jenis. hanya sebuah prosedur , misalnya

  • Anda dapat mengambil dua kelanjutan dan melakukan alternatif di antara mereka jika tipe mengikuti hukum menjadi monoid
  • Anda bisa mengabstraksi tipe untuk mengubah tipe input atau output dari kelanjutan jika Anda merangkum penutupan dalam tipe yang mematuhi hukum dari functor
  • Anda dapat secara sewenang-wenang dan sebagian menerapkan atau menghiasi kelanjutan Anda dengan fungsionalitas seperti validasi input atau konversi input jika Anda merangkum penutupan dalam jenis yang mengikuti undang-undang dari aplikator fungsional

Penutupan vs. Prosedur

Pada akhirnya Anda pada dasarnya benar; kelanjutan adalah "prosedur", meskipun saya lebih suka menyebutnya sebagai penutupan. Sering kali kelanjutan diekspresikan dengan baik sebagai penutupan kelas satu yang telah menyertakan lingkungan yang terikat. Dalam bahasa fungsional murni Anda mungkin mengatakan ini tidak masuk akal karena Anda tidak memiliki referensi; ini benar tetapi Anda dapat melampirkan nilai dan tugas tunggal membuat melampirkan nilai vs referensi hal yang sama persis. Ini menimbulkan di Haskell:

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

Bahasa yang tidak memiliki kemampuan untuk melampirkan lingkungan yang mengikat mungkin secara teknis tidak memiliki penutupan kelas satu, tetapi bahkan kemudian ada beberapa lingkungan (umumnya global) yang tersedia untuk penutupan.

Jadi saya akan mengatakan lebih akurat untuk menggambarkan kelanjutan sebagai: Penutupan yang digunakan dengan cara tertentu.


Kesimpulan

Untuk pertanyaan "Apakah kelanjutan dapat diterapkan dengan cara lain selain prosedur?" Tidak. Jika Anda tidak memiliki fungsi kelas pertama, Anda benar-benar tidak dapat memiliki kelanjutan seperti itu (ya fungsi pointer dapat dihitung sebagai fungsi kelas satu, jadi akses memori sewenang-wenang dapat mencukupi).

Sekarang untuk pertanyaan "Apakah ada cara untuk mengekspresikan kelanjutan dengan cara yang lebih abstrak daripada prosedur?" Mengekspresikannya sebagai tipe memberi Anda abstraksi yang jauh lebih besar, memungkinkan Anda untuk memperlakukan kelanjutan dengan cara yang sangat umum sehingga Anda dapat berinteraksi dengan kelanjutan dalam banyak cara lebih dari sekadar menjalankannya.

Jimmy Hoffa
sumber
1
Ini digeneralisasikan ke "Kelanjutan hanya bisa apa saja yang memungkinkan Anda mendapatkan hasil dari sisa program". Karena ini biasanya mandat memiliki beberapa kode (sisa program) sebagian besar bahasa menggunakan fungsi. Secara teoritis Anda dapat membangun kelanjutan dari apa pun. Selama konversi lanjutan pada kompiler saya, saya telah menggunakan pohon yang terisi sebagian.
Daniel Gratzer
1
@jozefg pohon yang diisi sebagian adalah representasi yang tepat dari perhitungan sebagai ekspresi, tetapi pada akhirnya kode aktual yang sedang ditulis adalah ekspresi yang tidak dapat diidentifikasi secara berbeda dari suatu prosedur, seperti yang saya mengerti. Selain itu, pohon yang terisi sebagian adalah representasi kompiler; representasi pengembang diharapkan merupakan ekspresi ekspresi normatif dengan sintaks dan semantik bahasa, yang akan tampak 99% pengembang sebagai "prosedur", "fungsi" atau sebaliknya. Anda berpikir dari perspektif pengembang kompiler heh
Jimmy Hoffa
2

Salah satu contoh yang mungkin Anda sukai adalah coroutine. Sebagai contoh, Coroutine dari Lua atau iterators / generator dari Python atau C # memiliki kekuatan yang sama dengan kelanjutan satu-shot (kelanjutan yang hanya diizinkan untuk Anda panggil sekali) tetapi kelanjutan tersebut tidak secara eksplisit dibuat menjadi fungsi. Sebaliknya, Anda memiliki cara untuk memajukan coroutine hingga pernyataan "hasil" berikutnya.

Misalnya, pertimbangkan program Python berikut:

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

Jenisnya mirip dengan program Javascript berikut dengan panggilan balik eksplisit:

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);
         });
      });
   });
}

Contoh Javascript agak berisik karena setiap langkah perlu mengembalikan kelanjutan berikutnya selain mengembalikan nilai yang dihasilkan (dalam Python melacak kelanjutan di dalam ite

hugomg
sumber