Mengapa suatu fungsi dengan tipe polymorphic `forall t: Type, t-> t` menjadi fungsi identitas?

18

Saya baru mengenal teori bahasa pemrograman. Saya menonton beberapa kuliah online di mana instruktur mengklaim bahwa fungsi dengan tipe polimorfik forall t: Type, t->tmenjadi identitas, tetapi tidak menjelaskan mengapa. Adakah yang bisa menjelaskan kepada saya mengapa? Mungkin bukti klaim dari prinsip pertama.

abhishek
sumber
3
Saya pikir pertanyaan ini harus merupakan duplikat, tetapi saya tidak dapat menemukannya. cs.stackexchange.com/questions/341/… adalah semacam tindak lanjut. Referensi standar adalah Teorema gratis! oleh Phil Wadler.
Gilles 'SANGAT berhenti menjadi jahat'
1
Cobalah untuk membangun fungsi generik dengan tipe ini yang melakukan hal lain. Anda akan menemukan bahwa tidak ada.
Bergi
@Bergi Ya saya tidak dapat menemukan contoh balasan, tetapi masih tidak yakin bagaimana membuktikannya.
abhishek
Tetapi apa pengamatan Anda ketika Anda mencoba menemukannya? Mengapa upaya apa pun yang Anda lakukan tidak berhasil?
Bergi
@Gilles Mungkin Anda ingat cs.stackexchange.com/q/19430/14663 ?
Bergi

Jawaban:

32

Hal pertama yang perlu diperhatikan adalah bahwa ini tidak selalu benar. Sebagai contoh, tergantung pada bahasa fungsi dengan tipe itu, selain menjadi fungsi identitas, dapat: 1) loop selamanya, 2) bermutasi beberapa negara, 3) kembali null, 4) melempar pengecualian, 5) melakukan beberapa I / O, 6) garpu utas untuk melakukan sesuatu yang lain, 7) lakukancall/cc shenanigans, 8) menggunakan sesuatu seperti Java Object.hashCode, 9) menggunakan refleksi untuk menentukan apakah jenisnya bilangan bulat dan menambahkannya jika demikian, 10) menggunakan refleksi untuk menganalisis tumpukan panggilan dan melakukan sesuatu berdasarkan konteks di mana ia disebut, 11) mungkin banyak hal lain dan tentu saja kombinasi sewenang-wenang di atas.

Jadi properti yang mengarah ke ini, parametrik, adalah properti dari bahasa secara keseluruhan dan ada variasi yang lebih kuat dan lebih lemah. Bagi banyak kalkulus formal yang dipelajari dalam teori jenis, tidak ada perilaku di atas yang dapat terjadi. Misalnya, untuk Sistem F / kalkulus lambda polimorfik murni, di mana parametrikitas pertama kali dipelajari, tidak ada perilaku di atas yang dapat terjadi. Ini sama sekali tidak memiliki pengecualian, keadaan bisa berubah null,,call/cc , I / O, refleksi, dan itu sangat normalisasi sehingga tidak dapat loop selamanya. Seperti yang disebutkan Gilles dalam komentar, makalah Teorema gratis!oleh Phil Wadler adalah pengantar yang baik untuk topik ini dan rujukannya akan melangkah lebih jauh ke dalam teori, khususnya teknik hubungan logis. Tautan itu juga mencantumkan beberapa makalah lain oleh Wadler tentang topik parametrik.

Karena parametrisitas adalah sifat bahasa, untuk membuktikannya diperlukan terlebih dahulu mengartikulasikan bahasa dan kemudian argumen yang relatif rumit. Argumen informal untuk kasus khusus ini dengan asumsi kita berada dalam kalkulus lambda polimorfik adalah bahwa karena kita tidak tahu apa-apa tentang tkita tidak dapat melakukan operasi pada input (misalnya kita tidak bisa menambahnya karena kita tidak tahu apakah itu angka) atau buat nilai dari tipe itu (untuk semua yang kita tahu t= Void, tipe tanpa nilai sama sekali). Satu-satunya cara untuk menghasilkan nilai tipe tadalah dengan mengembalikan nilai yang diberikan kepada kami. Tidak ada perilaku lain yang mungkin. Salah satu cara untuk melihatnya adalah dengan menggunakan normalisasi yang kuat dan menunjukkan bahwa hanya ada satu bentuk istilah normal dari tipe ini.

Derek Elkins meninggalkan SE
sumber
1
Bagaimana Sistem F menghindari loop tak terbatas yang tidak dapat dideteksi oleh sistem tipe? Itu diklasifikasikan sebagai tidak dapat diselesaikan dalam kasus umum.
Joshua
2
@ Yosua - bukti ketidakmungkinan standar untuk masalah penghentian dimulai dengan asumsi bahwa ada loop tak terbatas di tempat pertama. Jadi memintanya untuk mempertanyakan mengapa Sistem F tidak memiliki loop tak terbatas adalah penalaran melingkar. Secara lebih luas, Sistem F hampir tidak bisa menyelesaikan Turing, jadi saya ragu itu memenuhi semua asumsi bukti itu. Mudah sekali bagi komputer untuk membuktikan bahwa semua programnya berakhir (tidak ada rekursi, tidak ada loop sementara, hanya sangat lemah untuk loop, dll.).
Jonathan Cast
@ Yosua: itu tidak dapat diselesaikan dalam kasus umum , yang tidak menghalangi penyelesaiannya dalam banyak kasus khusus. Secara khusus, setiap program yang kebetulan merupakan sistem yang diketik dengan baik istilah F telah terbukti berhenti: ada satu bukti seragam yang bekerja untuk semua program ini. Jelas, ini berarti ada program lain , yang tidak dapat diketik dalam sistem F ...
cody
15

Buktinya, klaimnya cukup rumit, tetapi jika itu yang Anda inginkan, Anda bisa membaca makalah asli Reynolds tentang topik itu.

Gagasan utamanya adalah bahwa ia berlaku untuk fungsi-fungsi polimorfik parametrik , di mana tubuh fungsi polimorfiknya sama untuk semua instantiasi monomorfik dari fungsi tersebut. Dalam sistem seperti itu, tidak ada asumsi yang dapat dibuat tentang jenis parameter tipe polimorfik, dan jika satu-satunya nilai dalam lingkup memiliki tipe generik, tidak ada hubungannya dengan mengembalikannya, atau mengembalikannya ke fungsi lain. telah didefinisikan, yang pada gilirannya tidak dapat melakukan apa-apa selain mengembalikannya atau meneruskannya ... dll Jadi pada akhirnya, yang dapat Anda lakukan adalah beberapa rantai fungsi identitas sebelum mengembalikan parameter.

Ya ampun
sumber
8

Dengan semua peringatan yang disebutkan Derek, dan mengabaikan paradoks yang dihasilkan dari menggunakan teori himpunan, izinkan saya membuat sketsa bukti dalam semangat Reynolds / Wadler.

Fungsi dari tipe:

f :: forall t . t -> t

adalah keluarga dari fungsi diindeks oleh jenis t .ftt

Idenya adalah bahwa, untuk secara formal mendefinisikan fungsi-fungsi polimorfik, kita tidak seharusnya memperlakukan tipe sebagai set nilai, melainkan sebagai hubungan. Tipe dasar, seperti Intmenginduksi hubungan kesetaraan - misalnya, dua Intnilai terkait jika mereka sama. Fungsi terkait jika mereka memetakan nilai terkait ke nilai terkait. Kasus yang menarik adalah fungsi polimorfik. Mereka memetakan tipe terkait dengan nilai terkait.

Dalam kasus kami, kami ingin membangun hubungan antara dua fungsi polimorfik dan g dari tipe:fg

forall t . t -> t

stfsfssstgtttfgfsgt

fstfsft

()()t()t((), c)ctf()ftf()()()ftcc()cftidttfid

Anda dapat menemukan lebih banyak detail di blog saya .

Bartosz Milewski
sumber
-2

EDIT: Komentar di atas telah memberikan bagian yang hilang. Beberapa orang sengaja bermain dengan bahasa yang kurang lengkap. Secara eksplisit saya tidak peduli dengan bahasa seperti itu. Bahasa tidak lengkap yang benar-benar dapat digunakan adalah hal sulit yang gila untuk desain. Seluruh sisanya memperluas apa yang terjadi mencoba menerapkan teorema ini ke bahasa penuh.

Salah!

function f(a): forall t: Type, t->t
    function g(a): forall t: Type, t->t
       return (a is g) ? f : a
    return a is f ? g : a

di mana isoperator membandingkan dua variabel untuk identitas referensi. Artinya, mereka mengandung nilai yang sama. Bukan nilai yang setara, nilai yang sama. Fungsinya fdan gsetara dengan beberapa definisi tetapi tidak sama.

Jika fungsi ini dilewatkan dengan sendirinya ia mengembalikan sesuatu yang lain; jika tidak mengembalikan inputnya. Sesuatu yang lain memiliki tipe yang sama seperti itu sendiri sehingga dapat diganti. Dengan kata lain, fbukan identitas, karena f(f)kembali g, sedangkan identitas akan kembali f.

Untuk memegang teorema itu harus mengasumsikan kemampuan konyol untuk mengurangi

function cantor(n, <z, a>) : forall t: t: Type int, <int, t> -> <int, t>
    return n > 1 ? cantor((n % 2 > 0) ? (n + 1) : n / 2, <z + 1, a>) : <z, a>
return cantor(1000, <0, a>)[1]¹

Jika Anda mau berasumsi bahwa Anda dapat mengasumsikan inferensi tipe yang jauh lebih mudah dapat ditangani.

Jika kita mencoba untuk membatasi domain sampai teorema tersebut berlaku, kita akhirnya harus membatasi domain tersebut terlalu jauh.

  • Fungsional Murni (tidak ada keadaan bisa berubah, tidak ada IO). OK saya bisa hidup dengan itu. Banyak waktu kami ingin menjalankan bukti atas fungsi.
  • Perpustakaan standar kosong. meh.
  • Tidak raisedan tidak exit. Sekarang kita mulai dibatasi.
  • Tidak ada tipe bawah.
  • Bahasa memiliki aturan yang memungkinkan kompiler untuk menutup rekursi tak terbatas dengan menganggapnya harus diakhiri. Kompiler diizinkan untuk menolak rekursi tak terbatas sepele.
  • Kompiler dibiarkan gagal jika disajikan dengan sesuatu yang tidak dapat dibuktikan dengan baik.² Sekarang perpustakaan standar tidak dapat mengambil fungsi sebagai argumen. Boo.
  • Tidak ada nil. Ini mulai bermasalah. Kami sudah kehabisan cara untuk menangani 1 / 0.³
  • Bahasa tidak dapat melakukan inferensi tipe cabang dan tidak memiliki penggantian ketika programmer dapat membuktikan inferensi tipe yang tidak dapat dilakukan oleh bahasa. Ini sangat buruk.

Keberadaan kedua kendala terakhir telah melumpuhkan bahasa. Meskipun Turing masih lengkap, satu-satunya cara untuk menyelesaikannya adalah dengan mensimulasikan platform internal yang menafsirkan bahasa dengan persyaratan yang lebih longgar.

¹ Jika menurut Anda kompilator dapat menyimpulkan yang itu, coba yang ini

function fermat(z) : int -> int
    function pow(x, p)
        return p = 0 ? 1 : x * pow(x, p - 1)
    function f2(x, y, z) : int, int, int -> <int, int>
        left = pow(x, 5) + pow(y, 5)
        right = pow(z, 5)
        return left = right
            ? <x, y>
            : pow(x, 5) < right
                ? f2(x + 1, y, z)
                : pow(y, 5) < right
                    ? f2(2, y + 1, z)
                    : f2(2, 2, z + 1)
    return f2(2, 2, z)
function cantor(n, <z, a>) : forall t: t: Type int, <int, t> -> <int, t>
    return n > 1 ? cantor((n % 2 > 0) ? (n + 1) : n / 2, <z + 1, a>) : <z, a>
return cantor(fermat(3)[0], <0, a>)[1]

² Bukti bahwa kompiler tidak dapat melakukan ini tergantung pada penyamaran. Kita dapat menggunakan beberapa pustaka untuk memastikan kompiler tidak dapat melihat loop sekaligus. Juga, kita selalu dapat membangun sesuatu di mana program akan bekerja tetapi tidak dapat dikompilasi karena kompiler tidak dapat melakukan induksi dalam memori yang tersedia.

³ Seseorang mengira Anda dapat memiliki pengembalian nihil ini tanpa jenis generik sewenang-wenang yang mengembalikan nihil. Ini membayar penalti yang buruk karena saya tidak melihat bahasa yang efektif yang dapat membayarnya.

function f(a, b, c): t: Type: t[],int,int->t
    return a[b/c]

tidak boleh dikompilasi. Masalah mendasar adalah pengindeksan array runtime tidak berfungsi lagi.

Joshua
sumber
@Bergi: Saya membuat contoh tandingan.
Joshua
1
Silakan luangkan waktu sejenak untuk merenungkan perbedaan antara jawaban Anda dan dua lainnya. Kalimat pembuka Derek adalah "Hal pertama yang perlu diperhatikan adalah bahwa ini belum tentu benar". Dan kemudian dia menjelaskan sifat-sifat bahasa apa yang membuatnya benar. jmite juga menjelaskan apa yang membuatnya benar. Sebaliknya, jawaban Anda memberikan contoh dalam bahasa yang tidak ditentukan (dan tidak umum) dengan nol penjelasan. (Apa itu foilquantifier?) Ini sama sekali tidak membantu.
Gilles 'SANGAT berhenti menjadi jahat'
1
@ WD: jika a adalah f maka tipe a adalah tipe f yang juga tipe g dan oleh karena itu typecheck harus dilewati. Jika kompiler nyata menendangnya saya akan menggunakan runtime cast yang selalu memiliki bahasa nyata untuk sistem tipe statis mendapatkannya salah dan tidak akan pernah gagal saat runtime.
Joshua
2
Bukan itu cara kerja pengetik huruf statis. Itu tidak memeriksa bahwa jenis cocok untuk satu input spesifik. Ada aturan jenis khusus, yang dimaksudkan untuk memastikan bahwa fungsi akan mengetik centang pada semua input yang mungkin. Jika Anda memerlukan penggunaan typecast maka solusi ini jauh lebih menarik. Tentu saja jika Anda mem-bypass sistem tipe maka jenis fungsi tidak menjamin apa pun - tidak mengherankan!
DW
1
@ WD: Anda melewatkan intinya. Ada informasi yang cukup untuk pemeriksa tipe statis untuk membuktikan kode adalah tipe aman jika memiliki kecerdasan untuk menemukannya.
Yosua