Sama seperti judulnya: jaminan apa yang ada untuk unit pengembalian fungsi Haskell yang akan dievaluasi? Orang akan berpikir bahwa tidak perlu menjalankan evaluasi apa pun dalam kasus seperti itu, kompilator dapat mengganti semua panggilan seperti itu dengan ()
nilai langsung kecuali jika ada permintaan eksplisit untuk ketatnya, dalam hal ini kode mungkin harus memutuskan apakah harus kembali ()
atau bawah.
Saya telah bereksperimen dengan ini di GHCi, dan sepertinya kebalikannya terjadi, yaitu fungsi seperti itu tampaknya dievaluasi. Contoh yang sangat primitif adalah
f :: a -> ()
f _ = undefined
Mengevaluasi f 1
kesalahan melempar karena kehadiran undefined
, jadi beberapa evaluasi pasti terjadi. Tidak jelas seberapa dalam evaluasi berjalan; kadang-kadang tampaknya masuk sedalam yang diperlukan untuk mengevaluasi semua panggilan ke fungsi yang kembali ()
. Contoh:
g :: [a] -> ()
g [] = ()
g (_:xs) = g xs
Kode ini berulang tanpa batas jika disajikan dengan g (let x = 1:x in x)
. Tapi kemudian
f :: a -> ()
f _ = undefined
h :: a -> ()
h _ = ()
dapat digunakan untuk menunjukkan bahwa h (f 1)
pengembalian ()
, jadi dalam hal ini tidak semua subekspresi bernilai unit dievaluasi. Apa aturan umum di sini?
ETA: tentu saja saya tahu tentang kemalasan. Saya bertanya apa yang mencegah penulis kompiler membuat kasus khusus ini lebih malas dari biasanya.
ETA2: ringkasan dari contoh-contoh: GHC tampaknya memperlakukan ()
persis seperti jenis lainnya, yaitu seolah-olah ada pertanyaan tentang nilai reguler yang menghuni jenis tersebut harus dikembalikan dari suatu fungsi. Fakta bahwa hanya ada satu nilai yang tampaknya tidak (ab) digunakan oleh algoritma optimasi.
ETA3: ketika saya mengatakan Haskell, maksud saya Haskell-sebagaimana-didefinisikan-oleh-the-Report, bukan Haskell-the-H-in-GHC. Tampaknya ada asumsi yang tidak dibagikan seluas yang saya bayangkan (yang 'oleh 100% pembaca'), atau saya mungkin bisa merumuskan pertanyaan yang lebih jelas. Meski begitu, saya menyesal mengubah judul pertanyaan, karena awalnya bertanya jaminan apa yang ada untuk fungsi yang dipanggil.
ETA4: sepertinya pertanyaan ini sudah berjalan, dan saya menganggapnya belum terjawab. (Saya sedang mencari fungsi 'pertanyaan dekat' tetapi hanya menemukan 'jawab pertanyaan Anda sendiri' dan karena tidak bisa dijawab, saya tidak turun jalan itu.) Tidak ada yang membawa apa pun dari Laporan yang akan memutuskan dengan cara apa pun , yang saya tergoda untuk menafsirkan sebagai jawaban yang kuat tetapi tidak pasti 'tidak ada jaminan untuk bahasa seperti itu'. Yang kami tahu adalah bahwa implementasi GHC saat ini tidak akan melewatkan evaluasi fungsi tersebut.
Saya mengalami masalah saat porting aplikasi OCaml ke Haskell. Aplikasi asli memiliki struktur saling rekursif dari banyak jenis, dan kode menyatakan sejumlah fungsi yang disebut assert_structureN_is_correct
N dalam 1..6 atau 7, masing-masing mengembalikan unit jika struktur memang benar dan melemparkan pengecualian jika tidak . Selain itu, fungsi-fungsi ini saling memanggil karena mereka menguraikan kondisi kebenaran. Di Haskell ini lebih baik ditangani menggunakan Either String
monad, jadi saya menyalinnya seperti itu, tetapi pertanyaan sebagai masalah teoritis tetap ada. Terima kasih atas semua masukan dan balasan.
sumber
h1::()->() ; h1 () = ()
danh2::()->() ; h2 _ = ()
. Jalankan keduanyah1 (f 1)
danh2 (f 1)
, dan perhatikan bahwa hanya yang pertama yang dituntut(f 1)
.f 1
"diganti" olehundefined
dalam semua kasus.... -> ()
dapat 1) mengakhiri dan mengembalikan()
, 2) mengakhiri dengan pengecualian / kesalahan runtime dan gagal mengembalikan apa pun, atau 3) menyimpang (rekursi tak terbatas). GHC tidak mengoptimalkan kode dengan asumsi hanya 1) dapat terjadi: jikaf 1
diminta, ia tidak melewati evaluasi dan kembali()
. Semantik Haskell adalah untuk mengevaluasi dan melihat apa yang terjadi di antara 1,2,3.()
(baik tipe atau nilainya) dalam pertanyaan ini. Semua pengamatan yang sama terjadi jika Anda mengganti() :: ()
dengan, katakanlah, di0 :: Int
mana-mana. Ini semua hanya konsekuensi lama yang membosankan dari evaluasi malas.()
tipe,()
danundefined
.Jawaban:
Anda tampaknya berasal dari asumsi bahwa tipe
()
hanya memiliki satu nilai yang mungkin()
, dan dengan demikian berharap bahwa setiap panggilan fungsi yang mengembalikan nilai tipe()
harus secara otomatis diasumsikan memang menghasilkan nilai()
.Ini bukan cara Haskell bekerja. Setiap jenis memiliki satu nilai lagi di Haskell, yaitu tidak ada nilai, kesalahan, atau disebut "bawah", disandikan oleh
undefined
. Jadi evaluasi sebenarnya terjadi:setara dengan bahasa Core
atau bahkan (*)
dan Core
_Case
adalah memaksa :Nilai dipaksa untuk bentuk kepala normal lemah. Ini adalah bagian dari definisi bahasa.
Haskell adalah tidak sebuah deklaratif bahasa pemrograman.
(*)
print x = putStr (show x)
danshow () = "()"
, sehinggashow
panggilan dapat dikompilasi sama sekali.Nilai tersebut memang dikenal di muka sebagai
()
, dan bahkan nilaishow ()
tersebut dikenal di muka sebagai"()"
. Namun semantik Haskell yang diterima menuntut bahwa nilai(f 1)
dipaksa ke bentuk kepala normal sebelum melanjutkan untuk mencetak yang dikenal di string terlebih dahulu"()"
,.sunting: Pertimbangkan
concat (repeat [])
. Haruskah itu[]
, atau haruskah itu loop tak terbatas?Jawaban "bahasa deklaratif" kemungkinan besar adalah untuk ini
[]
. Jawaban Haskell adalah, infinite loop .Kemalasan adalah "pemrograman deklaratif orang miskin", tetapi itu masih bukan hal yang nyata .
sunting2 :
print $ h (f 1) == _Case (h (f 1)) _Of () -> print ()
dan hanyah
dipaksa, tidakf
; dan untuk menghasilkan yang jawabannyah
tidak harus memaksakan apapun, menurut definisih _ = ()
.kata perpisahan: Kemalasan mungkin memiliki raison d'etre tapi itu bukan definisi. Kemalasan adalah apa adanya. Hal ini didefinisikan sebagai semua nilai yang awalnya merupakan thunks yang dipaksa untuk WHNF sesuai dengan permintaan yang datang dari
main
. Jika itu membantu menghindari bottom dalam kasus spesifik tertentu sesuai dengan keadaan spesifiknya, maka itu membantu. Jika tidak, tidak. Itu semuanya.Ini membantu untuk mengimplementasikannya sendiri, dalam bahasa favorit Anda, untuk merasakannya. Tetapi kita juga dapat melacak evaluasi ekspresi apa pun dengan menyebutkan semua nilai sementara dengan hati-hati .
Mengikuti Laporan , kami punya
kemudian
dan dengan
itu berlanjut
Sekarang, bagian 3.17.3 Kata Semantik Resmi dari Pola yang Cocok dari Laporan mengatakan
dan kasus
(r)
pada Gambar 3.2 menyatakan()
adalah konstruktor data arity 0, jadi itu sama dengandan hasil keseluruhan dari evaluasi adalah demikian
⊥
.sumber
case
dari Core, dan mengabaikan lubang menganga. :) Saya sudah mengedit untuk menyebutkan Core.show
olehprint
? Sesuatu sepertishow x = case x of () -> "()"
case
pada Core, bukan di Haskell itu sendiri. Haskell diterjemahkan ke dalam Core, yang memiliki pemaksaancase
, AFAIK. Anda benar bahwacase
di Haskell tidak memaksa dengan sendirinya. Saya bisa menulis sesuatu dalam Skema atau ML (jika saya bisa menulis ML itu), atau pseudocode.print
Pasukan sebanyak yang dibutuhkan untuk mencetak. itu tidak melihat jenisnya, jenisnya hilang, terhapus, pada saat program berjalan, subrutin pencetakan yang benar sudah dipilih dan dikompilasi, sesuai dengan jenisnya, pada waktu kompilasi; bahwa subrutin masih akan memaksakan nilai inputnya ke WHNF pada saat run time, dan jika itu tidak didefinisikan, itu akan menyebabkan kesalahan.