BerandaComputers and TechnologyMungkin satu langkah menuju argumen bernama di Rust

Mungkin satu langkah menuju argumen bernama di Rust

Sejumlah bahasa pemrograman menawarkan fitur yang disebut “Argumen Bernama” atau “Argumen Berlabel”, yang membuat beberapa panggilan fungsi jauh lebih mudah dibaca dan lebih aman.

Mari kita lihat betapa sulitnya menambahkan ini di Rust.

Pertimbangkan fungsi seperti:

   fn   persegi  (x:  u32 , lebar:  u32 , y:  u32 , tinggi:  u32 , latar belakang: Opsi   Warna > , latar depan : Option   Warna > , baris: Option   Warna > ) ->  Widget  {   // ...   } rect (X, Y, WIDTH, HEIGHT, LINE, None, None);  // Ups, saya benar-benar bingung urutan argumennya.   

Akan lebih baik jika kita dapat memanfaatkan sintaksis Rust dan / atau sistem tipe membantu kita menghindari kebingungan argumen ini. Ini adalah fitur yang sering disebut Argumen Bernama atau Argumen Berlabel, yang tersedia dalam Python, OCaml, Swift, Dart,… dengan pengetikan yang kuat dalam beberapa bahasa yang mendukungnya.

Di Rust, orang bisa membayangkan menulis

   // Dalam contoh ini, `{(...)}` berarti kita menggunakan argumen bernama.   // Sintaks lain dimungkinkan.     fn   persegi  {(x:  u32 , lebar:  u32 , y:  u32 , tinggi:  u32 , latar belakang: Opsi   Warna >  = Tidak ada, latar depan: Opsi   Warna >  = Tidak ada, baris: Option   Warna >  = Tidak ada)} ->  Widget  {      // ...   } persegi {(x = X, y = Y, lebar = WIDTH, height = HEIGHT, garis = LINE)};  // Argumen diurutkan ulang secara otomatis. Nilai default dimasukkan secara otomatis. Kesalahan di atas menjadi tidak mungkin.   

Bukankah itu lebih mudah dibaca dan lebih aman?

Ini bukan tentang sintaks, hanya tentang semantik.

Saya sedang bermain-main dengan beberapa ide tentang bagaimana mengimplementasikan argumen bernama tanpa merusak sistem tipe yang ada. Saya memiliki beberapa implementasi prototipe berbasis makro. Masih terlalu dini untuk mengubahnya menjadi RFC, tapi saya harap ini bisa menjadi dasar untuk percakapan lebih lanjut.

Dengan prototipe ini, kita dapat menulis 1 :

   // Tentukan fungsinya. menetapkan! ( fn  (persegi, x:  u32 , lebar:  u32 , y:  u32 , tinggi:  u32 , latar belakang: Opsi   Warna >  = Tidak ada, latar depan: Opsi   Warna >  = Tidak ada, baris: Opsi   Warna >  = Tidak ada) ->  Widget  {      // ...   })   // Panggil fungsi, gaya makro.   // Argumen diurutkan ulang sesuai kebutuhan dan argumen opsional diganti dengan nilai defaultnya jika diperlukan. panggilan! (persegi, x = X, y = Y, lebar = WIDTH, height = HEIGHT, garis = LINE);   // Atau, setara dengan, gaya pembangun.    rect :: setup ()     .x (X)     .Y y)     .width (WIDTH)     .height (HEIGHT)     .line (LINE)     .panggilan();   // Di sisi lain, berikut ini gagal untuk memeriksa-ketik, karena kita lupa   // diperlukan argumen `tinggi`. panggilan! (persegi, x = X, y = Y, lebar = WIDTH, baris = LINE);  // ... atau yang setara    rect :: setup ()     .x (X)     .Y y)     .width (WIDTH)     .line (LINE)     .panggilan();   // Dan berikut ini gagal untuk memeriksa-ketik karena kita telah menggunakan argumen yang sama   // dua kali: panggilan! (persegi, x = X, y = Y, x = X, lebar = WIDTH, tinggi = TINGGI, garis = LINE);  // ... atau yang setara    rect :: setup ()     .x (X)     .Y y)     .x (X)     .width (WIDTH)     .height (HEIGHT)     .line (LINE)     .panggilan();   

Sedikit lebih bertele-tele tetapi jauh lebih aman, bukan?

Pada saat penulisan ini, prototipe hanya mendukung level modul fn deklarasi, bukan fungsi dalam impl atau sifat atau penutupan. Saya juga tidak berpikir bahwa ada pemblokir yang kuat, saya hanya belum mencoba menerapkannya. Ini dibiarkan sebagai karya masa depan.

Ide umumnya adalah bahwa makro mendefinisikan! membuat status akhir tingkat tipe mesin yang berperilaku sebagai berikut:

  • status adalah himpunan bernama argumen yang telah berlalu;
  • keadaan awalnya adalah {} (set kosong);
  • dari suatu negara S dan untuk setiap argumen a: T yang tidak muncul di S , ada transisi yang terwujud oleh fungsi S :: a (Self, T) -> S2 , di mana S2=S ∪ {a} ;
  • sebuah negara S menerima jika dan hanya jika S berisi semua argumen yang diperlukan;
  • ketika suatu negara S menerima, kami mewujudkannya dengan fungsi S :: call () itu memanggil fungsi asli kami.

Untuk saat ini, semua ini diimplementasikan dengan menghasilkan satu tipe struct per negara bagian S dan menyimpan semuanya dalam modul semi-tersembunyi.

Panggilan makro ! hanya panggilan berantai ke fungsi-fungsi ini S :: a (...) . Sistem tipe menangkap saat kami mencoba untuk menggunakan argumen yang sama dua kali (karena metode tidak ada) atau ketika kita kehilangan satu argumen (karena metode panggilan () tidak ada).

Ini masih merupakan prototipe awal tetapi saya yakin ini menunjukkan dengan memuaskan bahwa kita dapat memperluas Rust dengan argumen bernama, setidaknya dalam kasus sederhana, tanpa memodifikasi sistem jenis atau semantik bahasa secara mendalam.

Rasanya kita bisa memperluas sistem tipe Rust untuk memperbaiki pesan kesalahan (ketika kita menggunakan argumen yang sama dua kali atau melupakan satu argumen) dan kemungkinan kinerja waktu kompilasi kasus penggunaan ini tanpa membuatnya bertingkat di terlalu banyak tempat .

Seperti disebutkan sebelumnya, prototipe saat ini hanya berfungsi untuk deklarasi fungsi tingkat modul. Membuatnya berfungsi untuk impl – fungsi level dan sifat – fungsi level harus dimungkinkan tetapi akan membutuhkan sedikit lebih banyak usaha untuk menyimpan hingga level tipe mesin negara.

  • Prototipe awal tersedia di github 1 .
  • Prototipe ini didasarkan pada peti (hebat!) typed-builder oleh Chris Morgan dan IdanArye. Mereka melakukan semua pekerjaan berat 🙂

Read More

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Most Popular

Recent Comments