1 poin oleh GN⁺ 2024-09-27 | 1 komentar | Bagikan ke WhatsApp
  • Pada 1989, High C Compiler untuk FM TOWNS tidak hanya mendukung lingkungan DOS, tetapi juga menyertakan berbagai fitur bahasa yang berorientasi pada pengguna, sesuatu yang langka untuk kompiler C saat itu
  • Dikombinasikan dengan DOS extender dari Phar Lap, kompiler ini menjadi bagian dari alur pengembangan yang memanfaatkan 80386 32-bit di lingkungan MS-DOS 16-bit, dan menjadi kompiler C pihak pertama untuk FM TOWNS
  • Pemisah garis bawah pada literal angka, argumen berlabel, rentang case, fungsi bertingkat, dan generator adalah fitur yang masuk ke standar C/C++ jauh lebih lambat, atau bahkan masih belum ada dalam standar
  • Fungsi bertingkat menyediakan “full function value” dalam bentuk closure non-escaping yang membawa pointer fungsi bersama pointer konteks, sehingga lebih ekspresif daripada pointer fungsi C biasa
  • Generator diimplementasikan sebagai gula sintaks di atas fungsi bertingkat, dengan struktur sederhana yang mengubah isi loop for milik pemanggil menjadi fungsi bertingkat lalu meneruskannya sebagai argumen yield

Posisi FM TOWNS dan High C

  • Sebuah manual kompiler C dari era 1980-an yang ditemukan di tumpukan buku terkait FM TOWNS ternyata berisi ekstensi bahasa yang jauh lebih kaya dari perkiraan
  • Untuk benar-benar memakai C dan bahasa turunannya di lingkungan nyata, selama bertahun-tahun dibutuhkan ekstensi vendor
    • Di lingkungan modern yang berpusat pada GCC, Clang, dan MSVC, ekstensi cenderung terfokus pada penanganan spesifik platform atau kontrol detail tingkat rendah
    • Pada 1980-an, lebih banyak perusahaan yang lebih kecil saling bersaing untuk diadopsi, sehingga fitur ekstensi juga jauh lebih beragam
  • Phar Lap membuat salah satu DOS extender awal yang memungkinkan pemanfaatan prosesor 80386 32-bit di lingkungan MS-DOS 16-bit
  • Atas permintaan Phar Lap, MetaWare mem-porting High C Compiler ke SDK DOS extender milik Phar Lap
  • Fujitsu mengintegrasikan DOS extender dari Phar Lap ke dalam OS platform FM TOWNS berbasis 803386, dan High C menjadi kompiler C pihak pertama untuk platform ini
  • FM TOWNS dirilis pada 1989, tepat sebelum C89, standar ANSI C pertama, diratifikasi

Fitur-fitur kecil yang mendahului standar

  • Pemisah garis bawah pada literal angka

    • Untuk memudahkan pembacaan literal angka yang panjang, pengguna dapat menyisipkan pemisah garis bawah di dalam angka
    • C++ memperkenalkan pemisah tanda kutip tunggal seperti 1'000'000 di C++14
    • C baru memperkenalkan fitur serupa di C23
  • Argumen berlabel

    • Nama argumen dapat ditambahkan pada fungsi yang memiliki banyak parameter atau sering memakai tipe seperti bool yang maknanya sulit terlihat langsung di titik pemanggilan
    • Argumen berlabel di High C bekerja mirip dengan fitur populer di Python
      • Label argumen bersifat opsional
      • Jika memakai label, argumen dapat ditentukan dalam urutan bebas dengan sintaks argumentName => value
      • Argumen tanpa label dan berlabel dapat dicampur, tetapi semua parameter fungsi tetap harus memiliki argumen yang sesuai
    • C dan C++ standar masih belum memiliki fitur ini
  • Rentang case

    • Menyediakan kemampuan untuk mencocokkan rentang nilai sekaligus, seperti case low..high di Pascal
    • C dan C++ standar tidak mengadopsi fitur ini

Fungsi bertingkat dan full function value

  • High C memungkinkan deklarasi fungsi bertingkat di dalam fungsi lain, seperti di Pascal
  • Cara implementasinya lebih mendekati bentuk yang lengkap dibanding Pascal standar atau ekstensi fungsi bertingkat milik GCC
  • High C tidak hanya mendukung deklarasi fungsi bertingkat, tetapi juga dapat mendeklarasikan tipe full function value
    • Tidak seperti pointer fungsi C tradisional, tipe ini menyimpan pointer fungsi bersama pointer konteks
    • Ini memungkinkan konteks yang ditangkap oleh fungsi bertingkat ditemukan kembali
    • Bentuk ini adalah closure non-escaping yang masa hidupnya tidak berlanjut setelah fungsi luarnya mengembalikan hasil
  • Ekstensi fungsi bertingkat milik GCC mencoba merujuk fungsi bertingkat sebagai pointer fungsi biasa dengan menulis kode yang dapat dieksekusi ke call stack untuk melakukan thunk pada pointer konteks
    • Pendekatan ini menimbulkan risiko keamanan besar dan membuat banyak platform menonaktifkan fitur tersebut sepenuhnya
  • Referensi fungsi lokal di High C dapat dipakai seperti nilai kelas satu, tetapi masa hidupnya tidak diperpanjang hingga setelah fungsi luar selesai
  • Fungsi bertingkat juga dapat melakukan goto ke fungsi induknya
    • Ini memungkinkan pelarian non-lokal ke luar fungsi bertingkat, seperti blok di Smalltalk
    • Dengan itu, fungsi dapat dibuat agar bertindak seperti alur kontrol
  • Objective-C memperoleh blocks yang dapat dipakai sebagai escaping closure pada 2009, dan C++ memperkenalkan lambda pada 2011
  • Keduanya tidak memiliki kemampuan pelarian non-lokal
  • C standar masih belum memiliki fitur fungsi bertingkat resmi

Korutin generator

  • MetaWare menonjolkan fitur generator sampai mendedikasikan satu bab penuh untuknya
  • Pada 1989, High C sudah mendukung korutin generator bergaya Python dalam plain C
  • Fungsi generator dideklarasikan dengan sintaks void foo(Arg arguments) -> (Yield yields)
    • Di dalam fungsi, pemanggilan berulang ke fungsi ajaib yield(values...) dapat menghasilkan urutan nilai
    • Pemanggil menelusuri nilai yang dihasilkan satu per satu dengan sintaks loop for baru berbentuk for variable... <- foo(arguments...) do { ... }
  • Implementasi ini dapat dikombinasikan secara rumit dengan fungsi bertingkat
    • Fungsi bertingkat di dalam generator dapat menangkap perilaku yield dari generator luarnya
    • Fungsi bertingkat dapat memanggil dirinya sendiri secara rekursif untuk menelusuri pohon atau struktur data rekursif sambil melakukan yield di setiap langkah
  • Bentuk seperti ini tampak sulit diwujudkan di Python atau banyak bahasa generator korutin arus utama lainnya

Cara implementasi generator dan perbedaannya dari bahasa standar

  • Generator di High C bekerja sebagai gula sintaks di atas fungsi bertingkat tanpa runtime tingkat tinggi
  • Deklarasi generator berbentuk void foo(Arg arguments) -> (Yield yields) setara dengan deklarasi fungsi biasa void foo(void yield(Yield yields)!, Arg arguments)
    • yield adalah parameter implisit bertipe “full function value”
    • Pemanggilan yield(values) di dalam isi generator hanyalah pemanggilan fungsi biasa terhadap parameter fungsi implisit itu
  • Isi loop for di sisi pemanggil diubah menjadi fungsi bertingkat
    • Fungsi bertingkat ini lalu diteruskan sebagai argumen yield ke generator
    • Strukturnya sederhana, tetapi efektif
  • Karena fungsi bertingkat mendukung pelarian non-lokal, break, continue, dan goto yang keluar dari isi loop for juga bekerja dengan cara melakukan goto ke posisi yang sesuai di luar loop
  • Kecil kemungkinan C standar akan mencoba mengintegrasikan fitur seperti ini
  • C++20 menyediakan fitur korutin yang sangat fleksibel dan kompleks, berbasis transformasi korutin saat waktu kompilasi
    • Fitur ini tampaknya dapat dipakai untuk mengimplementasikan generator
    • Namun hasilnya kemungkinan tidak akan dapat dipadukan dengan fungsi lokal secara seintuitif ini

1 komentar

 
GN⁺ 2024-09-27
Komentar Hacker News
  • Pada 2011, saya pernah merangkum for berbasis iterator. Itu sudah termasuk salah satu fitur yang telah lama terlupakan, dan saat itu saya juga membahas seperti apa bentuknya jika masuk ke standar C++
    Untungnya saya punya satu eksemplar High C/C++ Language Reference versi bahasa Inggris
    http://jdebp.uk./FGA/metaware-iterator-driven-for.html
    http://jdebp.uk./Proposals/metaware-iterator-driven-for.html
    • Saya penasaran bagaimana break atau return dikompilasi. Apakah mestinya diubah sehingga fungsi yield mengembalikan kode status lalu diperiksa di titik pemanggilan?
    • Apakah wajah tersenyum terbalik itu disengaja?
  • Di D, termasuk Das BetterC, ada fitur-fitur seperti ini: underscore pada literal angka, rentang case, argumen bernama, fungsi bersarang, fungsi bersarang statis, dan fitur mirip generator
    Misalnya bentuk seperti int a = 1_234_567;, case 5 .. case 6:, test(b:3, a:4); dimungkinkan
    Fungsi bersarang statis tidak bisa mengakses variabel frame dari fungsi luar, sehingga muncul error seperti Error: static function test.foo.plus cannot access variable i in frame of function test.foo
    Fitur yang mirip generator ada di https://dlang.org/spec/statement.html#foreach_over_struct_an...
    • Sepanjang membaca tulisan ini, saya terus teringat pada D. Rasanya Walter Bright akan muncul di kolom komentar
    • Menurut saya garbage collector D juga fitur yang sangat bagus. Pada kode level rendah, kadang manajemen memori manual memang diperlukan, tetapi dalam praktiknya sering kali itu tidak terlalu menjadi masalah, dan garbage collector membuat banyak hal jauh lebih mudah
      Misalnya jika membuat layanan cache in-memory, sebaiknya item cache itu sendiri tidak dilacak oleh garbage collector. Sebab garbage collector sering tidak mengetahui pola akses sebenarnya, sehingga justru bisa mengganggu. Namun sebagian besar komponen lain dalam layanan itu lebih cocok jika ada garbage collector
    • Ada pertanyaan. Ada yang tahu mengapa orang tidak suka konsep fungsi bersarang di C?
      Mengapa argumen bernama berbentuk test(a:4, b:3), bukan test(.a=4, b.=3);?
      Saya juga penasaran bagaimana first-class type bisa ditangani di C
  • Terkait hal ini, compiler C lcc-win menambahkan operator overloading, argumen fungsi default, dan function overloading. Di dokumentasinya, lihat bagian “generic functions” [1]
    Compiler C Plan 9 juga memperkenalkan sejumlah ekstensi bahasa, dan sebagian di antaranya, seperti struct/union anonim, kemudian masuk ke standar C. Saat ini GCC menerima flag -fplan9-extensions [2], yang mengaktifkan fitur-fitur yang cukup berguna, seperti konversi otomatis pointer struct menjadi field anonim dalam pemanggilan fungsi dan assignment
    [1] https://lcc-win32.services.net/C-Tutorial.pdf
    [2] https://gcc.gnu.org/onlinedocs/gcc/Unnamed-Fields.html
  • Siapa jenius yang membuat fitur-fitur ini? Sepertinya ada orang yang sangat visioner di perusahaan itu
    Sayang sekali fitur-fitur itu tidak tersebar luas ke dunia dan memengaruhi standar bahasa. Mengejutkan bahwa fitur seperti ini sudah ada sejak lama
    Ini juga pernah dibahas di Hacker News: https://news.ycombinator.com/item?id=38938402
    Apakah ada salinan PDF-nya di suatu tempat?
    • CLU sudah memiliki iterator, yaitu for loop dengan generator dan yield, pada pertengahan hingga akhir 1970-an [0]. Bahasa Icon dari periode yang mirip juga punya fitur generator serupa [1], dan menyebut yield sebagai suspend. Sepengetahuan saya, Ada (1983) juga memiliki fitur seperti ini
      Fitur-fitur bahasa seperti ini bukan sama sekali tidak dikenal
      [0] https://publications.csail.mit.edu/lcs/pubs/pdf/MIT-LCS-TR-2...
      [1] https://dl.acm.org/doi/pdf/10.1145/800055.802034
    • Di Bitsavers ada salinan manual referensi HC 1.2 (1985)
      Isinya menjelaskan underscore dalam angka, rentang case, parameter bernama, fungsi bersarang, hingga variabel fungsi penuh
      https://bitsavers.org/pdf/metaware/…
      Lihat Appendix A, sekitar 50-an halaman sebelum akhir file
    • MetaWare adalah perusahaan compiler produktif di Santa Cruz pada era 80–90-an. Saya menyukai apa yang mereka buat, dan budayanya juga cukup menarik
      Dulu, saat masih belajar dan menulis kode, saya mengenalnya lewat situs-situs yang agak mencurigakan
    • Itu tidak terlalu mengejutkan. Jika menggali arsip bahasa pemrograman tingkat tinggi setelah FORTRAN, Lisp, ALGOL, dan COBOL, kita akan melihat banyak ide bahasa seperti ini
      Kita juga akan menemukan sejarah yang kaya dari bahasa pemrograman sistem. Terlihat juga betapa miripnya desain C dan Go dalam hal mengabaikan apa yang sedang terjadi di ekosistem lain serta pengalaman masa lalu
    • Sayang sekali fitur-fitur seperti ini terlihat seperti fitur baru, bukan bagian dari daftar fitur standar yang disediakan sebagian besar bahasa pemrograman

Tautan manual kompilernya ada di https://winworldpc.com/product/metaware-high-c-cpp/33x
PDF manual C memiliki keterangan hak cipta tahun 2007

  • Pengajuan sebelumnya dan komentarnya ada di sini: https://news.ycombinator.com/item?id=38938402
  • Kalau Anda penasaran kenapa literal string pada contoh di gambar berakhir dengan ¥n, bukan \n, tampaknya contoh-contoh kode ini ditulis dalam Shift-JIS. Dalam Shift-JIS, posisi \ pada ASCII ditempati oleh ¥
    • Asalnya adalah JIS Roman [0], varian ASCII Jepang dari tahun 1969. Shift-JIS baru jauh belakangan menambahkan dukungan set karakter byte ganda
      [0] https://en.wikipedia.org/wiki/JIS_X_0201
    • Masalahnya, dalam Shift-JIS kode ASCII untuk backslash juga digunakan sebagai byte kedua dari karakter 2 byte. Jadi kadang literal string bahasa Jepang di C tidak bekerja dengan benar
      Untuk penggunaan ini, EUC-JP lebih baik karena tidak punya masalah seperti itu. Di Pascal, jika memakai komentar (* *) dan tidak memakai komentar { }, Shift-JIS tidak mengalami masalah ini
    • Penulis tidak memberi informasi kapan buku ini terbit, dan saya juga tidak menemukan informasinya. Namun sepertinya saat buku itu terbit, standar Shift-JIS belum ada
      Sebagai gantinya, besar kemungkinan yang dipakai adalah JIS X 0201(https://en.m.wikipedia.org.org/wiki/JIS_X_0201), yang menjadi dasar Shift-JIS
    • Serupa dengan itu, prompt DOS Jepang adalah C:¥, bukan C:\
  • Ekstensi-ekstensi ini adalah fitur Ada. Ada memiliki label berbentuk Call (Param_A => 1, Param_B => "Foo");, garis bawah pada angka dengan basis sembarang (X : Integer := 1_000;), subprogram bersarang, dan pemeriksaan berbasis rentang
    • Seperti disebutkan dalam tulisan, Pascal sudah memiliki fitur-fitur seperti ini sebelum Ada, dan tipe task dengan entry point pada dasarnya bisa dilihat sebagai generator
      Kita tampaknya sering lupa bahwa C pada masa itu luar biasa primitif dibanding banyak bahasa lain
  • Terlepas dari isinya, tipografi buku ini menarik. Terasa indah sekaligus mengerikan
    Saya tidak cukup paham aturan penulisan Jepang atau kerning, tetapi kelihatannya seperti mereka mengambil font lebar variabel yang memuat kanji dan huruf Latin, lalu memaksanya masuk ke dalam kotak lebar tetap
    Bagaimanapun, bagus bahwa contoh kodenya bukan memakai font 8pt seperti banyak buku yang saya punya
  • Melihat generator membuat saya teringat pada masalah iterasi internal/eksternal di Rust dan try_fold() (https://scribe.rip/@veedrac/rust-is-slow-and-i-am-the-cure-3...)
  • Khususnya soal generator, rasanya ini jauh mendahului zamannya. Mungkin Fujitsu bisa langsung mengimplementasikannya karena tidak perlu memedulikan proses standardisasi yang panjang
    Namun justru karena alasan itu, ekstensi-ekstensi seperti ini relatif tidak dikenal, dan puluhan tahun kemudian harus ditemukan kembali serta diciptakan ulang dalam C/C++ modern
    • Ternyata bukan Fujitsu, melainkan MetaWare. MetaWare adalah perusahaan dengan pengalaman kompilator yang cukup banyak, dan pada periode yang sama juga memiliki kompilator Pascal yang cukup dikenal. Pascal sudah memiliki fungsi bersarang
    • C bisa menjadi bahasa yang jauh lebih baik seandainya tidak dikuasai orang-orang yang bersikeras bahwa bahkan komplemen dua pun tidak boleh dimasukkan ke standar
    • Coroutine dan generator saat itu sudah merupakan konsep yang dipahami dengan baik. Lihat saja Icon. Jadi alasan utamanya tampaknya memang lebih karena mereka tidak perlu khawatir terhadap beban standardisasi