1 poin oleh GN⁺ 2024-11-14 | 1 komentar | Bagikan ke WhatsApp
  • Raku lebih mirip laboratorium fitur bahasa tempat berbagai ide diuji langsung, alih-alih satu desain tunggal yang sangat matang, sehingga kita bisa melihat eksperimen yang layak diadopsi bahasa lain di satu tempat
  • Junctions menyebarkan operasi ke himpunan nilai yang mungkin, lalu melipatnya dengan kuantor boolean seperti all, any, dan one untuk mengekspresikan kondisi kompleks secara singkat
  • * pada Whatevers dipakai untuk rentang tak terbatas, fungsi anonim, dan pengindeksan berbasis panjang daftar, menghasilkan gaya kode yang ringkas tetapi terasa asing
  • Regex Raku meninggalkan kompatibilitas PCRE, tetapi sebagai gantinya menawarkan grammar yang bisa dikomposisikan, pemisah nilai, lookahead yang mudah dibaca, dan fitur praktis seperti mengabaikan spasi secara default
  • Jika melihat Hyperoperators, Pair Syntax, Slangs, RakuAST, hingga Rakudo Star, Raku adalah bahasa yang bereksperimen luas dari sisi sintaks hingga cara distribusi

Mengapa Raku terlihat seperti laboratorium fitur bahasa

  • Setelah benar-benar memakai Raku selama lebih dari setahun, Raku tampak lebih dekat ke bahasa yang berfungsi sebagai laboratorium untuk fitur bahasa
  • Lima model konkurensi dan banyak cara melakukan hal yang sama menunjukkan sifat eksperimentalnya, seolah dibuat untuk “melihat apa yang terjadi”
  • Banyak fitur berinteraksi secara aneh dan memiliki banyak edge case juga karena menjalankan 100 eksperimen jauh lebih mudah daripada memoles semuanya agar bisa bekerja bersama
  • Fitur-fitur yang dibahas di sini adalah eksperimen ala Raku yang mungkin suatu hari akan muncul juga di bahasa pemrograman lain

Junctions: tumpang tindih nilai yang mungkin dan kuantor boolean

  • Junctions merepresentasikan tumpang tindih nilai-nilai yang mungkin, dan saat operasi diterapkan ke junction, operasi yang sama diterapkan ke semua nilai di dalamnya
    • 2|10 menjadi any(2, 10)
    • 2&10 + 3 menjadi all(5, 13)
    • (1&2) + (10^20) digabungkan menjadi sesuatu seperti all(one(11, 21), one(12, 22))
  • Kegunaan utama Junctions adalah mengekspresikan rumus boolean seperti all dan any
    • Tidak ada cara untuk membongkar struktur junction
    • Agar benar-benar berguna, junction harus lebih dulu dilipat menjadi boolean
  • so memaksa konversi junction menjadi boolean
    • so (1&2) + (10^20) < 15 adalah True
    • so (1&2) + (10^20) > 0 adalah False
  • list.all di Raku bukan metode yang menerima argumen, melainkan metode tanpa argumen yang mengubah daftar menjadi junction
    • <1 2 3>.all menjadi all(1, 2, 3)
    • Jika diterapkan is-prime($x), hasilnya menjadi seperti all(False, True, True)
  • Dengan menggabungkan Junctions, kita juga bisa membuat perilaku seperti unique
    • “Apakah setiap elemen sama dengan tepat satu elemen” dapat ditulis sebagai {.all == .one}
    • <1 2 3 7> bernilai benar, sedangkan <1 2 3 7 2> bernilai salah

Whatevers: rentang tak terbatas dan fungsi anonim dengan *

  • * pada Whatevers adalah simbol whatever di Raku yang memegang beberapa peran
  • Dalam rentang atau urutan, * berarti tidak ada batas
    • 1..* adalah 1..Inf
    • (2,4,8...*)[17] adalah 262144
  • Ekspresi yang mengandung * dipromosikan menjadi fungsi anonim
    • Perilaku ini disebut whatever-priming
    • Hasilnya adalah WhateverCode, dan selain tipenya, tidak bisa dibedakan dari fungsi lain
    • (* + 10)(2) menghasilkan 12
    • (^10).map(* % 2) membuat pola 0 1 0 1 ...
  • Jika ada dua * dalam satu ekspresi, masing-masing menjadi argumen posisi yang terpisah
    • (2, 30, 4, 50).map(* + *) mengembalikan (32, 54)
    • Ini berguna untuk definisi Fibonacci yang rumit, tetapi belum tentu lebih baik daripada pendekatan di mana setiap * berarti nilai yang sama
  • Banyak metode Raku bisa menerima fungsi, sehingga priming dipakai secara luas
    • l[*-1] terlihat seperti indeks negatif, tetapi sebenarnya [] memberikan panjang daftar ke fungsi lalu memakai hasilnya sebagai indeks
    • Jika panjang daftar adalah 10, maka l[*-1] berarti l[10-1], yaitu elemen terakhir
    • l.head(2) berarti dua elemen pertama, sedangkan l.head(*-2) berarti seluruh daftar kecuali dua elemen terakhir
  • Jika fungsi lain diberikan ke [], struktur seperti ring buffer juga bisa diimplementasikan dengan sederhana
    • @x[95 % *]-- menghitung posisi dengan memakai panjang daftar

Regex: grammar yang bisa dikomposisikan dengan meninggalkan kompatibilitas PCRE

  • Raku menggunakan sintaks regex baru yang berbeda dari regex POSIX maupun PCRE
    • Regex POSIX adalah struktur yang penuh backslash dan tanda baca
    • PCRE kompatibel mundur dengan POSIX dan mewarisi struktur backslash serta tanda baca yang lebih rumit
    • Perl 6 memutus kompatibilitas mundur dan memperkenalkan sintaks regex baru
  • Perbaikan yang paling menonjol adalah kemampuan komposisi
    • Di banyak bahasa, regex digabungkan dengan menyambung string regex
    • Raku memungkinkan penyisipan regex lain seperti /< foo >+/, sehingga regex foo bisa dicocokkan satu kali atau lebih tanpa bocor ke regex induk
  • Regex yang kompleks bisa dipecah menjadi bagian-bagian yang lebih kecil dan lebih mudah dibaca
    • Pendekatan ini menjadi dasar dukungan Raku terhadap grammar parsing sebagai fitur bawaan bahasa
  • Dengan memutus kompatibilitas mundur, beberapa fitur praktis juga ditambahkan
    • \\d+ % ',' mencocokkan 1, 1,2, dan 1,1,4, tetapi tidak mencocokkan 1, atau 12
    • lookahead dan non-capturing group bukan lagi kombinasi simbol yang sulit dibaca
    • r1 && r2 hanya mencocokkan string yang memenuhi r1 dan r2 sekaligus
    • : bisa dipakai untuk menghentikan backtracking
    • Spasi diabaikan secara default dan harus diaktifkan secara eksplisit di pola pencocokan
  • Terlepas dari kemampuan pemrosesannya sendiri, notasi regex Raku dapat dilihat sebagai kumpulan fitur yang mungkin juga muncul di bahasa lain

Hyperoperators: pemetaan tergeneralisasi yang turun hingga ke daftar bertingkat

  • Bentuk dasar Hyperoperators, yaitu l>>.method, mirip map tetapi turun secara rekursif ke subdaftar
    • [1, [2, 3], 4]>>.succ menjadi [2 [3 4] 5]
  • Di Raku, pemanggilan fungsi berbentuk f(list, *args) bisa diubah menjadi bentuk metode list.&f(* args)
    • Karena itu, >>. bekerja seperti operator pemetaan yang digeneralisasi
    • Bersama Whatever, ini bisa dipakai seperti [1, [2, 3], 4]>>.&(*+1)
  • Hyperoperator biner l1 << op >> l2 menerapkan op per elemen ke dua daftar
    • Daftar yang lebih pendek diulang sampai daftar yang lebih panjang habis
    • [1, 2, 3, 4, 5] <<+>> [10, 20] menghasilkan [11 22 13 24 15]
    • Bentuk << op<< atau >>op>> hanya mengulang sampai daftar kiri atau kanan habis
  • Ini juga bekerja pada nilai tunggal dan daftar bertingkat
    • [1, 2, 3, 4, 5] <<+>> 10 menghasilkan [11 12 13 14 15]
    • [1, [2, 3], 4, 5] <<+>> [10, 20] menghasilkan [11 [22 23] 14 25]
  • Hyperoperator untuk dua hash juga memiliki perilaku terpisah yang menerapkan op pada union, intersection, dan selisih hash
  • Nama Hyperoperator berasal dari fakta bahwa semua operasi dievaluasi secara paralel
    • Bahkan loop eksplisit pun bisa diparalelkan dengan prefiks hyper

Pair Syntax: unit dasar hash dan pengiriman flag yang ringkas

  • Pair adalah unit dasar yang menyusun hash di Raku
    • Hash adalah kumpulan pasangan key => value
    • Pair adalah tipe dasarnya, sedangkan hash adalah koleksi pair
  • Raku memiliki banyak sintaks colon singkat untuk menulis pair
    • my $x = 3; :$x berarti x => 3
    • :a<$x> berarti a => "$x"
    • :a($x) berarti a => 3
    • :3a berarti a => 3
  • Singkatan yang paling penting adalah :key dan :!key
    • Keduanya dipetakan ke key => True dan key => False
    • Ini dipakai sebagai cara ringkas untuk menambahkan flag ke metode
  • Metode match menerima banyak colon pair sebagai flag
    • Bentuk seperti :continue(:$c), :pos(:$p), :global(:$g), dan :overlap(:$ov) digunakan
    • :f(:$foo) dalam signature mendefinisikan parameter $foo sekaligus membuat alias :f
    • Pemanggil bisa menyetel flag yang sama dengan :f atau :foo
  • Argumen colon pair fleksibel dalam urutan dan posisinya
    • "abab".match(/../, :g) melakukan pencocokan global
    • "abab".match(/../, :g, :ov) melakukan pencocokan overlap
    • Urutannya bisa diubah dan digabung seperti "abab".match(:g:ov, /../)
    • Argumen keyword juga bisa diletakkan setelah pemanggilan fungsi seperti "abab".match(:g, /../):ov:2nd
  • Sintaks ini membuat penentuan opsi metode jauh lebih singkat dibanding match(global=True, overlap=True)

Slangs, RakuAST, Rakudo Star

  • Slangs adalah fitur untuk mengubah grammar Raku
  • RakuAST adalah fitur yang mem-parsing ekspresi Raku menjadi AST dari dalam Raku sendiri
    • Q/my $x; $x++/.AST menampilkan deklarasi variabel dan operasi postfix increment dalam bentuk RakuAST::StatementList
    • Ada juga contoh seperti Q/my $x; put $x/.AST.DEPARSE("NL") yang menampilkan kode Raku dalam notasi ala bahasa lain
  • Raku memiliki distribusi instalasi Rakudo Star
    • Distribusi ini menyediakan modul pihak ketiga terpilih yang sudah dipasang sebelumnya
    • Ini lebih mirip kompromi antara beban pemeliharaan standard library besar dan beban pengguna yang harus mencari sendiri paket yang tepat di ekosistem

1 komentar

 
GN⁺ 2024-11-14
Komentar Hacker News
  • Dulu saya pernah mengimplementasikan secara terpisah sesuatu yang mirip dengan fitur regular expression komposisional yang dibahas di sini untuk JavaScript, dan bahkan setelah beberapa tahun berlalu itu masih menjadi salah satu library yang paling sering saya bawa ke proyek lain
    Rasanya sedikit lebih dekat ke seolah-olah ada parser kelas satu di dalam bahasa, dan sebagai contoh ada implementasi parsing media type dengan regex yang memanfaatkannya: https://runkit.com/tolmasky/media-type-parsing-with-template...
    npm: templated-regular-expression, GitHub: https://github.com/tolmasky/templated-regular-expression
    Tentu saja bahasa pemrograman seharusnya punya parser sungguhan dan regex tidak seharusnya dipakai sebagai parser, tetapi kalau toh akan memakai regex, kemampuan untuk memecahnya menjadi bagian-bagian kecil itu sangat bagus

    • “Parser sungguhan” tidak cukup kuat untuk mem-parse Raku
      Regex di Raku, ketika digabungkan dengan grammar, jauh lebih kuat dan, jika ditulis dengan baik, lebih mudah dipahami daripada “parser sungguhan” mana pun. Untuk mem-parse Raku dengan parser biasa, parser itu harus bisa menambahkan dan menghapus entri saat proses parsing berjalan, dan parser Raku menangani ini dengan men-subclass grammar saat ini untuk menambah atau menghapus entri dalam cakupan leksikal saat ini lalu kembali ke grammar sebelumnya ketika cakupan berakhir
      Di Raku, regex adalah tata bahasa lain untuk menulis kode, hanya dengan grammar dan perilaku dasar yang sedikit berbeda, dan bisa memiliki parameter serta variabel. Jika ada pekerjaan yang tidak cocok dengan grammar regex, Anda bisa menyisipkan grammar Raku biasa untuk melakukan yang diperlukan lalu kembali lagi ke grammar regex
      Sebagai contoh, mencocokkan satu atau lebih A diikuti jumlah B yang sama dan jumlah C yang sama juga memungkinkan, dan di kebanyakan grammar regex, mencocokkan jumlah A dan B yang sama biasanya memerlukan rekursi di tengah, tetapi dengan begitu akan sulit memperlakukan C dengan cara yang sama dan juga jauh lebih sulit dibaca. Pendekatan Raku tidak memerlukan backtracking maupun rekursi sehingga bisa berjalan cukup cepat
      Jika dibungkus sebagai grammar, Anda mendapatkan parse tree lengkap, dan tanpa grammar pun tetap bisa, tetapi memakai grammar lebih mudah. Untuk contoh parser nyata, saya sering merekomendasikan JSON::TINY::Grammar: https://github.com/moritz/json/blob/master/lib/JSON/Tiny/Gra...
      Sejujurnya, sebagian besar desain “parser sungguhan” tampak seperti produk sampingan dari keterbatasan RAM pada komputer awal. Tahap tokenisasi terpisah ada untuk mengurangi RAM yang dipakai source code agar menyisakan memori untuk analisis semantik dan kompilasi sesudahnya, dan saya tidak melihat tahap-tahap setelah itu jadi sesederhana itu karenanya. Modul JSON::Tiny di atas pada dasarnya mengompilasi sambil mem-parse, karena grammar-nya membangun struktur data Raku native ke dalam action class selama proses parsing
  • Saya memakai Raku di produksi. Untuk pemrosesan teks, ini bahasa terbaik, dan pengalaman membuat parser dengannya luar biasa menyenangkan
    Yang mengejutkan, ini bukan bahasa tingkat atas untuk membangun pipeline teks LLM

    • Saya datang terlambat, tetapi saya penasaran apakah Anda tahu contoh yang bagus dalam Raku untuk memanggil HTTPS API dan melakukan polling sampai nilai tertentu dikembalikan
    • Saya penasaran apakah Anda memakai paket LLM di Raku, dan kalau ya, yang mana
  • Wow. Saya jadi ingin meninggalkan industri ini sebelum hari ketika saya harus memelihara codebase Raku tiba

    • Lucunya, setelah membaca tulisan blog itu saya justru ingin berhenti dari pekerjaan saya dan pergi mencari tim Raku untuk bekerja bersama mereka. Mungkin saya masih terlalu naif :)
    • Jika Anda belum pernah melihat kode Raku yang umum, reaksi seperti itu dari membaca artikel tersebut memang masuk akal
      Kalau melihat buku-buku pengantar Raku, banyak bagiannya terasa seperti Python dengan sintaks keluarga C. Sintaksnya berpusat pada kurung kurawal, tetapi kemudahan penggunaan, struktur data bawaan, dan fitur berorientasi objeknya semuanya tingkat tinggi
      Kalau Anda mengenal bahasa skrip tingkat tinggi lain, saya rasa kode Raku untuk skrip serupa akan cukup mudah dibaca. Saya juga tidak merasa kebanyakan orang akan memakai fitur yang benar-benar aneh dalam kode sehari-hari, dan meskipun Raku lebih fleksibel, kode biasa umumnya tidak terlihat membingungkan
    • Mirip Perl. Tidak ada yang benar-benar ingin memeliharanya, tetapi memakainya sangat menyenangkan. Daya ekspresinya sangat tinggi
      Itu juga terlihat dari kemampuan Raku untuk mendefinisikan argumen keyword dalam bentuk singkat, misalnya :global(:$g) atau mengasumsikan nilai True sehingga bisa memanggil global regex match seperti match(/foo/, :g). Perl juga punya banyak hal seperti ini, dan semuanya dibuat agar lebih cepat dan lebih menyenangkan dipakai, tetapi jadi kurang mudah dibaca bagi pemula
    • Aneh melihat orang-orang mengatakan hal yang mirip tentang pemeliharaan codebase yang ditulis dengan bantuan AI
      Sepertinya ini akan menjadi perbedaan generasi. Bisa jadi seluruh generasi programmer yang lebih tua akan merasa tersingkir dari lingkungan kerja yang bagi generasi sekarang terasa normal
    • Saya tidak berpikir Raku adalah bahasa yang memang ditujukan untuk “industri” itu
  • Beberapa fitur terasa setengah familier. Hyper terdengar seperti versi yang lebih improvisasional dari sesuatu di sisi recursion-schemes, dan * yang ditampilkan di sini agak mirip dengan _ di Scala
    _ enak dipakai untuk lambda, jadi saya berharap semua bahasa mengadopsi sesuatu yang mirip

  • > (2, 30, 4, 50).map(* + *) returns (32, 45) bukankah seharusnya returns (32, 54)? Soalnya elemen kedua berarti 4+50
    Mungkin ini hasil terjemahan lead yang berasal dari cara beberapa negara menyebut angka dalam bahasa Inggris bukan fifty-four melainkan seperti vierenvijftig, “empat dan lima puluh”

    • Saya cek di rakudo, dan memang mengembalikan (32 54). Sepertinya penulisnya salah ketik
  • Jadi sekarang Perl adalah obat gerbang menuju bahasa keluarga APL?

    • Betul. Lagi pula, tulisan itu juga tidak membahas meta-operator
      Misalnya, meta-operator reduce [ ] bisa dipakai bersama + infiks untuk melakukan “menjumlahkan semuanya”: [+] 1, 2, 3
  • Raku masih punya masalah besar pada kecepatan. Mem-parsing file log dengan regex adalah keunggulan Perl, tetapi Raku terbaru pun butuh waktu 6,5 kali lebih lama daripada Python 3.13, bahkan setelah waktu startup dikecualikan

    • Perlu disertai contoh. Dalam pengalaman saya, ada hal yang membuat Raku lebih cepat dan ada juga yang lebih lambat, jadi kalau hanya bilang “Raku butuh waktu 6,5 kali lebih lama daripada Python 3.13” tanpa melihat lambatnya pada apa, itu tidak terlalu berarti
  • Fakta bahwa (2,4,8...*)[17] menghasilkan 262144 benar-benar mengejutkan

    • Kata yang langsung muncul di kepala saya adalah “mengerikan”, lalu saya teringat pada “konsekuensi yang mengerikan” dan halting problem, tetapi selera tiap orang bisa berbeda
    • Deteksi kenaikan sudah ada di Perl6 sejak lama, jadi ini bukan fitur baru
      Selain Whatever, hal yang terasa baru setelah Perl6/Raku mungkin lebih ke sisi evaluasi malas
    • Cukup konsisten
      (1,3,9...*)[4,5] menjadi (81 243), dan (1,3,9...*)[(1..3)] menjadi (3 9 27)
      Bahkan bisa dinest, jadi bentuk seperti (1,2,4...*)[(1,2,4...*)[1,2,3]] juga memungkinkan
  • Ini terasa seperti memilih beberapa fitur Raku yang menarik untuk dibahas
    Menurut saya, fitur Raku yang paling penting adalah antarmuka baris perintah dan grammar
    Dukungan antarmuka baris perintah adalah fitur yang umum, misalnya ada implementasi dan contoh adopsi docopt. Tetapi di Raku, fitur ini sudah bawaan dan enak dipakai
    Sebaliknya, grammar adalah fitur yang jarang karena bahasa pemrograman menyediakan grammar sebagai komponen kelas satu, dan memungkinkan grammar dibuat serta dikombinasikan dengan pemrograman berorientasi objek

  • Saya sudah mengikuti proyek ini selama beberapa tahun dan memang menarik, tetapi sangat disayangkan karena Perl 6 tampaknya terlalu tersesat dalam perjalanan ke negeri cermin ini

    • Saya penasaran Patrick Michaud belakangan ini sedang bagaimana dan mengerjakan apa