Tulis juru bahasa Haskell di Haskell


90

Latihan pemrograman klasik adalah menulis penerjemah Lisp / Skema di Lisp / Skema. Kekuatan bahasa lengkap dapat dimanfaatkan untuk menghasilkan penerjemah untuk subset bahasa.

Apakah ada latihan serupa untuk Haskell? Saya ingin mengimplementasikan subset Haskell menggunakan Haskell sebagai mesinnya. Tentu saja dapat dilakukan, tetapi adakah sumber daya online yang tersedia untuk dilihat?


Berikut backstory-nya.

Saya sedang mengeksplorasi ide menggunakan Haskell sebagai bahasa untuk mengeksplorasi beberapa konsep dalam kursus Struktur Diskrit yang saya ajarkan. Untuk semester ini saya telah memilih Miranda , bahasa yang lebih kecil yang menginspirasi Haskell. Miranda melakukan sekitar 90% dari apa yang saya ingin lakukan, tetapi Haskell melakukan sekitar 2000%. :)

Jadi ide saya adalah membuat bahasa yang memiliki fitur Haskell yang saya suka dan tidak mengizinkan yang lainnya. Seiring kemajuan siswa, saya dapat secara selektif "mengaktifkan" berbagai fitur setelah mereka menguasai dasar-dasarnya.

"Tingkat bahasa" pedagogis telah berhasil digunakan untuk mengajar Java dan Skema . Dengan membatasi apa yang dapat mereka lakukan, Anda dapat mencegah mereka menembak diri sendiri saat mereka masih menguasai sintaks dan konsep yang Anda coba ajarkan. Dan Anda dapat menawarkan pesan kesalahan yang lebih baik.


Saya memiliki dialek WIP Haskell yang diimplementasikan dengan Mengetik Haskell di Haskell sebagai dasarnya. Ada demo di sini chrisdone.com/toys/duet-delta Ini belum siap untuk rilis sumber terbuka publik, tapi saya bisa berbagi sumber dengan Anda jika tertarik.
Christopher Selesai

Jawaban:


76

Saya suka tujuan Anda, tapi itu pekerjaan besar. Beberapa petunjuk:

  • Saya telah mengerjakan GHC, dan Anda tidak menginginkan bagian mana pun dari sumber. Pelukan adalah implementasi yang jauh lebih sederhana dan lebih bersih, tetapi sayangnya itu dalam C.

  • Ini adalah bagian kecil dari teka-teki, tetapi Mark Jones menulis makalah yang indah berjudul Typing Haskell in Haskell yang akan menjadi titik awal yang bagus untuk front end Anda.

Semoga berhasil! Mengidentifikasi tingkat bahasa untuk Haskell, dengan bukti pendukung dari kelas, akan sangat bermanfaat bagi komunitas dan pasti merupakan hasil yang dapat dipublikasikan!


2
Saya penasaran apakah komentar tentang GHC ini masih akurat. GHC itu kompleks, tetapi cukup terdokumentasi dengan baik. Secara khusus, internal Notessangat membantu dalam memahami detail tingkat rendah, dan bab tentang GHC dalam Arsitektur Aplikasi Sumber Terbuka memberikan gambaran umum tingkat tinggi yang sangat baik.
sjy

37

Ada parser Haskell lengkap: http://hackage.haskell.org/package/haskell-src-exts

Setelah Anda menguraikannya, menghapus atau melarang hal-hal tertentu itu mudah. Saya melakukan ini untuk tryhaskell.org untuk melarang pernyataan impor, untuk mendukung definisi tingkat atas, dll.

Parsing saja modulnya:

parseModule :: String -> ParseResult Module

Kemudian Anda memiliki AST untuk modul:

Module SrcLoc ModuleName [ModulePragma] (Maybe WarningText) (Maybe [ExportSpec]) [ImportDecl] [Decl]    

Jenis Decl sangat luas: http://hackage.haskell.org/packages/archive/haskell-src-exts/1.9.0/doc/html/Language-Haskell-Exts-Syntax.html#t%3ADecl

Yang perlu Anda lakukan adalah menentukan daftar putih - deklarasi, impor, simbol, sintaks yang tersedia, lalu jalankan AST dan berikan "parse error" pada apa pun yang Anda tidak ingin mereka sadari. Anda dapat menggunakan nilai SrcLoc yang dilampirkan ke setiap node di AST:

data SrcLoc = SrcLoc
     { srcFilename :: String
     , srcLine :: Int
     , srcColumn :: Int
     }

Tidak perlu menerapkan ulang Haskell. Jika Anda ingin memberikan kesalahan kompilasi yang lebih bersahabat, cukup parsing kode, filter, kirim ke compiler, dan parse keluaran compiler. Jika itu adalah "tidak bisa mencocokkan jenis yang diharapkan dengan yang disimpulkan a -> b" maka Anda tahu itu mungkin terlalu sedikit argumen untuk suatu fungsi.

Kecuali jika Anda benar-benar ingin menghabiskan waktu menerapkan Haskell dari awal atau mengotak-atik internal Hugs, atau implementasi yang bodoh, saya pikir Anda sebaiknya memfilter apa yang diteruskan ke GHC. Dengan begitu, jika siswa Anda ingin mengambil basis kode mereka dan membawanya ke langkah berikutnya dan menulis beberapa kode Haskell yang benar-benar lengkap, transisinya akan transparan.


24

Apakah Anda ingin membangun penerjemah Anda dari awal? Mulailah dengan menerapkan bahasa fungsional yang lebih mudah seperti kalkulus lambda atau varian cadel. Untuk yang terakhir ada sebuah wiki yang cukup bagus bernama Write yourself a Scheme in 48 hours memberikan pengenalan yang keren dan pragmatis ke dalam teknik parsing dan interpretasi.

Menafsirkan Haskell dengan tangan akan jauh lebih kompleks karena Anda harus berurusan dengan fitur yang sangat kompleks seperti kelas tipe, sistem tipe yang sangat kuat (tipe-inferensi!) Dan evaluasi malas (teknik reduksi).

Jadi, Anda harus menentukan sebagian kecil Haskell untuk dikerjakan dan mungkin mulai dengan memperluas contoh Skema selangkah demi selangkah.

Tambahan:

Perhatikan bahwa di Haskell, Anda memiliki akses penuh ke interpreters API (setidaknya di bawah GHC) termasuk parser, compiler, dan tentu saja interpreter.

Paket yang akan digunakan adalah petunjuk (Language.Haskell. *) . Sayangnya saya tidak menemukan tutorial online tentang ini atau mencobanya sendiri tetapi kelihatannya cukup menjanjikan.


12
Perhatikan bahwa tipe-inferensi sebenarnya adalah algoritme garis 20-30 yang sangat mudah. itu indah dalam kesederhanaannya. Evaluasi malas juga tidak terlalu sulit untuk dikodekan. Saya akan mengatakan kesulitannya terletak pada sintaksis yang gila, pencocokan pola, dan hanya sejumlah besar hal dalam bahasa tersebut.
Claudiu

Menarik - Dapatkah Anda memposting link untuk algo tipe-inferensi?
Dario

5
Ya, lihat buku gratis ini - cs.brown.edu/~sk/Publications/Books/ProgLangs/2007-04-26 -, ada di halaman 273 (289 di pdf). Pseudocode alg ada di P296.
Claudiu

1
Ada juga implementasi algoritma tipe-inferensi / pemeriksaan (the?) Dalam " Implementasi Bahasa Pemrograman Fungsional ".
Phil Armstrong

1
Jenis inferensi dengan kelas-tipe tidaklah sederhana.
Christopher Selesai

20

membuat bahasa yang memiliki fitur Haskell yang persis seperti yang saya suka dan tidak mengizinkan yang lainnya. Seiring kemajuan siswa, saya dapat secara selektif "mengaktifkan" berbagai fitur setelah mereka menguasai dasar-dasarnya.

Saya menyarankan solusi yang lebih sederhana (seperti dalam sedikit pekerjaan yang terlibat) untuk masalah ini. Alih-alih membuat implementasi Haskell di mana Anda bisa mematikan fitur, bungkus kompiler Haskell dengan program yang pertama memeriksa bahwa kode tidak menggunakan fitur apa pun yang Anda larang, dan kemudian menggunakan kompiler siap pakai untuk mengkompilasinya.

Itu akan mirip dengan HLint (dan juga jenis kebalikannya):

HLint (sebelumnya Dr. Haskell) membaca program Haskell dan menyarankan perubahan yang diharapkan membuatnya lebih mudah untuk dibaca. HLint juga memudahkan untuk menonaktifkan saran yang tidak diinginkan, dan untuk menambahkan saran kustom Anda sendiri.

  • Terapkan "saran" Petunjuk Anda sendiri untuk tidak menggunakan fitur yang tidak Anda izinkan
  • Nonaktifkan semua saran HLint standar.
  • Buat pembungkus Anda menjalankan HLint yang telah dimodifikasi sebagai langkah pertama
  • Perlakukan saran HLint sebagai kesalahan. Artinya, jika HLint “mengeluh” maka program tidak melanjutkan ke tahap kompilasi


6

Seri kompiler EHC mungkin adalah taruhan terbaik: dikembangkan secara aktif dan tampaknya persis seperti yang Anda inginkan - serangkaian kompiler / juru bahasa lambda calculi yang berpuncak pada Haskell '98.

Tetapi Anda juga dapat melihat berbagai bahasa yang dikembangkan di Jenis dan Bahasa Pemrograman Pierce , atau penerjemah Helium (Haskell lumpuh yang ditujukan untuk siswa http://en.wikipedia.org/wiki/Helium_(Haskell) ).


6

Jika Anda mencari subset dari Haskell yang mudah diimplementasikan, Anda dapat menyingkirkan kelas tipe dan pemeriksaan tipe. Tanpa kelas tipe, Anda tidak memerlukan inferensi tipe untuk mengevaluasi kode Haskell.

Saya menulis kompiler subset Haskell yang dapat dikompilasi sendiri untuk tantangan Code Golf. Dibutuhkan kode subset Haskell pada input dan menghasilkan kode C pada output. Maaf, tidak tersedia versi yang lebih dapat dibaca; Saya mengangkat definisi bersarang dengan tangan dalam proses membuatnya sendiri.

Untuk seorang siswa yang tertarik dalam mengimplementasikan penerjemah untuk subset Haskell, saya akan merekomendasikan untuk memulai dengan fitur-fitur berikut:

  • Evaluasi malas. Jika penerjemah ada di Haskell, Anda mungkin tidak perlu melakukan apa pun untuk ini.

  • Definisi fungsi dengan argumen dan pelindung yang cocok dengan pola. Hanya khawatirkan variabel, kontra, nihil, dan _pola.

  • Sintaks ekspresi sederhana:

    • Literal bilangan bulat

    • Literal karakter

    • [] (nol)

    • Aplikasi fungsi (asosiatif kiri)

    • Infix :(kontra, asosiatif kanan)

    • Kurung

    • Nama variabel

    • Nama fungsi

Lebih konkretnya, tulis penerjemah yang dapat menjalankan ini:

-- tail :: [a] -> [a]
tail (_:xs) = xs

-- append :: [a] -> [a] -> [a]
append []     ys = ys
append (x:xs) ys = x : append xs ys

-- zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
zipWith f (a:as) (b:bs) = f a b : zipWith f as bs
zipWith _ _      _      = []

-- showList :: (a -> String) -> [a] -> String
showList _    []     = '[' : ']' : []
showList show (x:xs) = '[' : append (show x) (showItems show xs)

-- showItems :: (a -> String) -> [a] -> String
showItems show []     = ']' : []
showItems show (x:xs) = ',' : append (show x) (showItems show xs)

-- fibs :: [Int]
fibs = 0 : 1 : zipWith add fibs (tail fibs)

-- main :: String
main = showList showInt (take 40 fibs)

Pemeriksaan jenis adalah fitur penting dari Haskell. Namun, beralih dari tidak ada ke kompiler Haskell yang memeriksa tipe sangat sulit. Jika Anda mulai dengan menulis penerjemah untuk hal di atas, menambahkan pemeriksaan jenis seharusnya tidak terlalu menakutkan.


"Evaluasi malas. Jika penerjemah ada di Haskell, Anda mungkin tidak perlu melakukan apa pun untuk ini." Ini mungkin tidak benar. Lihat artikel Naylor di haskell.org/wikiupload/0/0a/TMR-Issue10.pdf untuk info lebih lanjut tentang menerapkan penerjemah malas di Haskell.
Jared Updike


3

Ini mungkin ide yang bagus - buat versi kecil NetLogo di Haskell. Inilah penerjemah kecil itu.


Tautannya sudah mati. Adakah kemungkinan konten ini masih ada di tempat lain? Saya ingin tahu ...
Nicolas Payette

hmm itu adalah posting blog dan saya tidak tahu kata kunci apa yang digunakan untuk mencarinya. Pelajaran yang baik untuk memasukkan lebih banyak info substansial saat memberikan tautan ...
Claudiu

1
Pencarian Google untuk "netlogo haskell" muncul ... pertanyaan ini. Pokoknya, bukan masalah besar. Terima kasih!
Nicolas Payette



2

Saya telah diberitahu bahwa Idris memiliki parser yang cukup kompak, tidak yakin apakah itu benar-benar cocok untuk diubah, tetapi ditulis dalam Haskell.


2

Kebun Binatang Bahasa Pemrograman Andrej Bauer memiliki implementasi kecil dari bahasa pemrograman murni yang berfungsi agak nakal bernama "minihaskell". Ini sekitar 700 baris OCaml, jadi sangat mudah dicerna.

Situs ini juga berisi versi mainan dari bahasa pemrograman ML-style, Prolog-style dan OO.


1

Tidakkah menurut Anda akan lebih mudah untuk mengambil sumber GHC dan menghapus apa yang tidak Anda inginkan, daripada menulis juru bahasa Haskell Anda sendiri dari awal? Secara umum, harus ada banyak sedikit usaha yang terlibat dalam menghilangkan fitur sebagai lawan untuk menciptakan / menambahkan fitur.

GHC ditulis di Haskell, jadi secara teknis itu tetap dengan pertanyaan Anda tentang juru bahasa Haskell yang ditulis di Haskell.

Mungkin tidak akan terlalu sulit untuk membuat semuanya terhubung secara statis dan kemudian hanya mendistribusikan GHCi yang Anda sesuaikan, sehingga siswa tidak dapat memuat modul sumber Haskell lainnya. Mengenai berapa banyak pekerjaan yang diperlukan untuk mencegah mereka memuat file objek Haskell lainnya, saya tidak tahu. Anda mungkin ingin menonaktifkan FFI juga, jika Anda memiliki banyak penipu di kelas Anda :)


1
Ini tidak semudah kedengarannya, karena banyak fitur bergantung pada yang lain. Tapi mungkin OP hanya ingin tidak mengimpor Prelude dan malah menyediakannya sendiri. Sebagian besar Haskell yang Anda lihat adalah fungsi normal, bukan fitur runtime tertentu. (Tapi tentu saja, banyak yang .)
jrockway

0

Alasan mengapa ada begitu banyak juru bahasa LISP adalah karena LISP pada dasarnya adalah pendahulu JSON: format sederhana untuk menyandikan data. Ini membuat bagian frontend cukup mudah ditangani. Dibandingkan dengan itu, Haskell, terutama dengan Ekstensi Bahasa, bukanlah bahasa yang paling mudah untuk diurai. Ini adalah beberapa konstruksi sintaksis yang terdengar sulit untuk dilakukan dengan benar:

  • operator dengan prioritas yang dapat dikonfigurasi, asosiatif, dan fiksasi,
  • komentar bertingkat
  • aturan tata letak
  • sintaks pola
  • do- blok dan desugaring ke kode monadik

Masing-masing, kecuali mungkin operator, dapat ditangani oleh siswa setelah Kursus Konstruksi Penyusun mereka, tetapi ini akan mengalihkan fokus dari cara kerja Haskell yang sebenarnya. Selain itu, Anda mungkin tidak ingin menerapkan semua konstruksi sintaksis Haskell secara langsung, tetapi menerapkan operan untuk menyingkirkannya. Yang membawa kita ke inti literal dari masalah ini, permainan kata-kata yang dimaksudkan sepenuhnya.

Saran saya adalah menerapkan pemeriksaan ketik dan penerjemah Corealih - alih Haskell penuh. Kedua tugas ini sendiri sudah cukup rumit. Bahasa ini, meskipun masih merupakan bahasa fungsional yang diketik dengan kuat, jauh lebih mudah untuk ditangani dalam hal pengoptimalan dan pembuatan kode. Namun, itu masih independen dari mesin yang mendasarinya. Oleh karena itu, GHC menggunakannya sebagai bahasa perantara dan menerjemahkan sebagian besar konstruksi sintaksis Haskell ke dalamnya.

Selain itu, Anda tidak boleh menghindari penggunaan frontend GHC (atau kompiler lain). Saya tidak akan menganggapnya curang karena LISP kustom menggunakan parser sistem LISP host (setidaknya selama bootstrap). Membersihkan Corecuplikan dan menyajikannya kepada siswa, bersama dengan kode aslinya, harus memungkinkan Anda memberikan gambaran umum tentang apa yang dilakukan frontend, dan mengapa lebih baik untuk tidak menerapkannya kembali.

Berikut beberapa link ke dokumentasi Coreseperti yang digunakan di GHC:

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.