Design: Proposal: Tambahkan binding jenis antar bahasa

Dibuat pada 1 Mei 2019  ·  61Komentar  ·  Sumber: WebAssembly/design

WebAssembly saat ini sangat baik dalam mengeksekusi kode yang ditulis dalam bahasa arbitrer dari juru bahasa tertentu (biasanya JS), tetapi tidak memiliki beberapa fitur utama dalam hal menggabungkan beberapa bahasa arbitrer secara bersamaan .

Salah satu fitur ini adalah sistem tipe bahasa-agnostik. Saya ingin mengusulkan agar satu atau beberapa sistem seperti itu ditambahkan ke WebAssembly.

Selain itu, dalam diskusi fitur sebelumnya, beberapa kontributor telah menyatakan bahwa interoperabilitas bahasa tidak boleh menjadi tujuan desain WebAssembly. Meskipun saya setuju bahwa itu tidak harus menjadi tujuan prioritas tinggi , saya pikir itu adalah tujuan yang diperjuangkan dalam jangka panjang. Jadi sebelum saya masuk ke tujuan desain, saya akan menjelaskan alasan mengapa menurut saya interoperabilitas bahasa sepadan dengan usaha.

Mengapa peduli tentang interoperabilitas bahasa?

Manfaat dari hambatan bahasa-ke-bahasa yang lebih rendah meliputi:

  • Lebih banyak perpustakaan untuk pengguna wasm : Tidak perlu dikatakan lagi, tetapi meningkatkan interoperabilitas bahasa berarti bahwa pengguna dapat menggunakan perpustakaan yang ada lebih sering, bahkan jika perpustakaan ditulis dalam bahasa yang berbeda dari yang mereka gunakan.

  • Adopsi bahasa kecil yang lebih mudah: Di pasar saat ini, seringkali sulit bagi bahasa tanpa dukungan perusahaan untuk mendapatkan daya tarik. Bahasa baru (dan bahkan bahasa seperti D dengan penyempurnaan bertahun-tahun) harus bersaing dengan bahasa dengan ekosistem besar, dan menderita kekurangan perpustakaan. Interoperabilitas bahasa akan memungkinkan mereka untuk menggunakan ekosistem yang ada seperti Python atau Java.

  • Rantai alat agnostik bahasa yang lebih baik : Saat ini, sebagian besar bahasa memiliki skema pemuatan perpustakaan dan manajer paket mereka sendiri (atau, dalam kasus C/C++, beberapa yang tidak resmi). Menulis pembuat proyek agnostik bahasa itu sulit, karena bahasa ini sering memiliki ketergantungan yang halus, dan ketidakcocokan ABI, yang memerlukan solusi seluruh proyek monolitik untuk diselesaikan. Sistem tipe antar bahasa yang kuat akan memudahkan proyek untuk dipecah menjadi modul yang lebih kecil, yang dapat ditangani oleh solusi seperti npm.

Secara keseluruhan, saya pikir poin pertama adalah yang paling penting, dengan selisih yang lebar. Sistem tipe yang lebih baik berarti akses yang lebih baik ke bahasa lain, yang berarti lebih banyak peluang untuk menggunakan kembali kode daripada menulisnya dari awal. Saya tidak bisa melebih-lebihkan betapa pentingnya itu.

Persyaratan

Dengan mengingat hal itu, saya ingin menguraikan persyaratan yang harus dilalui oleh sistem tipe antar-bahasa.

Saya menulis dengan asumsi bahwa sistem tipe akan digunakan secara ketat untuk membubuhi keterangan fungsi yang diteruskan antar modul, dan tidak akan memeriksa bagaimana bahasa menggunakan memori linier atau terkelola dengan cara apa pun.

Agar benar-benar berguna dalam pengaturan wasm, sistem tipe seperti itu perlu:

1 - Keamanan

  • Jenis-aman: Yang dipanggil hanya boleh memiliki akses ke data yang ditentukan oleh pemanggil, gaya-kemampuan-objek.

    • Memori harus "dilupakan" di akhir panggilan. Orang yang dipanggil tidak boleh mendapatkan akses ke data pemanggil, kembali, dan kemudian mengakses data itu lagi dalam bentuk apa pun.

2 - Overhead

  • Pengembang harus nyaman melakukan panggilan antar-modul secara teratur, misalnya, dalam loop render.

    • Zero-copy: Sistem tipe harus cukup ekspresif untuk memungkinkan interpreter menerapkan strategi zero-copy jika mereka mau, dan cukup ekspresif bagi para pelaksana untuk mengetahui kapan zero-copy optimal.

3 - Struktur grafik

  • Sistem tipe harus mencakup struktur, pointer opsional, array panjang variabel, irisan, dll.

    • Idealnya, pemanggil harus dapat mengirim grafik objek yang tersebar di memori dengan tetap memperhatikan persyaratan 1 dan 2.

4 - Jenis referensi

  • Modul harus dapat bertukar tipe referensi yang bersarang jauh di dalam grafik struktur.

5 - Jembatan antara tata letak memori

  • Ini adalah poin yang sangat penting. Kategori bahasa yang berbeda memiliki persyaratan yang berbeda. Bahasa yang mengandalkan memori linier ingin melewatkan irisan memori, sedangkan bahasa yang mengandalkan GC ingin meneruskan referensi GC.

    • Sistem tipe ideal harus mengekspresikan tipe semantik, dan membiarkan bahasa memutuskan bagaimana menafsirkannya dalam memori. Meskipun meneruskan data antar bahasa dengan tata letak memori yang tidak kompatibel akan selalu menimbulkan beberapa overhead, meneruskan data antara bahasa yang serupa idealnya harus murah (misalnya, penyemat harus menghindari langkah serialisasi-deserialisasi jika memcpy dapat melakukan pekerjaan yang sama).

    • Binding tambahan juga memungkinkan caching dan strategi pengoptimalan lainnya.

    • Pekerjaan konversi saat meneruskan data di antara dua modul harus transparan bagi pengembang, selama tipe semantiknya kompatibel.

6 - Penanganan kesalahan waktu kompilasi

  • Kesalahan apa pun yang terkait dengan argumen panggilan fungsi yang tidak valid harus dapat dideteksi dan diekspresikan pada waktu kompilasi, tidak seperti di, misalnya, JS, di mana TypeErrors dilemparkan saat runtime ketika mencoba mengevaluasi argumen.
  • Idealnya, kompiler bahasa itu sendiri harus mendeteksi kesalahan tipe saat mengimpor modul wasm, dan menampilkan kesalahan ekspresif, idiomatik kepada pengguna. Bentuk pemeriksaan kesalahan seperti apa yang harus dilakukan perlu dirinci dalam repositori tool-conventions .
  • Ini berarti bahwa IDL dengan konverter yang ada ke bahasa lain akan menjadi nilai tambah.

7 - Memberikan poin Schelling untuk interaksi antar bahasa

  • Ini lebih mudah diucapkan daripada dilakukan, tetapi saya pikir wasm harus mengirim sinyal ke semua penulis kompiler, bahwa cara standar untuk saling beroperasi antar bahasa adalah X. Untuk alasan yang jelas, memiliki banyak standar yang bersaing untuk interoperabilitas bahasa tidak diinginkan.

Implementasi yang diusulkan

Yang saya usulkan adalah agar binding ke Cap'n'Proto IDL oleh @kentonv ditambahkan ke Webassembly.

Mereka akan bekerja dengan cara yang mirip dengan binding WebIDL: modul wasm akan mengekspor fungsi, dan menggunakan instruksi khusus untuk mengikatnya ke tanda tangan yang diketik; modul lain akan mengimpor tanda tangan ini, dan mengikatnya ke fungsinya sendiri.

Sintaks semu berikut dimaksudkan untuk memberikan gambaran tentang seperti apa bentuk ikatan ini; itu perkiraan dan sangat terinspirasi oleh proposal WebIDL, dan lebih berfokus pada tantangan teknis daripada memberikan daftar instruksi yang lengkap.

Instruksi pengikatan Capnproto semuanya akan disimpan di bagian pengikatan Cap'n'proto yang baru.

Jenis cap'n'proto

Standar akan membutuhkan representasi internal dari bahasa skema capnproto . Sebagai contoh, berikut jenis Capnproto:

``` Cap'n Proto
struct Orang {
nama @0 :Teks;
tanggal lahir @3 :Tanggal;

email @1 :Teks;
telepon @2 :Daftar(Nomor Telepon);

struct Nomor Telepon {
nomor @0 :Teks;
ketik @1 :Jenis;

enum Type {
  mobile @0;
  home @1;
  work @2;
}

}
}

struct Tanggal {
tahun @0 :Int16;
bulan @1 :UInt8;
hari @2 :UInt8;
}

might be represented as

```wasm
(<strong i="32">@capnproto</strong> type $Date (struct
    (field "year" Int16)
    (field "month" UInt8)
    (field "day" UInt8)
))
(<strong i="33">@capnproto</strong> type $Person_PhoneNumber_Type (enum 0 1 2))
(<strong i="34">@capnproto</strong> type $Person_PhoneNumber (struct
    (field "number" Text)
    (field "type" $Person_PhoneNumber_Type)
))
(<strong i="35">@capnproto</strong> type $Person (struct
    (field "name" Text)
    (field "email" Text)
    (field "phones" (generic List $Person_PhoneNumber))
    (field "birthdate" $Data)
))

Serialisasi dari memori linier

Pesan Capnproto melewati dua jenis data: segmen (byte mentah), dan kemampuan.

Ini secara kasar memetakan ke memori dan tabel linier WebAssembly. Dengan demikian, cara paling sederhana yang mungkin untuk perakitan web untuk membuat pesan capnproto adalah dengan melewatkan offset dan panjang ke memori linier untuk segmen, dan offset dan panjang ke tabel untuk kemampuan.

(Pendekatan yang lebih baik dapat dirancang untuk kemampuan, untuk menghindari pemeriksaan tipe runtime.)

Perhatikan bahwa perhitungan serialisasi yang sebenarnya akan terjadi dalam kode lem, jika ada (lihat Membuat kode lem ).

Operator pengikat

| Operator | Segera | Anak-anak | Deskripsi |
| :--- | :--- | :--- | :--- |
| segmen | off‑idx
len‑idx | | Mengambil nilai wasm off-idx 'th dan len-idx ' dari tupel sumber, yang keduanya harus i32 s, sebagai offset dan panjang sepotong memori linier di dimana segmen disimpan. |
| jago | off‑idx
len‑idx | | Mengambil nilai wasm off-idx 'th dan len-idx ' dari tuple sumber, yang keduanya harus i32 s, sebagai offset dan panjang irisan tabel di mana tabel kemampuan disimpan. |
| pesan | capnproto-type
tabel kemampuan | segmen | Membuat pesan capnproto dengan format capnproto-type , menggunakan tabel kemampuan dan segmen yang disediakan. |

Serialisasi dari memori terkelola

Sulit untuk menentukan perilaku spesifik sebelum proposal GC mendarat. Tetapi implementasi umumnya adalah bahwa pengikatan capnproto akan menggunakan operator konversi tunggal untuk mendapatkan tipe capnproto dari tipe GC.

Aturan konversi untuk tipe tingkat rendah akan cukup mudah: i8 mengonversi ke Int8, UInt8 dan bool, i16 mengonversi ke Int16, dll. Tipe tingkat tinggi akan mengonversi ke capnproto yang setara: referensi struktur dan array dikonversi ke pointer, referensi buram mengkonversi ke kemampuan.

Proposal yang lebih lengkap perlu mendefinisikan strategi untuk enum dan serikat pekerja.

Operator pengikat

| Operator | Segera | Anak-anak | Deskripsi |
| :--- | :--- | :--- | :--- |
| sebagai | capnproto-type
idx | | Mengambil nilai wasm idx ' dari tupel sumber, yang harus menjadi referensi, dan menghasilkan nilai capnproto capnproto-type . |

Deserialisasi ke memori linier

Deserialisasi ke memori linier sebagian besar mirip dengan membuat serial darinya, dengan satu peringatan tambahan: kode wasm sering kali tidak mengetahui sebelumnya berapa banyak memori yang akan digunakan oleh tipe capnproto, dan perlu menyediakan semacam metode manajemen memori dinamis kepada host .

Dalam proposal binding WebIDL, solusi yang diusulkan adalah meneruskan callback pengalokasi ke fungsi host. Untuk pengikatan capnproto, metode ini tidak akan cukup, karena alokasi dinamis perlu terjadi baik di sisi pemanggil maupun sisi yang dipanggil.

Solusi lain adalah mengizinkan peta pengikatan yang masuk untuk mengikat dua ekspresi pengikatan yang masuk (dan dengan demikian dua fungsi): satu yang mengalokasikan memori untuk data capnproto, dan satu yang benar-benar mengambil data.

Deserialisasi ke memori terkelola

Deserialisasi ke memori terkelola akan menggunakan jenis operator konversi yang sama dengan arah yang berlawanan.

Menghasilkan kode lem

Saat menautkan dua modul wasm bersama-sama (baik secara statis maupun dinamis), penyemat harus mencantumkan semua jenis capnproto yang umum untuk kedua modul, ikatan antara jenis fungsi dan dan jenis capnproto, dan menghasilkan kode lem di antara setiap pasangan jenis fungsi yang berbeda.

Kode lem akan tergantung pada jenis data terikat. Kode lem antara ikatan memori linier akan bermuara pada panggilan memcpy. Kode lem antara binding memori terkelola akan bermuara pada referensi yang lewat. Di sisi lain, kode lem antara memori linier dan terkelola akan melibatkan operasi konversi bersarang yang lebih rumit.

Misalnya, modul Java dapat mengekspor fungsi, mengambil argumen sebagai tipe GC, dan mengikat fungsi tersebut ke tanda tangan yang diketik; juru bahasa harus mengizinkan modul Python dan C++ untuk mengimpor tanda tangan tipe itu; pengikatan C++ akan meneruskan data dari memori linier, sedangkan pengikatan Python akan meneruskan data dari memori GC. Konversi yang diperlukan akan transparan untuk kompiler Java, Python dan C++.

Solusi alternatif

Di bagian ini, saya akan memeriksa cara alternatif untuk bertukar data, dan bagaimana mereka menilai metrik yang ditentukan di bagian Persyaratan .

Tukar pesan JSON

Ini adalah solusi kekerasan. Saya tidak akan menghabiskan banyak waktu untuk itu, karena kekurangannya cukup jelas. Gagal memenuhi persyaratan 2, 4 dan 6.

Kirim byte mentah yang dikodekan dalam format serialisasi

Ini adalah solusi parsial. Tentukan cara modul wasm untuk meneruskan irisan memori dan tabel linier ke modul lain, dan penulis modul kemudian dapat menggunakan format serialisasi (capnproto, protobuff atau lainnya) untuk mengkodekan grafik terstruktur ke dalam urutan byte, meneruskan byte, dan gunakan format yang sama untuk memecahkan kodenya.

Ini melewati 1 dan 3, dan dapat melewati 2 dan 4 dengan beberapa penyesuaian (misalnya meneruskan referensi sebagai indeks ke tabel). Itu dapat melewati 6 jika pengguna memastikan untuk mengekspor tipe serialisasi ke definisi tipe dalam bahasa pemanggil.

Namun, gagal pada persyaratan 5 dan 7. Ini tidak praktis saat mengikat antara dua implementasi GC; misalnya, modul Python yang memanggil perpustakaan Java dengan melalui Protobuf perlu membuat kamus bersambung sebagai memori linier, meneruskan potongan memori itu, dan kemudian mendeserialisasikannya sebagai objek Java, alih-alih membuat beberapa pencarian hashtable yang dapat dioptimalkan jauh di implementasi JIT.

Dan itu mendorong setiap penulis perpustakaan untuk menggunakan format serialisasi mereka sendiri (JSON, Protobuf, FlatBuffer, Cap'n Proto, SBE), yang tidak ideal untuk interoperabilitas; meskipun itu dapat dikurangi dengan mendefinisikan format serialisasi kanonik di tool-conventions .

Namun, menambahkan kemungkinan untuk melewatkan irisan memori linier yang sewenang-wenang akan menjadi langkah pertama yang baik.

Kirim objek GC

Dimungkinkan untuk mengandalkan modul yang saling mengirim objek GC .

Solusinya memiliki beberapa keuntungan: proposal GC sudah berjalan; melewati 1, 3, 4 dan 7. Data yang dikumpulkan GC mahal untuk dialokasikan, tetapi murah untuk disebarkan.

Namun, solusi itu tidak ideal untuk bahasa seperti C. Misalnya, modul D yang meneruskan data ke modul Rust perlu membuat serialisasi datanya ke dalam grafik GC, meneruskan grafik ke fungsi Rust, yang akan melakukan deserialize ke dalam memori liniernya. Proses ini mengalokasikan node GC yang segera dibuang, untuk banyak overhead yang tidak perlu.

Selain itu, proposal GC saat ini tidak memiliki dukungan bawaan untuk enum dan serikat pekerja; dan penanganan kesalahan akan dilakukan pada waktu tautan atau waktu berjalan alih-alih waktu kompilasi, kecuali jika kompiler dapat membaca dan memahami tipe GC wasm.

Gunakan penyandian lain

Pustaka serialisasi apa pun yang mendefinisikan sistem tipe dapat berfungsi untuk wasm.

Capnproto tampaknya paling tepat, karena penekanannya pada zero-copy, dan kemampuan objek bawaannya yang memetakan dengan rapi ke tipe referensi.

Pekerjaan yang tersisa

Konsep-konsep berikut perlu disempurnakan untuk mengubah proposal sederhana ini menjadi dokumen yang dapat diserahkan kepada Kelompok Masyarakat.

  • Operator pengikat
  • kesetaraan tipe GC
  • Kemampuan objek
  • Array Bool
  • Array
  • Konstanta
  • Obat generik
  • Jenis evolusi
  • Tambahkan jenis pengikatan "getter dan setter" ketiga.
  • Kemungkinan strategi caching
  • Dukungan untuk beberapa tabel dan memori linier

Sementara itu, umpan balik apa pun tentang apa yang sudah saya tulis akan diterima. Cakupannya di sini cukup luas, jadi saya menghargai bantuan untuk mempersempit pertanyaan apa yang perlu dijawab oleh proposal ini.

Komentar yang paling membantu

kita dapat menambahkan beberapa binding per jenis IR untuk mencakup sebagian besar bahasa.

Ini adalah asumsi mendasar yang penting yang saya yakini tidak benar. Pengalaman saya adalah bahwa ada (setidaknya!) sebanyak pilihan representasi karena ada implementasi bahasa. Dan mereka bisa menjadi rumit secara sewenang-wenang.

Ambil V8, yang sendiri memiliki beberapa lusin (!) representasi untuk string, termasuk pengkodean yang berbeda, tali heterogen, dll.

Kasus Haskell jauh lebih rumit daripada yang Anda gambarkan, karena daftar di Haskell malas, yang berarti bahwa untuk setiap karakter dalam string Anda mungkin perlu memanggil thunk.

Bahasa lain menggunakan representasi lucu untuk panjang string, atau tidak menyimpannya secara eksplisit tetapi mengharuskannya untuk dihitung.

Kedua contoh ini sudah menunjukkan bahwa tata letak data deklaratif tidak memotongnya, Anda sering kali harus dapat memanggil kode runtime, yang pada gilirannya mungkin memiliki konvensi pemanggilannya sendiri.

Dan itu hanya string, yang merupakan tipe data yang cukup sederhana secara konseptual. Saya bahkan tidak ingin memikirkan jumlah cara yang tak terbatas di mana bahasa mewakili jenis produk (tupel/struktur/objek).

Dan kemudian ada sisi penerima, di mana Anda harus dapat membuat semua struktur data ini!

Jadi saya pikir sama sekali tidak realistis bahwa kita akan pernah hampir mendukung "sebagian besar bahasa". Sebagai gantinya, kami akan mulai memberikan hak istimewa kepada beberapa orang, sementara kami telah menumbuhkan kebun binatang besar yang berisi barang-barang sewenang-wenang. Itu tampaknya fatal di berbagai tingkatan.

Semua 61 komentar

Ini sangat menarik! Saya baru saja membaca dengan cepat, dan hanya memiliki beberapa pemikiran awal, tetapi pertanyaan pertama dan terpenting saya adalah menanyakan mengapa mekanisme FFI yang ada yang sudah disediakan/digunakan sebagian besar bahasa tidak cukup untuk WebAssembly. Hampir setiap bahasa yang saya kenal memiliki beberapa bentuk C FFI, dan dengan demikian sudah mampu beroperasi hari ini. Banyak dari bahasa tersebut dapat melakukan pemeriksaan tipe statis berdasarkan binding tersebut juga. Selain itu, sudah ada banyak perkakas di sekitar antarmuka ini (misalnya, peti bindgen untuk Rust, erl_nif untuk Erlang/BEAM, dll.). C FFI telah memenuhi persyaratan yang paling penting, dan memiliki manfaat utama karena telah terbukti dan digunakan secara luas dalam praktik.

5 - Jembatan antara tata letak memori

Sistem tipe ideal harus mengekspresikan tipe semantik, dan membiarkan bahasa memutuskan bagaimana menafsirkannya dalam memori. Meskipun meneruskan data antar bahasa dengan tata letak memori yang tidak kompatibel akan selalu menimbulkan beberapa overhead, meneruskan data antara bahasa yang serupa idealnya harus murah (misalnya, penyemat harus menghindari langkah serialisasi-deserialisasi jika memcpy dapat melakukan pekerjaan yang sama).

Pekerjaan konversi saat meneruskan data di antara dua modul harus transparan bagi pengembang, selama tipe semantiknya kompatibel.

Terjemahan transparan dari satu tata letak ke tata letak lain ketika meneruskan data melintasi penghalang FFI benar-benar tampak seperti pekerjaan untuk backend kompiler atau runtime bahasa bagi saya, dan kemungkinan tidak diinginkan sama sekali dalam bahasa yang sensitif terhadap kinerja seperti C/C++/Rust/etc. Khususnya, untuk hal-hal yang Anda rencanakan untuk diteruskan bolak-balik di FFI, bagi saya tampaknya selalu lebih baik menggunakan ABI umum, daripada melakukan terjemahan apa pun, karena terjemahan kemungkinan akan dikenakan biaya yang terlalu tinggi. Manfaat memilih tata letak selain ABI umum platform tidak mungkin sepadan, tetapi saya akan dengan mudah mengakui bahwa saya mungkin salah paham dengan apa yang Anda maksud dengan tata letak alternatif.

Selain itu, menempatkan beban alat FFI yang solid pada kompiler/runtime memiliki manfaat tambahan, karena setiap peningkatan yang dilakukan dapat diterapkan pada platform lain, dan sebaliknya, karena peningkatan FFI untuk platform non-Wasm menguntungkan Wasm. Saya pikir argumennya harus benar-benar menarik untuk memulai dari awal dan membangun mekanisme FFI baru.

Maaf jika saya salah memahami tujuan proposal, atau melewatkan sesuatu yang kritis, seperti yang saya sebutkan di atas, saya perlu membaca lagi dengan lebih hati-hati, tetapi saya merasa perlu mengajukan pertanyaan awal saya sementara saya punya waktu.

Apache Arrow juga ada untuk ini, tetapi lebih fokus pada aplikasi berkinerja tinggi.

Saya pikir saya setuju dengan motivasi umum di sini dan pada dasarnya sejalan dengan diskusi yang kami lakukan dengan bagaimana Pengikatan IDL Web dapat digeneralisasi di masa depan. Memang, draf penjelasan sebelumnya berisi entri FAQ yang menyebutkan kasus penggunaan antar bahasa ini.

Perhatian utama saya (dan alasan untuk menghilangkan entri FAQ itu) adalah ruang lingkup: masalah umum pengikatan bahasa N tampaknya akan menghasilkan banyak diskusi terbuka (dan mungkin tidak berakhir), terutama mengingat belum ada yang melakukannya ( yang tentu saja merupakan masalah ayam-dan-telur). Sebaliknya, masalah yang ditangani oleh Web IDL Bindings cukup konkret dan mudah ditunjukkan dengan Rust/C++ hari ini, memungkinkan kami untuk memotivasi upaya (non-sepele) untuk menstandarisasi/mengimplementasikan dan juga dengan bersemangat membuat prototipe/memvalidasi solusi yang diusulkan.

Tetapi harapan saya adalah bahwa Pengikatan IDL Web memungkinkan kita untuk memecahkan masalah ayam-dan-telur ini dan mulai mendapatkan pengalaman dengan pengikatan antar-bahasa yang dapat memotivasi gelombang ekstensi berikutnya atau sesuatu yang baru dan bukan khusus IDL Web . (Perhatikan bahwa, seperti yang diusulkan saat ini, jika dua modul wasm menggunakan Web IDL Bindings yang kompatibel saling memanggil, impl pengoptimalan dapat melakukan pengoptimalan yang Anda sebutkan di sini; hanya tanpa ekspresi penuh Cap'n Proto.)

Saya harus menyatakan di muka bahwa saya belum punya waktu untuk sepenuhnya menyetujui proposal tersebut.
Alasannya adalah karena saya percaya tugas itu tidak mungkin. Ada dua alasan mendasar untuk ini:
A. Bahasa yang berbeda memiliki semantik yang berbeda yang belum tentu ditangkap dalam anotasi tipe. Contoh kasus, evaluasi Prolog sangat berbeda dengan evaluasi C++: ke titik di mana bahasa pada dasarnya tidak dapat dioperasikan. (Untuk Prolog Anda dapat mengganti beberapa bahasa lain)

B. Menurut definisi, sistem tipe LCD apa pun tidak dijamin untuk menangkap semua bahasa tipe bahasa tertentu. Itu membuat pelaksana bahasa memiliki pilihan yang sangat tidak nyaman: mendukung bahasa mereka sendiri atau melupakan manfaat dari sistem tipe bahasa mereka. Contoh kasus: Haskell memiliki 'kelas tipe'. Setiap implementasi Haskell yang melibatkan kelas tipe yang tidak mendukung akan secara efektif menghilangkannya dan membuatnya tidak dapat digunakan.
Contoh lain: dukungan C++ untuk obat generik memerlukan penghapusan waktu kompilasi dari generikitas; di sisi lain, ML, Java (dan banyak bahasa lain) menggunakan bentuk representasi universal -- yang tidak kompatibel dengan pendekatan yang diambil oleh C++.

Di sisi lain, memiliki dua ekspresi dari tipe yang diekspor/diimpor tampaknya memunculkan masalahnya sendiri: apakah sistem bahasa seharusnya memverifikasi bahwa kedua ekspresi itu konsisten dalam beberapa hal? Tanggung jawab siapa untuk melakukan pekerjaan ini?

@lukewagner Terima kasih atas

Bagi saya sepertinya ada dua hal yang agak bercampur dalam diskusi khusus ini - beberapa dari apa yang ada di bawah ini ditulis sehingga saya dapat memeriksa pemahaman saya, jadi jangan ragu untuk menunjukkan apa pun yang mungkin saya salah pahami atau lewatkan:

  1. Pengikatan host yang efisien

    • Pada dasarnya masalah WebIDL dimaksudkan untuk dipecahkan, setidaknya untuk lingkungan browser - deskripsi antarmuka yang memetakan dari modul->Host dan Host->modul, pada dasarnya mendelegasikan pekerjaan menerjemahkan dari satu ke yang lain ke mesin host. Terjemahan ini tidak selalu dijamin ideal, atau bahkan dioptimalkan sama sekali, tetapi mesin pengoptimal dapat memanfaatkannya untuk melakukannya. Namun, meskipun dioptimalkan, terjemahan masih dilakukan sampai tingkat tertentu, tetapi ini dapat diterima karena alternatifnya masih terjemahan, hanya lebih lambat.

  2. Binding modul-ke-modul heterogen yang efisien.

    • Dengan kata lain, diberikan dua modul, satu ditulis dalam source dan yang lainnya dalam dest , berbagi jenis di antara mereka, memanggil dari source->dest dan/atau dest->source

    • Jika tidak ada FFI umum yang tersedia, dan diberikan sesuatu seperti WebIDL, yaitu mendukung 1, jalur yang tidak dioptimalkan adalah menerjemahkan melalui beberapa jenis penyebut umum yang disediakan oleh lingkungan host saat menelepon melintasi hambatan bahasa, misalnya source type -> common type -> dest type .



      • Mesin pengoptimal secara teoritis dapat membuat terjemahan ini langsung dari source ke dest tanpa perantara, tetapi masih membebankan biaya terjemahan.



    • Jika FFI umum tersedia, yaitu source dan dest berbagi ABI (misalnya C ABI), maka source dan dest dapat saling memanggil secara langsung dengan tidak ada overhead sama sekali, melalui FFI. Ini mungkin skenario yang paling mungkin dalam praktik.

Jadi pendapat saya adalah bahwa pasti ada manfaat untuk memanfaatkan WebIDL, atau sesuatu seperti itu (yaitu superset yang mendukung kumpulan API/lingkungan host yang lebih luas), tetapi itu benar-benar hanya solusi untuk masalah yang diuraikan dalam 1, dan subset dari 2 yang berhubungan dengan ikatan antar bahasa di mana tidak ada FFI yang tersedia. Subset dari 2 di mana FFI _is_ tersedia, jelas lebih disukai daripada alternatif, karena tidak menimbulkan overhead.

Apakah ada alasan bagus untuk menggunakan IDL meskipun FFI merupakan opsi? Untuk lebih jelasnya, saya pasti setuju dengan menggunakan IDL untuk kasus penggunaan lain yang disebutkan, tetapi saya secara khusus bertanya dalam konteks interoperabilitas bahasa, bukan binding Host.

Beberapa pertanyaan tambahan yang saya miliki, jika C FFI (sebagai contoh, karena ini paling umum) dan IDL digunakan/hadir pada saat yang sama:

  • Jika bahasa source dan dest memberikan definisi tipe yang berbeda untuk tipe bersama dengan representasi dalam memori yang mendasari yang sama sesuai dengan ABI umum mereka (misalnya, representasi umum untuk array panjang variabel ) - akankah mesin host mencoba melakukan terjemahan di antara tipe-tipe itu hanya karena arahan IDL ada, meskipun mereka dapat dengan aman memanggil satu sama lain menggunakan FFI standar mereka?

    • Jika tidak, dan itu adalah keikutsertaan, itu sepertinya skenario yang ideal, karena Anda dapat menambahkan IDL untuk mendukung interop dengan bahasa tanpa FFI, sambil mendukung bahasa dengan FFI pada saat yang bersamaan. Saya tidak yakin bagaimana mesin Host akan membuatnya berfungsi. Saya belum memikirkannya sepenuhnya, jadi saya mungkin melewatkan sesuatu

    • Jika demikian, bagaimana mesin host menyatukan jenis?:



      • Jika mesin hanya peduli dengan tata letak, lalu bagaimana analisis statis dapat mendeteksi ketika pemanggil memberikan tipe argumen yang salah kepada pemanggil? Jika analisis semacam itu bukan tujuan, maka tampaknya IDL benar-benar hanya cocok untuk binding host, dan kurang dari itu lintas bahasa.


      • Jika mesin lebih mementingkan tata letak, dengan kata lain sistem tipe membutuhkan kompatibilitas nominal dan struktural:





        • Siapa yang mendefinisikan tipe otoritatif untuk beberapa fungsi? Bagaimana saya merujuk tipe otoritatif dari beberapa bahasa? Misalnya, katakanlah saya memanggil pustaka bersama yang ditulis dalam bahasa lain yang mendefinisikan fungsi add/2 , dan add/2 mengharapkan dua argumen dari beberapa tipe size_t . Bahasa saya tidak selalu tahu tentang size_t secara nominal, ia memiliki representasi yang kompatibel dengan ABI dari bilangan bulat tidak bertanda lebar mesin, usize , jadi ikatan FFI untuk fungsi itu dalam bahasa saya menggunakan my jenis bahasa. Mengingat itu, bagaimana kompiler saya tahu untuk menghasilkan IDL yang memetakan usize ke size_t .






  • Apakah ada contoh antarmuka IDL yang digunakan untuk memanggil antar modul dalam suatu program, di mana FFI tersedia tetapi secara eksplisit dibiarkan tidak digunakan demi antarmuka yang dijelaskan IDL? Khususnya sesuatu yang bukan WebAssembly, sebagian besar tertarik untuk mempelajari manfaat dalam kasus tersebut.

Saya akui saya masih mencoba menggali detail lengkap WebIDL dan pendahulunya, bagaimana semua ini cocok dengan host yang berbeda (browser vs non-browser) dan seterusnya, pasti beri tahu saya jika saya telah mengabaikan sesuatu.

@bitwalker

Ini sangat menarik!

Senang kamu menyukainya!

tetapi pertanyaan pertama dan terpenting saya adalah menanyakan mengapa mekanisme FFI yang ada yang sudah disediakan/digunakan sebagian besar bahasa tidak cukup untuk WebAssembly.

Sistem tipe C memiliki beberapa masalah sebagai IDL antar bahasa:

  • Ini beroperasi di bawah asumsi ruang alamat bersama, yang tidak aman dan sengaja tidak disimpan di WebAssembly. (pengalaman saya sendiri dengan FFI JS-ke-C menunjukkan bahwa implementasi cenderung hanya memperdagangkan keamanan untuk kecepatan)

  • Itu tidak memiliki dukungan asli untuk array panjang dinamis, serikat yang ditandai, nilai default, obat generik, dll.

  • Tidak ada padanan langsung untuk tipe referensi.

C++ memecahkan beberapa masalah ini (bukan yang terbesar, ruang alamat bersama), tetapi menambahkan banyak konsep yang tidak terlalu berguna di IPC. Tentu saja, Anda selalu dapat menggunakan superset C atau subset C++ sebagai IDL Anda dan kemudian menyusun aturan yang mengikat di sekitarnya, tetapi pada saat itu Anda hampir tidak mendapatkan manfaat dari kode yang ada, jadi Anda juga dapat menggunakan kode yang sudah ada. IDL.

Khususnya, untuk hal-hal yang Anda rencanakan untuk bolak-balik melintasi FFI

Saya tidak mengerti maksud Anda, tetapi untuk lebih jelasnya: Saya tidak berpikir meneruskan data yang dapat berubah bolak-balik antar modul dimungkinkan dalam kasus umum. Proposal ini mencoba menguraikan cara untuk mengirim data yang tidak dapat diubah dan mendapatkan data yang tidak dapat diubah sebagai imbalannya, di antara modul yang tidak memiliki informasi tentang cara yang lain menyimpan datanya.

Manfaat memilih tata letak selain ABI umum platform tidak mungkin sepadan, tetapi saya akan dengan mudah mengakui bahwa saya mungkin salah paham dengan apa yang Anda maksud dengan tata letak alternatif.

Masalahnya, saat ini, ABI yang umum adalah sepotong byte yang disimpan dalam memori linier. Tetapi di masa depan, ketika proposal GC diimplementasikan, beberapa bahasa (Java, C#, Python) akan menyimpan sangat sedikit atau tidak sama sekali dalam memori linier. Sebaliknya, mereka akan menyimpan semua data mereka dalam struktur GC. Jika dua bahasa ini mencoba untuk berkomunikasi, membuat serialisasi struktur ini ke aliran byte hanya untuk segera membatalkan serialisasinya akan menjadi overhead yang tidak perlu.


@KronicDeth Terima kasih, saya akan memeriksanya.

Meskipun, dari membaca sekilas dokumen, ini tampaknya merupakan superset dari Flatbuffers, yang secara khusus dimaksudkan untuk meningkatkan kinerja? Apa pun kualitasnya yang secara unik dapat membantu interoperabilitas modul WebAssembly, dibandingkan dengan Flatbuffers atau Capnproto?


@lukewagner

Tetapi harapan saya adalah bahwa Pengikatan IDL Web memungkinkan kita untuk memecahkan masalah ayam-dan-telur ini dan mulai mendapatkan beberapa pengalaman dengan pengikatan antar bahasa yang dapat memotivasi gelombang ekstensi berikutnya atau sesuatu yang baru dan bukan spesifik IDL Web.

Sepakat. Asumsi saya ketika menulis proposal ini adalah bahwa setiap implementasi capnproto binding akan didasarkan pada umpan balik dari implementasi proposal WebIDL.

Perhatian utama saya (dan alasan untuk menghilangkan entri FAQ itu) adalah ruang lingkup: masalah umum pengikatan bahasa N tampaknya akan menghasilkan banyak diskusi terbuka (dan mungkin tidak berakhir), terutama mengingat belum ada yang melakukannya ( yang tentu saja merupakan masalah ayam-dan-telur).

Saya pikir membahas implementasi capnproto memang memiliki nilai, meskipun sedini ini.

Secara khusus, saya mencoba menguraikan persyaratan apa yang harus/dapat coba dipenuhi oleh implementasi. Saya pikir itu juga akan berguna untuk membuat daftar kasus penggunaan umum yang mungkin coba ditangani oleh sistem tipe antar bahasa.

Mengenai masalah N-ke-N, saya fokus pada solusi ini:

  • Hanya khawatir tentang transfer data gaya RPC. Jangan mencoba meneruskan data yang dapat diubah, kelas, masa pakai penunjuk, atau jenis lain apa pun pada informasi yang lebih rumit daripada "vektor memiliki tiga bidang: 'x', 'y', dan 'z', yang semuanya mengapung".

  • Cobalah untuk mengelompokkan bahasa dan menggunakan kasus ke dalam "kelompok" strategi penanganan data. Menetapkan strategi di pusat cluster ini; kompiler bahasa mengikat strategi yang diberikan, dan penerjemah melakukan sisa pekerjaan NxN.


@fgmccabe

Alasannya adalah karena saya percaya tugas itu tidak mungkin. Ada dua alasan mendasar untuk ini:
A. Bahasa yang berbeda memiliki semantik yang berbeda yang belum tentu ditangkap dalam anotasi tipe. Contoh kasus, evaluasi Prolog sangat berbeda dengan evaluasi C++: ke titik di mana bahasa pada dasarnya tidak dapat dioperasikan. (Untuk Prolog Anda dapat mengganti beberapa bahasa lain)

Setiap implementasi Haskell yang melibatkan kelas tipe yang tidak mendukung akan secara efektif menghilangkannya dan membuatnya tidak dapat digunakan.

Ya, idenya bukan untuk mendefinisikan abstraksi "mudah kompatibel dengan semua bahasa" yang sempurna.

Yang mengatakan, saya pikir sebagian besar bahasa memiliki beberapa kesamaan dalam cara mereka menyusun data mereka (misalnya, mereka memiliki cara untuk mengatakan "setiap orang memiliki nama, email, dan usia", atau "setiap grup memiliki daftar orang ukuran sewenang-wenang").

Saya pikir mungkin untuk memanfaatkan kesamaan ini untuk secara signifikan mengurangi gesekan antar modul. (lihat juga jawaban saya untuk Lukewagner)

B. Menurut definisi, sistem tipe LCD apa pun tidak dijamin untuk menangkap semua bahasa tipe bahasa tertentu. Itu membuat pelaksana bahasa memiliki pilihan yang sangat tidak nyaman: mendukung bahasa mereka sendiri atau melupakan manfaat dari sistem tipe bahasa mereka.

Ya. Saya pikir aturan praktis di sini adalah "Jika itu adalah batas perpustakaan bersama, buatlah itu menjadi tipe capnproto, jika tidak, gunakan tipe asli Anda".

Di sisi lain, memiliki dua ekspresi dari tipe yang diekspor/diimpor tampaknya memunculkan masalahnya sendiri: apakah sistem bahasa seharusnya memverifikasi bahwa kedua ekspresi itu konsisten dalam beberapa hal? Tanggung jawab siapa untuk melakukan pekerjaan ini?

Ya, awalnya saya ingin memasukkan bagian tentang pemeriksaan invarian, dan bagian lain tentang kompatibilitas tipe, tetapi saya kehilangan keberanian.

Jawaban untuk "siapa yang bertanggung jawab" biasanya adalah "yang dipanggil" (karena mereka harus menganggap semua data yang mereka terima mencurigakan), tetapi pemeriksaan dapat dihilangkan jika penerjemah dapat membuktikan bahwa penelepon menghormati tipe invarian.

Sistem tipe C memiliki beberapa masalah sebagai IDL antar bahasa

Untuk memperjelas, saya tidak menyarankannya sebagai IDL. Sebaliknya saya menyarankan bahwa antarmuka biner (C ABI) sudah ada, terdefinisi dengan baik, dan sudah memiliki dukungan bahasa yang luas. Implikasinya kemudian adalah bahwa WebAssembly tidak perlu memberikan solusi lain kecuali masalah yang dipecahkan melampaui interop lintas bahasa.

Ini beroperasi di bawah asumsi ruang alamat bersama, yang tidak aman dan sengaja tidak disimpan di WebAssembly.

Jadi saya pikir saya melihat bagian dari kesalahpahaman di sini. Ada dua kelas FFI yang sedang kita bicarakan di sini, satu yang melibatkan berbagi memori linier (FFI memori bersama yang lebih tradisional), dan yang tidak (IPC/RPC yang lebih tradisional). Saya telah berbicara tentang yang pertama, dan saya pikir Anda lebih fokus pada yang terakhir.

Berbagi memori antar modul saat Anda mengendalikannya (seperti kasus di mana Anda menautkan beberapa modul independen sebagai bagian dari keseluruhan aplikasi) diinginkan untuk efisiensi, tetapi mengorbankan keamanan. Di sisi lain, dimungkinkan untuk berbagi memori linier yang ditunjuk khusus untuk FFI, meskipun saya tidak tahu seberapa praktis itu dengan perkakas default di luar sana hari ini.

Interop lintas-modul yang _tidak_ menggunakan FFI memori bersama, yaitu IPC/RPC, sepertinya cocok untuk WebIDL, capnproto atau salah satu saran lain dalam nada itu, karena itu adalah roti dan mentega mereka.

Bagian yang saya tidak yakin tentang itu adalah bagaimana memadukan dua kategori sedemikian rupa sehingga Anda tidak mengorbankan manfaat keduanya, karena pilihan untuk pergi ke satu arah atau yang lain sangat bergantung pada use case. Setidaknya seperti yang dinyatakan tampaknya kita hanya bisa memiliki satu atau yang lain, jika memungkinkan untuk mendukung keduanya, saya pikir itu ideal.

Itu tidak memiliki dukungan asli untuk array panjang dinamis, serikat yang ditandai, nilai default, obat generik, dll.

Saya pikir ini mungkin tidak relevan sekarang karena saya menyadari bahwa kita berbicara tentang dua hal yang berbeda, tetapi hanya untuk anak cucu: ABI tentu saja memiliki _representasi_ untuk array panjang variabel dan serikat yang ditandai, tetapi Anda benar karena C memang memiliki sistem tipe lemah, tapi bukan itu intinya, bahasa tidak menargetkan C FFI untuk sistem tipe C. Alasan mengapa C ABI berguna adalah karena C ABI memberikan kesamaan yang dapat digunakan bahasa untuk berkomunikasi dengan orang lain yang mungkin tidak memiliki konsep sistem tipe yang berinteraksi dengannya. Kurangnya fitur sistem tipe tingkat yang lebih tinggi tidak ideal, dan membatasi jenis hal yang dapat Anda ekspresikan melalui FFI, tetapi keterbatasan juga merupakan bagian dari mengapa ia begitu sukses dalam apa yang dilakukannya, hampir semua bahasa dapat menemukan caranya untuk mewakili hal-hal yang terpapar melalui antarmuka itu, dan sebaliknya.

C++ memecahkan beberapa masalah ini (bukan yang terbesar, ruang alamat bersama), tetapi menambahkan banyak konsep yang tidak terlalu berguna di IPC. Tentu saja, Anda selalu dapat menggunakan superset C atau subset C++ sebagai IDL Anda dan kemudian menyusun aturan yang mengikat di sekitarnya, tetapi pada saat itu Anda hampir tidak mendapatkan manfaat dari kode yang ada, jadi Anda juga dapat menggunakan kode yang sudah ada. IDL.

Setuju, untuk IPC/RPC, C adalah bahasa yang buruk untuk mendefinisikan antarmuka.

Masalahnya, saat ini, ABI yang umum adalah sepotong byte yang disimpan dalam memori linier.

Itu tentu saja primitif yang sedang kami kerjakan, tetapi C ABI mendefinisikan banyak hal di atas itu.

Tetapi di masa depan, ketika proposal GC diimplementasikan, beberapa bahasa (Java, C#, Python) akan menyimpan sangat sedikit atau tidak sama sekali dalam memori linier. Sebaliknya, mereka akan menyimpan semua data mereka dalam struktur GC. Jika dua bahasa ini mencoba untuk berkomunikasi, membuat serialisasi struktur ini ke aliran byte hanya untuk segera membatalkan serialisasinya akan menjadi overhead yang tidak perlu.

Saya tidak yakin bahwa bahasa-bahasa itu akan melompat dengan menunda GC ke Host, tetapi itu hanya spekulasi di pihak saya. Bagaimanapun, bahasa yang memahami struktur host GC yang dikelola hanya dapat memutuskan representasi umum untuk struktur tersebut menggunakan C ABI semudah mereka dapat direpresentasikan menggunakan capnproto, satu-satunya perbedaan adalah di mana spesifikasi representasi itu hidup. Yang mengatakan, saya hanya memiliki pemahaman yang sangat lemah tentang detail proposal GC dan bagaimana hubungannya dengan proposal binding host, jadi jika saya jauh dari sasaran di sini, silakan abaikan.

TL; DR: Saya pikir kami setuju sehubungan dengan interop modul di mana memori linier bersama tidak dimainkan. Tapi saya pikir memori bersama _is_ penting untuk didukung, dan C ABI adalah pilihan paling masuk akal untuk kasus penggunaan itu karena dukungan bahasa yang ada. Harapan saya adalah bahwa proposal ini seiring berkembangnya akan mendukung keduanya.

Yang kami butuhkan hanyalah cara yang efisien secara maksimal untuk menukar buffer byte, dan cara agar bahasa menyetujui formatnya. Tidak perlu memperbaiki ini ke satu sistem serialisasi tertentu. Jika Cap'n Proto adalah yang paling cocok untuk tujuan ini, itu dapat muncul sebagai default umum secara organik, daripada diamanatkan oleh wasm.

Saya tentu saja bias, karena saya membuat FlatBuffers , yang mirip dengan Cap'n Proto dalam hal efisiensi, tetapi lebih fleksibel dan didukung lebih luas. Namun saya tidak akan merekomendasikan format ini untuk diamanatkan oleh wasm juga.

Ada banyak format lain yang lebih disukai daripada kedua format ini mengingat kasus penggunaan tertentu.

Perhatikan bahwa baik Cap'n Proto dan FlatBuffers adalah nol salinan, akses acak, dan efisien dalam format bersarang (artinya format yang dibungkus dengan yang lain tidak kurang efisien daripada tidak dibungkus), yang merupakan properti nyata yang perlu dipertimbangkan untuk antar-bahasa komunikasi. Anda dapat membayangkan IDL yang memungkinkan Anda menentukan tata letak byte yang sangat tepat untuk buffer, termasuk "byte berikut adalah skema Cap'n Proto X".

Sementara saya secara halus mempromosikan diri, saya mungkin mengarahkan orang ke FlexBuffers yang seperti FlatBuffers tanpa skema. Ini memiliki nol-salinan yang diinginkan, akses acak dan properti bersarang murah yang sama, tetapi dapat memungkinkan bahasa untuk berkomunikasi tanpa menyetujui skema, tanpa melakukan codegen, mirip dengan bagaimana JSON digunakan.

@aardappel

Yang kami butuhkan hanyalah cara yang efisien secara maksimal untuk menukar buffer byte, dan cara agar bahasa menyetujui formatnya. Tidak perlu memperbaiki ini ke satu sistem serialisasi tertentu. Jika Cap'n Proto adalah yang paling cocok untuk tujuan ini, itu dapat muncul sebagai default umum secara organik, daripada diamanatkan oleh wasm.

Saya mengerti poin implisitnya, bahwa wasm tidak boleh digunakan sebagai cara untuk memaksakan satu standar kepada pesaingnya, dan saya pribadi tidak peduli dengan IDL yang dipilih.

Yang mengatakan, ketika semua dikatakan dan dilakukan, karet perlu memenuhi jalan di beberapa titik. Jika wasm ingin memfasilitasi komunikasi antar bahasa (yang, tentu saja, bukan asumsi yang dimiliki semua orang), maka diperlukan format standar yang dapat mengekspresikan lebih dari "byte ini membuat angka". Format itu dapat berupa capnproto, struktur C, flatbuffer atau bahkan sesuatu yang spesifik untuk wasm, tetapi itu tidak dapat menjadi bagian dari semua ini pada saat yang bersamaan, karena alasan yang diuraikan @fgmccabe .

Sementara saya secara halus mempromosikan diri, saya mungkin mengarahkan orang ke FlexBuffers yang seperti FlatBuffers tanpa skema. Ini memiliki nol-salinan yang diinginkan, akses acak dan properti bersarang murah yang sama, tetapi dapat memungkinkan bahasa untuk berkomunikasi tanpa menyetujui skema, tanpa melakukan codegen, mirip dengan bagaimana JSON digunakan.

Saya melihat daya tariknya, saya rasa ini bukan yang paling Anda inginkan, saat menulis perpustakaan. Masalah dengan JSON (selain dari waktu parse yang buruk) adalah ketika Anda menulis impor objek JSON di suatu tempat dalam kode Anda, Anda akhirnya menulis banyak kode sanitasi sebelum Anda dapat menggunakan data Anda, misalnya:

assert(myObj.foo);
assert(isJsonObject(myObj.foo));
assert(myObj.foo.bar);
assert(isString(myObj.foo.bar));
loadUrl(myObj.foo.bar);

dengan potensi kerentanan keamanan jika Anda tidak melakukannya.

Lihat juga 6 - Penanganan kesalahan waktu kompilasi di atas.


@bitwalker

Benar, saya tidak terlalu mempertimbangkan kemungkinan memori linier bersama. Saya membutuhkan seseorang yang lebih akrab dengan desain webassembly daripada saya ( @lukewagner ?) untuk mendiskusikan seberapa layak itu, dan apakah itu cara yang baik untuk mencapai panggilan antar-modul; itu juga akan tergantung pada berapa banyak asumsi yang diandalkan oleh FFI yang tidak valid oleh tata letak memori wasm.

Misalnya, FFI akan sering mengandalkan fakta bahwa bahasa host mereka menggunakan pustaka C, dan memberikan pustaka asli akses ke fungsi malloc secara langsung. Seberapa baik strategi itu dapat diterjemahkan menjadi wasm, dalam konteks dua modul yang saling curiga?

Saya kira saya harus mengatakan sesuatu di utas ini, sebagai pencipta Cap'n Proto, tetapi anehnya, saya belum menemukan bahwa saya memiliki banyak pendapat. Izinkan saya mengungkapkan beberapa pemikiran yang berdekatan yang mungkin menarik atau tidak.

Saya juga pemimpin teknologi Cloudflare Workers, lingkungan "tanpa server" yang menjalankan JavaScript dan WASM.

Kami telah mempertimbangkan untuk mendukung Cap'n Proto RPC sebagai protokol bagi pekerja untuk berbicara satu sama lain. Saat ini, mereka terbatas pada HTTP, jadi bilah disetel cukup rendah. :)

Di Worker, ketika satu Worker memanggil yang lain, sangat umum kasus keduanya berjalan di mesin yang sama, bahkan dalam proses yang sama. Untuk alasan itu, serialisasi nol-salinan seperti Cap'n Proto jelas sangat masuk akal, terutama bagi Pekerja WASM karena mereka beroperasi pada memori linier yang, secara teori, dapat dibagi secara fisik di antara mereka.

Alasan kedua yang kurang terkenal yang menurut kami cocok adalah sistem RPC. Cap'n Proto memiliki fitur protokol RPC kemampuan objek penuh dengan pipelining janji, dimodelkan setelah CapTP. Ini memudahkan untuk mengekspresikan interaksi yang kaya dan berorientasi objek dengan cara yang aman dan berperforma tinggi. Cap'n Proto RPC bukan hanya protokol point-to-point, melainkan model interaksi antara sejumlah pihak jaringan, yang menurut kami akan menjadi masalah yang cukup besar.

Sementara di lahan WASM, WASI memperkenalkan API berbasis kapabilitas. Sepertinya ada "sinergi" yang menarik di sini.

Dengan semua itu, beberapa tujuan desain Cap'n Proto mungkin tidak masuk akal untuk kasus penggunaan khusus FFI:

  • Pesan Cap'n Proto dirancang untuk menjadi posisi-independen dan berdekatan sehingga mereka dapat ditransmisikan dan dibagi antara ruang alamat. Pointer bersifat relatif, dan semua objek dalam pesan perlu dialokasikan dalam memori yang berdekatan, atau setidaknya sejumlah kecil segmen. Ini secara signifikan memperumit model penggunaan dibandingkan dengan objek asli. Saat menggunakan FFI dalam ruang memori linier yang sama, overhead ini terbuang sia-sia, karena Anda dapat meneruskan pointer asli ke objek heap yang longgar dengan baik.
  • Pesan Cap'n Proto dirancang agar kompatibel ke depan dan ke belakang antara versi skema, termasuk kemampuan untuk menyalin objek dan sub-pohon tanpa kehilangan tanpa mengetahui skema. Ini memerlukan beberapa informasi jenis cahaya yang disimpan langsung dalam konten, yang dikodekan oleh Cap'n Proto sebagai metadata pada setiap penunjuk. Jika dua modul yang berkomunikasi melalui FFI dikompilasi secara bersamaan, maka metadata ini tidak diperlukan.
  • Janji pipa, pemendekan jalur, dan jaminan pemesanan Cap'n Proto RPC masuk akal ketika ada latensi yang tidak dapat diabaikan antara penelepon dan yang dipanggil. FFI pada satu CPU tidak memiliki latensi seperti itu, dalam hal ini mesin pipelining yang dijanjikan mungkin hanya membuang-buang siklus.

Singkatnya, saya pikir ketika Anda memiliki modul yang digunakan secara independen di kotak pasir terpisah yang berbicara satu sama lain, Cap'n Proto sangat masuk akal. Tetapi untuk modul yang digunakan secara bersamaan dalam satu kotak pasir, itu mungkin berlebihan.

Terima kasih untuk umpan baliknya!

Pointer bersifat relatif, dan semua objek dalam pesan perlu dialokasikan dalam memori yang berdekatan, atau setidaknya sejumlah kecil segmen. Ini secara signifikan memperumit model penggunaan dibandingkan dengan objek asli. Saat menggunakan FFI dalam ruang memori linier yang sama, overhead ini terbuang sia-sia, karena Anda dapat meneruskan pointer asli ke objek heap yang longgar dengan baik.

Saya tidak tahu seberapa layak pendekatan memori linier bersama untuk wasm (lihat di atas).

Yang mengatakan, bagaimanapun, saya tidak berpikir overhead dari pointer relatif akan seburuk itu. WebAssembly sudah menggunakan offset relatif terhadap awal memori linier, dan implementasi memiliki trik untuk mengoptimalkan instruksi ADD dalam banyak kasus (saya pikir), sehingga overhead menggunakan pointer relatif mungkin dapat dioptimalkan juga.

Pesan Cap'n Proto dirancang agar kompatibel ke depan dan ke belakang antara versi skema, termasuk kemampuan untuk menyalin objek dan sub-pohon tanpa kehilangan tanpa mengetahui skema. [...] Jika dua modul yang berkomunikasi melalui FFI dikompilasi secara bersamaan, maka metadata ini tidak diperlukan.

Saya rasa itu tidak benar. Memiliki cara bagi modul untuk mendefinisikan tipe yang kompatibel ke belakang pada batasnya memungkinkan wasm untuk menggunakan model pohon ketergantungan, sementara sebagian besar menghindari masalah berlian ketergantungan Haskell.

Sumber overhead yang tidak berguna yang lebih besar adalah cara capnproto xor s variabelnya terhadap nilai defaultnya, yang berguna ketika nol-byte dikompresi, tetapi kontra-produktif dalam alur kerja nol-salinan.

Saya tidak tahu seberapa layak pendekatan memori linier bersama untuk wasm (lihat di atas).

Ah, TBH Saya rasa saya tidak memiliki konteks yang cukup untuk mengikuti bagian diskusi itu. Jika Anda tidak memiliki ruang alamat bersama, ya, Cap'n Proto mulai masuk akal.

Saya senang memberikan saran tentang cara mendesain format seperti ini. FWIW ada beberapa hal kecil yang akan saya ubah di Cap'n Proto jika saya tidak peduli dengan kompatibilitas dengan aplikasi yang sudah ada saat ini... namun sebagian besar, seperti, detail penyandian pointer tingkat rendah.

Sumber overhead yang tidak berguna yang lebih besar adalah cara capnproto mengubah variabelnya terhadap nilai defaultnya, yang berguna ketika nol-byte dikompresi, tetapi kontra-produktif dalam alur kerja nol-salinan.

Sedikit di luar topik, tetapi XOR adalah pengoptimalan, bukan overhead, bahkan dalam kasus nol-salinan. Ini memastikan bahwa semua struktur diinisialisasi nol, yang berarti Anda tidak perlu melakukan inisialisasi apa pun pada alokasi objek jika buffer sudah nol (yang sering kali akan tetap demikian). XOR terhadap konstanta waktu kompilasi mungkin berharga 1 siklus sedangkan segala jenis akses memori akan lebih mahal.

@lukewagner Adakah pemikiran tentang bagian "berbagi memori linier"?

Saya pikir ada kasus penggunaan untuk berbagi dan tidak berbagi memori linier dan pada akhirnya alat perlu mendukung keduanya:

Berbagi masuk akal di mana aplikasi asli saat ini akan menggunakan tautan statis atau dinamis hari ini: ketika semua kode yang digabungkan sepenuhnya dipercaya dan kombinasinya semuanya menggunakan rantai alat yang sama atau menggunakan ABI yang ditentukan secara ketat. Ini lebih merupakan model komposisi perangkat lunak yang lebih rapuh.

Tidak berbagi memori masuk akal untuk kumpulan modul yang lebih longgar, di mana desain gaya Unix klasik akan menempatkan kode ke dalam proses terpisah yang dihubungkan oleh pipa. Secara pribadi, saya pikir ini adalah arah yang lebih menarik/futuristik untuk ekosistem perangkat lunak yang lebih komposisional dan jadi saya menganjurkan ini menjadi default untuk setiap rantai alat yang bertujuan untuk berpartisipasi dalam ekosistem ESM/npm melalui integrasi ESM (dan memang itu adalah kasus hari ini dengan wasm-pack/wasm-bindgen Rust). Menggunakan mekanisme di sekitar Web IDL Bindings atau ekstensi yang Anda usulkan sangat masuk akal bagi saya sebagai bentuk RPC yang efisien, ergonomis, diketik (sinkron atau asinkron).

Setelah akhirnya membaca ini secara lengkap, kedengarannya sangat mirip dengan pemikiran saya di area ini (yang mana kotak komentar ini terlalu pendek untuk diisi?).

Secara khusus saya telah memikirkan masalah komunikasi antar-modul sebagai yang terbaik dijelaskan dengan skema. Artinya, kita tidak memerlukan format serialisasi Cap'nProto, kita cukup menggunakan skema. Saya tidak memiliki pendapat tentang bahasa skema Cap'nProto secara khusus saat ini.

Dari perspektif WASI/ESM+npm, solusi formulir ini paling masuk akal bagi saya. Ini adalah abstraksi atas ABI, tanpa bergantung pada ABI bersama. Ini pada dasarnya memungkinkan seseorang untuk menggambarkan antarmuka dengan API skema-lang, dan memanggil melintasi batas-batas bahasa ini dengan ABI yang tampak asli di kedua ujungnya, membiarkan host menangani terjemahan.

Secara khusus, ini tidak termasuk kasus penggunaan untuk memiliki lebih banyak koordinasi dengan modul lain: jika Anda tahu pasti bahwa Anda dapat berbagi ABI, Anda sebenarnya dapat menggunakan ABI, ABI apa pun, apakah itu C atau Haskell. Jika Anda mengontrol dan mengkompilasi semua wasm yang dimaksud, itu masalah yang jauh lebih mudah untuk dipecahkan. Hanya ketika Anda masuk ke kasus npm di mana Anda memuat kode yang tidak dikenal dan Anda tidak tahu bahasa sumbernya, sesuatu seperti interop tingkat skema antar modul menjadi sangat menarik. Karena kita dapat menggunakan LCD wasm itu sendiri - yang saya prediksi akan mengikuti alur yang mirip dengan perpustakaan asli, dan menggunakan C ABI - atau kita dapat menggunakan LCD bahasa, yang dikodekan dalam bahasa skema. Dan skema dapat lebih fleksibel dengan membuat persyaratan 2) persyaratan lunak, misalnya mungkin untuk mengkonversi dari C ke Rust ke Nim secara efisien, tetapi C ke Haskell memiliki lebih banyak overhead bukanlah dealbreaker.

Secara khusus saya telah memikirkan masalah komunikasi antar-modul sebagai yang terbaik dijelaskan dengan skema. Artinya, kita tidak membutuhkan format serialisasi [a], kita cukup menggunakan skema.

Saya cenderung setuju dengan yang pertama, tetapi saya tidak yakin yang terakhir mengikuti. Siapa yang mengimplementasikan skema? Bahkan jika Host melakukan pengangkutan, pada titik tertentu Anda harus menentukan nilai/byte Wasm apa yang sebenarnya dikonsumsi/diproduksi di kedua ujungnya, dan setiap modul harus membawa datanya sendiri ke dalam bentuk yang dipahami oleh host. Bahkan mungkin ada beberapa formulir yang tersedia, tetapi tetap saja itu tidak berbeda dari format serialisasi, hanya sedikit lebih tinggi.

itu harus mungkin untuk mengkonversi dari C ke Rust ke Nim secara efisien, C ke Haskell memiliki lebih banyak overhead bukanlah dealbreaker.

Mungkin tidak, tetapi Anda harus menyadari implikasinya. Mengistimewakan bahasa seperti C berarti Haskell tidak akan menggunakan abstraksi ini untuk modul Haskell, karena overhead yang diinduksi. Itu pada gilirannya berarti bahwa itu tidak akan berpartisipasi dalam ekosistem "npm" yang sama untuk perpustakaannya sendiri.

Dan "Haskell" di sini hanyalah pengganti untuk hampir semua bahasa tingkat tinggi. Sebagian besar bahasa tidak seperti C.

Saya tidak mengklaim memiliki solusi yang lebih baik, tetapi saya pikir kita harus tetap realistis tentang seberapa efisien dan menarik setiap ABI atau abstraksi skema tunggal untuk populasi umum bahasa, di luar gaya FFI biasa dari interoperabilitas satu arah. Secara khusus, saya tidak yakin bahwa ekosistem paket pan-linguistik adalah hasil yang terlalu realistis.

Mengistimewakan bahasa seperti C berarti Haskell tidak akan menggunakan abstraksi ini untuk modul Haskell, karena overhead yang diinduksi. Itu pada gilirannya berarti bahwa itu tidak akan berpartisipasi dalam ekosistem "npm" yang sama untuk perpustakaannya sendiri.

Dan "Haskell" di sini hanyalah pengganti untuk hampir semua bahasa tingkat tinggi. Sebagian besar bahasa tidak seperti C.

Bisakah memberikan beberapa kasus penggunaan khusus? Idealnya, perpustakaan yang ada di Haskell atau bahasa lain yang akan canggung untuk diterjemahkan ke dalam skema serialisasi?

Saya menduga sebagian besar akan turun ke perpustakaan utilitas vs perpustakaan bisnis. Misalnya wadah, algoritme pengurutan, dan utilitas lain yang mengandalkan bahasa generik tidak akan menerjemahkan dengan baik ke wasm, tetapi pengurai, widget gui, dan alat sistem file akan menerjemahkannya.

@PoignardAzur , tidak sulit untuk menerjemahkannya, tetapi mengharuskan mereka untuk menyalin (serialise/deserialise) semua argumen/hasil di kedua ujung setiap panggilan lintas-modul. Jelas, Anda tidak ingin membayar biaya itu untuk setiap panggilan perpustakaan internal bahasa.

Di Haskell secara khusus Anda juga memiliki masalah tambahan bahwa penyalinan tidak sesuai dengan semantik kemalasan. Dalam bahasa lain mungkin tidak kompatibel dengan data stateful.

Siapa yang mengimplementasikan skema? Bahkan jika Host melakukan pengangkutan, pada titik tertentu Anda harus menentukan nilai/byte Wasm apa yang sebenarnya dikonsumsi/diproduksi di kedua ujungnya, dan setiap modul harus membawa datanya sendiri ke dalam bentuk yang dipahami oleh host. Bahkan mungkin ada beberapa formulir yang tersedia, tetapi tetap saja itu tidak berbeda dari format serialisasi, hanya sedikit lebih tinggi.

Tuan rumah mengimplementasikan skema. Skema tidak menggambarkan byte sama sekali, dan memungkinkan itu menjadi detail implementasi. Ini meminjam dari desain proposal WebIDL Bindings, di mana bagian yang menarik adalah konversi dari struct C ke tipe WebIDL. Jenis desain ini menggunakan Wasm Abstract Interface Types (saya sarankan akronimnya: WAIT) alih-alih tipe WebIDL. Dalam proposal WebIDL, kami tidak perlu atau ingin mengamanatkan representasi biner data ketika telah "diterjemahkan ke WebIDL", karena kami ingin dapat langsung dari wasm ke API browser tanpa berhenti di antaranya.

Mengistimewakan bahasa seperti C berarti Haskell tidak akan menggunakan abstraksi ini untuk modul Haskell, karena overhead yang diinduksi.

Oh, setuju 100%. Saya seharusnya menyelesaikan contoh untuk membuatnya lebih jelas: Sementara itu, Haskell ke Elm ke C# bisa sama efisiennya (dengan asumsi mereka menggunakan tipe wasm gc), tetapi C# ke Rust mungkin memiliki overhead. Saya tidak berpikir ada cara untuk menghindari overhead ketika melompati paradigma bahasa.

Saya pikir pengamatan Anda benar bahwa kita perlu mencoba menghindari hak istimewa bahasa apa pun, karena jika kita gagal menjadi cukup ergonomis + berkinerja untuk bahasa tertentu, mereka tidak akan melihat banyak nilai dalam menggunakan antarmuka, dan dengan demikian tidak berpartisipasi dalam ekosistem .

Saya percaya bahwa dengan mengabstraksikan tipe dan tidak menentukan format kawat, kami dapat memberikan lebih banyak kelonggaran kepada host untuk dioptimalkan. Saya pikir non-tujuan adalah untuk mengatakan "string gaya-C efisien", tetapi itu adalah tujuan untuk mengatakan "bahasa yang [ingin] beralasan tentang string gaya-C dapat melakukannya secara efisien". Atau, tidak ada satu format pun yang harus diberkati, tetapi rantai panggilan tertentu yang kompatibel harus efisien, dan semua rantai panggilan harus dimungkinkan.

Dengan rantai panggilan yang saya maksud:

  1. C -> Karat -> Zig -> Fortran, efisien
  2. Haskell -> C# -> Haskell, efisien
  3. C -> Haskell -> Karat -> Skema, tidak efisien
  4. Java -> Karat, tidak efisien

Dan "Haskell" di sini hanyalah pengganti untuk hampir semua bahasa tingkat tinggi. Sebagian besar bahasa tidak seperti C.

Ya, itulah maksud saya di balik penggunaan Haskell sebagai bahasa konkret. (Meskipun Nim mungkin adalah contoh yang buruk dari bahasa seperti C karena menggunakan GC juga)

--

Cara lain yang saya pikirkan tentang tipe abstrak adalah sebagai IR. Dengan cara yang sama seperti LLVM menggambarkan hubungan banyak-ke-satu-ke-banyak (banyak bahasa -> satu IR -> banyak target), tipe abstrak wasm dapat memediasi pemetaan banyak-ke-banyak, dari bahasa+host -> bahasa + host. Sesuatu dalam ruang desain ini mengambil masalah pemetaan N^2 dan mengubahnya menjadi masalah N+N.

Tuan rumah mengimplementasikan skema.

Nah, itu tidak cukup, setiap modul harus mengimplementasikan sesuatu agar tuan rumah dapat menemukan datanya. Jika tuan rumah mengharapkan tata letak C maka Anda harus mendefinisikan tata letak C ini, dan setiap klien harus menyusun/membuka mars ke/dari itu secara internal. Itu tidak jauh berbeda dari format serialisasi.

Bahkan jika kita melakukannya, masih berguna untuk mendefinisikan format serialisasi, misalnya, untuk aplikasi yang perlu mentransfer data antar mesin tunggal, misalnya melalui jaringan atau persistensi berbasis file.

Nah, itu tidak cukup, setiap modul harus mengimplementasikan sesuatu agar tuan rumah dapat menemukan datanya. Jika tuan rumah mengharapkan tata letak C maka Anda harus menentukan tata letak C ini

Tuan rumah seharusnya tidak mengharapkan apa pun, tetapi perlu mendukung segalanya. Lebih konkretnya, dengan menggunakan proposal webidl-bindings sebagai contoh ilustrasi , kita memiliki utf8-cstr dan utf8-str , yang masing-masing mengambil i32 (ptr) dan i32 (ptr), i32 (len) . Tidak perlu mandat dalam spesifikasi "Host secara internal mewakili ini sebagai C-string" untuk dapat memetakan secara konkret di antara mereka.
Jadi, setiap modul mengimplementasikan sesuatu, ya, tetapi representasi data tidak perlu diekspresikan dalam lapisan data/skema abstrak, yang memberi kita properti abstrak di atas tata letak data tersebut.
Selain itu, ini dapat diperluas pada lapisan binding yang memetakan antara tipe beton dan tipe perantara abstrak. Untuk menambahkan dukungan Haskell (yang memodelkan string sebagai daftar kontra karakter dan array karakter), kita dapat menambahkan utf8-cons-str dan utf8-array-str binding, yang mengharapkan (dan memvalidasi) jenis wasm (menggunakan current sintaks proposal gc) (type $haskellString (struct (field i8) (field (ref $haskellString)))) dan (type $haskellText (array i8)) .

Artinya, setiap modul memutuskan bagaimana data berasal. Jenis abstrak + binding memungkinkan konversi antara bagaimana modul melihat data yang sama, tanpa memberkati representasi tunggal sebagai kanonik.

Format serialisasi untuk (sebagian dari) tipe abstrak akan berguna, tetapi dapat diimplementasikan sebagai konsumen dari format skema, dan saya yakin merupakan masalah ortogonal. FIDL Saya percaya memiliki format serialisasi untuk subset jenis yang dapat ditransfer di seluruh jaringan, melarang terwujudnya pegangan buram, sementara mengizinkan pegangan buram untuk ditransfer dalam suatu sistem (IPC ya, RPC tidak).

Apa yang Anda gambarkan cukup dekat dengan apa yang ada dalam pikiran saya, dengan satu peringatan besar: skema harus memiliki sejumlah kecil representasi yang mungkin. Menjembatani antara representasi yang berbeda adalah masalah N*N, yang berarti jumlah representasi harus dijaga tetap kecil untuk menghindari beban penulis VM yang berlebihan.

Jadi menambahkan dukungan Haskell akan membutuhkan penggunaan binding yang ada, bukan menambahkan binding khusus.

Beberapa kemungkinan representasi:

  • C-style struct dan pointer.
  • Byte capnproto sebenarnya.
  • kelas GC.
  • Penutupan berfungsi sebagai pengambil dan penyetel.
  • Kamus bergaya Python.

Idenya adalah bahwa meskipun setiap bahasa berbeda, dan ada beberapa outlier ekstrim, Anda dapat memasukkan sejumlah besar bahasa ke dalam jumlah kategori yang cukup kecil.

Jadi menambahkan dukungan Haskell akan membutuhkan penggunaan binding yang ada, bukan menambahkan binding khusus.

Tergantung pada tingkat granularitas binding yang ada yang Anda pikirkan. N<->N bahasa yang menyandikan setiap kemungkinan pengikatan adalah 2*N*N, tetapi N<->IR adalah 2*N, dan selanjutnya jika Anda mengatakan N<->[gaya pengikatan umum]<->IR, di mana nomornya format umum adalah k, Anda berbicara 2*k, di mana k <N.

Secara khusus, dengan skema yang saya jelaskan, Anda mendapatkan Skema secara gratis (itu akan menggunakan kembali utf8-cons-str ). Jika Java juga memodelkan string sebagai array char, itu adalah pengikatan utf8-array-str . Jika Nim menggunakan string_views di bawah tenda, utf8-str . Jika Zig sesuai dengan C ABI, utf8-cstr . (Saya tidak tahu ABI Java/Nim/Zig, jadi saya tidak menyebutkannya sebagai contoh konkret sebelumnya)

Jadi, ya, kami tidak ingin menambahkan pengikatan untuk setiap bahasa yang memungkinkan, tetapi kami dapat menambahkan beberapa pengikatan per jenis IR untuk mencakup sebagian besar bahasa. Saya pikir ruang untuk ketidaksepakatan di sini adalah, berapa banyak binding adalah "beberapa", apa sweet spot, seberapa ketat kriteria untuk apakah kita mendukung ABI suatu bahasa?
Saya tidak punya jawaban spesifik untuk pertanyaan-pertanyaan ini. Saya mencoba memberikan banyak contoh konkret untuk menggambarkan ruang desain dengan lebih baik.

Saya juga akan menegaskan bahwa kami benar-benar ingin menentukan beberapa ikatan per tipe abstrak, untuk menghindari hak istimewa satu gaya data. Jika satu-satunya pengikatan yang kami ekspos ke Strings adalah utf8-cstr , maka semua bahasa yang tidak memiliki C-ABI harus berurusan dengan ketidakcocokan itu. Saya setuju dengan peningkatan kompleksitas penulisan VM beberapa faktor yang tidak kecil.
Pekerjaan total dalam ekosistem adalah O(upaya VM + upaya implementasi bahasa), dan kedua istilah tersebut berskala dalam beberapa cara dengan N=jumlah bahasa. Misalkan M=jumlah embedder, k=jumlah binding, dan a=jumlah rata-rata binding yang perlu diimplementasikan oleh bahasa tertentu, dengan a<=k. Minimal kami memiliki M+N implementasi wasm yang terpisah.
Pendekatan naif, dengan setiap bahasa N secara independen mengimplementasikan ABI FFI dengan setiap bahasa N lainnya, adalah O(M + N*N). Inilah yang kami miliki pada sistem asli, yang merupakan sinyal kuat bahwa setiap O(N*N) akan menghasilkan hasil yang tidak berbeda dari sistem asli.
Pendekatan naif kedua, di mana setiap VM perlu mengimplementasikan semua ikatan N*N: O(M*N*N + N), yang jelas lebih buruk.
Apa yang kami coba usulkan adalah bahwa kami memiliki k binding yang memetakan antara bahasa abstrak, yang memetakan kembali ke semua bahasa. Ini berarti k bekerja untuk setiap VM. Untuk setiap bahasa, kita hanya perlu mengimplementasikan subset dari binding. Usaha totalnya adalah M*k + N*a, yaitu O(M*k + N*k). Perhatikan bahwa jika k=N sisi VM adalah "hanya" M*N, jadi untuk VM apa pun itu "hanya" linier dalam jumlah bahasa. Jelas meskipun kita menginginkan k << N, karena jika tidak, ini masih O(N*N), tidak lebih baik dari solusi pertama.
Namun, O(M*k + N*k) jauh lebih enak. Jika k adalah O(1), itu membuat seluruh ekosistem linier dalam jumlah implementasi, yang merupakan batas bawah kami pada jumlah upaya yang terlibat. Ikatan yang lebih mungkin adalah k menjadi O(log(N)), yang saya masih cukup puas.

Yang merupakan cara yang panjang untuk mengatakan, saya sepenuhnya setuju dengan meningkatkan kompleksitas VM untuk fitur ini dengan beberapa faktor konstan.

kita dapat menambahkan beberapa binding per jenis IR untuk mencakup sebagian besar bahasa.

Ini adalah asumsi mendasar yang penting yang saya yakini tidak benar. Pengalaman saya adalah bahwa ada (setidaknya!) sebanyak pilihan representasi karena ada implementasi bahasa. Dan mereka bisa menjadi rumit secara sewenang-wenang.

Ambil V8, yang sendiri memiliki beberapa lusin (!) representasi untuk string, termasuk pengkodean yang berbeda, tali heterogen, dll.

Kasus Haskell jauh lebih rumit daripada yang Anda gambarkan, karena daftar di Haskell malas, yang berarti bahwa untuk setiap karakter dalam string Anda mungkin perlu memanggil thunk.

Bahasa lain menggunakan representasi lucu untuk panjang string, atau tidak menyimpannya secara eksplisit tetapi mengharuskannya untuk dihitung.

Kedua contoh ini sudah menunjukkan bahwa tata letak data deklaratif tidak memotongnya, Anda sering kali harus dapat memanggil kode runtime, yang pada gilirannya mungkin memiliki konvensi pemanggilannya sendiri.

Dan itu hanya string, yang merupakan tipe data yang cukup sederhana secara konseptual. Saya bahkan tidak ingin memikirkan jumlah cara yang tak terbatas di mana bahasa mewakili jenis produk (tupel/struktur/objek).

Dan kemudian ada sisi penerima, di mana Anda harus dapat membuat semua struktur data ini!

Jadi saya pikir sama sekali tidak realistis bahwa kita akan pernah hampir mendukung "sebagian besar bahasa". Sebagai gantinya, kami akan mulai memberikan hak istimewa kepada beberapa orang, sementara kami telah menumbuhkan kebun binatang besar yang berisi barang-barang sewenang-wenang. Itu tampaknya fatal di berbagai tingkatan.

Pengalaman saya adalah bahwa ada (setidaknya!) sebanyak pilihan representasi karena ada implementasi bahasa. Dan mereka bisa menjadi rumit secara sewenang-wenang.

Saya sangat setuju. Saya pikir mencoba merancang jenis yang entah bagaimana akan mencakup sebagian besar representasi data internal bahasa sama sekali tidak dapat dilacak, dan akan membuat ekosistem menjadi terlalu rumit.

Pada akhirnya hanya ada satu denominator umum terendah antar bahasa dalam hal data: yaitu "buffer". Semua bahasa dapat membaca dan membangun ini. Mereka efisien dan sederhana. Ya, mereka menyukai bahasa yang secara langsung dapat menangani konten mereka, tetapi saya tidak berpikir itu adalah ketidaksetaraan yang diselesaikan dengan mempromosikan sel kontra (malas) ke tingkat dukungan yang sama entah bagaimana.

Bahkan, Anda bisa mencapai sangat jauh hanya dengan satu tipe data: pasangan pointer + len. Maka Anda hanya perlu "skema" yang mengatakan apa yang ada di byte itu. Apakah itu berjanji untuk mematuhi UTF-8? Apakah byte terakhir dijamin selalu 0? Apakah bidang panjang/kapasitas 4/8 byte pertama? Apakah semua byte ini merupakan float endian kecil yang dapat dikirim langsung ke WebGL? Apakah byte ini mungkin skema format serialisasi yang ada X? dll?

Saya akan mengusulkan spesifikasi skema yang sangat sederhana yang dapat menjawab semua pertanyaan ini (bukan format serialisasi yang ada, tetapi sesuatu yang lebih rendah, lebih sederhana, dan khusus untuk wasm). Ini kemudian menjadi beban setiap bahasa untuk membaca dan menulis buffer ini secara efisien dalam format yang ditentukan. Lapisan di antara kemudian dapat melewati buffer secara membabi buta tanpa memproses, baik dengan menyalin, atau jika memungkinkan, dengan referensi/tampilan.

Ini adalah asumsi mendasar yang penting yang saya yakini tidak benar. Pengalaman saya adalah bahwa ada (setidaknya!) sebanyak pilihan representasi karena ada implementasi bahasa. Dan mereka bisa menjadi rumit secara sewenang-wenang.

Saya setuju bahwa ini adalah asumsi mendasar yang penting. Saya tidak setuju bahwa itu tidak benar, meskipun saya pikir karena nuansa semantik yang menurut saya belum saya jelaskan.

Ikatan tidak dimaksudkan untuk memetakan semua representasi bahasa dengan sempurna , mereka hanya perlu memetakan ke semua bahasa dengan cukup baik .

Itu adalah asumsi mendasar yang penting yang membuat ini sama sekali dapat ditundukkan, sama sekali, terlepas dari pengkodean. Usulan @aardappel untuk pergi ke arah lain dan benar-benar mengubah byte menjadi buffer yang dapat didekodekan juga dibangun dengan asumsi bahwa itu adalah pengkodean lossy dari semantik program apa pun, beberapa lebih lossy daripada yang lain.

Kasus Haskell jauh lebih rumit daripada yang Anda gambarkan, karena daftar di Haskell malas, yang berarti bahwa untuk setiap karakter dalam string Anda mungkin perlu memanggil thunk.

Sebenarnya aku sudah melupakannya, tapi kurasa itu tidak penting. Tujuannya bukan untuk merepresentasikan Haskell Strings sambil mempertahankan semua nuansa semantiknya melintasi batas modul. Tujuannya adalah untuk mengonversi Haskell String ke IR String, berdasarkan nilai. Ini tentu melibatkan komputasi seluruh string.

Kedua contoh ini sudah menunjukkan bahwa tata letak data deklaratif tidak memotongnya, Anda sering kali harus dapat memanggil kode runtime, yang pada gilirannya mungkin memiliki konvensi pemanggilannya sendiri.

Cara untuk memodelkannya, terlepas dari bagaimana kita menentukan binding (atau bahkan JIKA kita menentukan sesuatu untuk binding), adalah dengan menanganinya di userland. Jika representasi bahasa dari suatu tipe tidak memetakan ke pengikatan secara langsung, itu perlu dikonversi ke representasi yang melakukannya. Misalnya jika Haskell's Strings benar-benar direpresentasikan sebagai (type $haskellString (struct (field i8) (field (func (result (ref $haskellString)))))) , itu akan perlu dikonversi ke string ketat dan menggunakan pengikatan seperti Skema, atau ke array Teks dan menggunakan pengikatan seperti Java, atau ke CFIString dan gunakan pengikatan seperti C. Proposisi nilai memiliki beberapa jenis pengikatan tidak sempurna adalah bahwa beberapa di antaranya kurang canggung untuk Haskell daripada yang lain, dan dimungkinkan untuk membuat jenis Wasm-FFI tanpa perlu memodifikasi kompiler.

Dan itu hanya string, yang merupakan tipe data yang cukup sederhana secara konseptual. Saya bahkan tidak ingin memikirkan jumlah cara yang tak terbatas di mana bahasa mewakili jenis produk (tupel/struktur/objek).
Dan kemudian ada sisi penerima, di mana Anda harus dapat membuat semua struktur data ini!

Saya bingung, saya melihat bahwa mengatakan "mengikat antar bahasa sama sekali tidak mungkin jadi kita tidak boleh mencoba sama sekali," tetapi saya percaya apa yang Anda katakan lebih mirip dengan "Saya tidak percaya pendekatan yang dijelaskan di sini adalah penurut," yang tampaknya jauh lebih masuk akal. Secara khusus, keberatan saya terhadap argumen ini adalah bahwa argumen itu tidak menggambarkan jalan ke depan. Mengingat bahwa masalah ini "sangat sulit", apa yang kita lakukan?

Sebagai gantinya, kami akan mulai memberikan hak istimewa kepada beberapa orang

Hampir-pasti. Pertanyaannya adalah salah satu, sejauh mana beberapa bahasa yang didukung secara optimal diistimewakan? Berapa banyak kelonggaran yang dimiliki bahasa yang kurang mampu dalam menemukan solusi ergonomis?

sementara sudah menumbuhkan kebun binatang besar barang sewenang-wenang.

Saya tidak yakin apa yang Anda maksud dengan itu. Interpretasi saya tentang apa yang sewenang-wenang adalah "bahasa mana yang kami dukung", tetapi itu sama dengan "mengistimewakan beberapa", yang akan dihitung ganda. Dan dengan demikian ini hanya akan cacat fatal pada satu tingkat itu, daripada beberapa: D

@aardappel versi singkatnya adalah itu rencana cadangan saya jika pendekatan abstrak deklaratif gagal: pergi ke arah yang benar-benar berlawanan dan jelaskan format serialisasi. Pengamatan dibuat bahwa Web itu sendiri dibangun hampir seluruhnya di atas Teks, karena merupakan common denominator yang sangat rendah. Teks dapat dimengerti dengan mudah oleh semua alat, sehingga sesuatu seperti Web dimungkinkan di atasnya.

Kekhawatiran terbesar yang saya miliki dengan data-in-buffer yang mungkin membuat pendekatan itu sulit dilakukan adalah bagaimana kita bisa menangani tipe referensi? Ide terbaik yang saya miliki adalah berbagi tabel dan membuat serial indeks ke dalamnya, tetapi saya tidak memiliki gambaran lengkap tentang seberapa baik itu benar-benar berfungsi.

@jgravelle-google mungkin tipe referensi harus dipisah? Jadi tanda tangan mentah fungsi yang diberikan mungkin ref ref i32 i32 i32 i32 yang sebenarnya adalah 2 anyrefs diikuti oleh 2 buffer dari tipe tertentu (ditentukan dalam skema hipotetis di atas).

(Sebagai samping, saya tidak terbiasa dengan Haskell, tetapi gagasan string sebagai daftar malas chars meniup pikiran saya. Ketika dihubungkan daftar byte pernah cara yang paling efisien atau nyaman untuk melakukan sesuatu? Saya mendapatkan bahwa Haskell perlu segalanya untuk tidak dapat diubah, dan daftar tertaut itu memungkinkan prepending yang murah, tetapi Anda bisa mendapatkan dan memanipulasi string yang tidak dapat diubah tanpa menggunakan daftar tertaut.)

Kekhawatiran terbesar yang saya miliki dengan data-in-buffer yang mungkin membuat pendekatan itu sulit dilakukan adalah bagaimana kita bisa menangani tipe referensi? Ide terbaik yang saya miliki adalah berbagi tabel dan membuat serial indeks ke dalamnya, tetapi saya tidak memiliki gambaran lengkap tentang seberapa baik itu benar-benar berfungsi.

Itulah salah satu alasan saya mengusulkan capnproto sebagai penyandian. Tabel referensi kurang lebih sudah terpasang.

Bagaimanapun, kami ingin format apa pun yang kami pilih memiliki tipe referensi sebagai warga negara kelas satu, yang dapat ditempatkan di mana saja dalam grafik data. (misalnya dalam opsional, array, varian, dll)

Terima kasih semuanya atas tanggapan Anda.

Saya pikir kita mulai mencapai titik di mana kita kebanyakan mengulang kembali argumen yang sama dengan variasi yang sangat sedikit, jadi saya akan memperbarui proposal dan mencoba mengatasi kekhawatiran semua orang. Saya mungkin akan memulai lagi dengan masalah baru setelah saya selesai menulis proposal yang diperbarui.

Untuk meringkas umpan balik sejauh ini:

  • Ada sedikit konsensus tentang format serialisasi apa, jika ada, yang terbaik untuk wasm. Alternatifnya termasuk FlatBuffers, grafik struct C ABI dengan pointer mentah, format IDL wasm yang dibuat khusus, atau beberapa kombinasi di atas.

  • Proposal membutuhkan ruang negatif yang lebih kuat. Banyak pembaca yang bingung dengan ruang lingkup proposal, dan kasus penggunaan mana yang dimaksudkan untuk memfasilitasi (penautan statis vs dinamis, modul-ke-Host vs Host-to-Host, berbagi data yang dapat diubah vs meneruskan pesan yang tidak dapat diubah).

  • @lukewagner telah menyatakan beberapa antusiasme untuk potensi sistem modul yang menghubungkan modul yang saling tidak percaya, ketika dikombinasikan dengan integrasi ESM. Iterasi proposal berikutnya harus memperluas potensi itu; khususnya, saya percaya bahwa memiliki sistem tipe yang kompatibel ke belakang akan memungkinkan wasm untuk menggunakan model pohon ketergantungan seperti npm, sambil menghindari beban masalah berlian ketergantungan.

  • Ada sedikit umpan balik tentang kemampuan, yaitu nilai buram yang dapat dikembalikan dan diteruskan, tetapi tidak dibuat dari data mentah. Saya menganggapnya sebagai tanda bahwa iterasi berikutnya harus lebih menekankan pada mereka.

  • Beberapa pembaca menyatakan keprihatinan tentang kelayakan sistem tipe antar bahasa. Kekhawatiran ini agak kabur dan sulit untuk didefinisikan, sebagian karena materi pelajarannya sangat abstrak, sebagian karena proposal itu sendiri sejauh ini cukup kabur, yang menggemakan masalah ayam-dan-telur

    • Berfokus terlalu banyak pada bahasa yang sangat terlihat, meninggalkan lebih banyak bahasa khusus.
    • Memiliki abstraksi bocor yang mencoba menjadi terlalu umum, tetapi tidak mencakup kasus penggunaan siapa pun dengan nyaman atau efisien.
    • Mencakup terlalu banyak kasus, membuat implementasi N*N membengkak yang masih mengalami masalah di atas.

Iterasi proposal berikutnya perlu mengatasi masalah ini, dengan satu atau lain cara.

Secara khusus, saya pikir diskusi akan mendapat banyak manfaat dari beberapa contoh strawman untuk alasan sekitar. Contoh-contoh ini akan mencakup setidaknya dua perpustakaan, ditulis dalam bahasa yang berbeda dengan tata letak data yang berbeda (misalnya C++ dan Java), yang dikonsumsi oleh setidaknya dua program minimal dalam bahasa yang berbeda (misalnya Rust dan Python), untuk menggambarkan masalah n*n dan strategi untuk mengatasinya.

Juga, seperti yang telah ditunjukkan oleh pembaca, proposal saat ini melibatkan gagasan skema tipe dengan gagasan representasinya. Sementara proposal melakukan pekerjaan yang adil dalam menyusun persyaratan format representasi, proposal perlu menetapkan persyaratan skema tipe abstrak terlebih dahulu.

Bagaimanapun, terima kasih sekali lagi untuk semua orang yang berpartisipasi dalam diskusi ini sejauh ini. Saya akan mencoba untuk membuat proposal yang lebih menyeluruh sesegera mungkin. Jika ada orang di sini yang tertarik untuk membantu saya menulisnya, pastikan untuk mengirim email kepada saya!

@jgravelle-google:

Cara untuk memodelkannya, terlepas dari bagaimana kita menentukan binding (atau bahkan JIKA kita menentukan sesuatu untuk binding), adalah dengan menanganinya di userland.

Ya, saya setuju, dan argumen saya mirip dengan @aardappel 's: jika itu yang umumnya harus kita lakukan maka kita harus menerimanya dan tidak mencoba hal-hal ad-hoc untuk memperbaiki beberapa kasus aneh. Userland adalah tempat konversi berada, dalam semangat segala sesuatu yang lain di Wasm.

Saya bingung, saya melihat bahwa mengatakan "mengikat antar bahasa sama sekali tidak mungkin jadi kita tidak boleh mencoba sama sekali,"

Saya pikir itu benar-benar diinginkan untuk mendefinisikan DDL (skema tipe) untuk interop data antar bahasa. Saya hanya tidak berpikir itu mudah untuk membangun konversi ke Wasm. Konversi perlu diterapkan di userland. Lapisan pengikat hanya mengatur format yang harus dihasilkan/dikonsumsi oleh kode pengguna.

sementara sudah menumbuhkan kebun binatang besar barang sewenang-wenang.
Saya tidak yakin apa yang Anda maksud dengan itu. Interpretasi saya tentang apa yang sewenang-wenang adalah "bahasa mana yang kami dukung", tetapi itu sama dengan "mengistimewakan beberapa", yang akan dihitung ganda.

Maaf, maksud saya saya menduga bahwa tidak akan ada sesuatu yang sangat kanonik tentang konversi ini. Jadi kedua pilihan mereka "sewenang-wenang" dan semantik masing-masing.

bagaimana kita bisa menangani tipe referensi?

Ah, itu pertanyaan yang bagus. FWIW, kami mencoba untuk mengatasi masalah ini sekarang juga untuk IDL/DDL untuk platform Dfinity. Selama hanya ada anyref, solusinya cukup sederhana: format serialisasi mendefinisikan dua bagian, irisan memori memproyeksikan data transparan dan irisan tabel memproyeksikan referensi yang terkandung. Beberapa jenis referensi memerlukan beberapa irisan tabel yang sesuai. Pertanyaan rumitnya adalah apa yang harus dilakukan setelah kumpulan tipe ref tidak lagi terbatas (misalnya dengan referensi fungsi yang diketik).

Setelah kita memiliki tipe GC harus ada cara alternatif untuk memasok data, yaitu sebagai nilai GC. Dalam hal ini referensi tidak menjadi masalah, karena mereka dapat dicampur dengan bebas.

@PoignardAzur :

Selain itu, saya tidak terbiasa dengan Haskell, tetapi gagasan tentang string sebagai daftar karakter yang malas membuat saya berpikir.

Ya, saya percaya itu secara luas dianggap sebagai kesalahan saat ini. Tetapi ini menunjukkan betapa banyak keragaman yang ada bahkan untuk tipe data "sederhana".

@rossberg

Saya pikir itu benar-benar diinginkan untuk mendefinisikan DDL (skema tipe) untuk interop data antar bahasa. Saya hanya tidak berpikir itu mudah untuk membangun konversi ke Wasm. Konversi perlu diterapkan di userland.

Saya setuju, dan untuk menambahkan itu: Saya skeptis tentang menambahkan sesuatu ke spesifikasi wasm untuk ini karena saya tidak berpikir wasm memiliki kebutuhan yang lebih besar untuk solusi antar-bahasa daripada platform lain, dan saya tidak berpikir wasm memiliki kemampuan yang lebih besar untuk mengimplementasikan solusi seperti itu daripada platform lain. Jelas tidak ada yang istimewa bagi saya tentang wasm di sini, jadi saya tidak yakin mengapa kita bisa melakukan yang lebih baik daripada solusi standar di area ini, misalnya buffer seperti yang disebutkan @aardappel . (Tapi saya pikir eksperimen di ruang pengguna sangat menarik, seperti di semua platform!)

Satu hal khusus yang dimiliki wasm, setidaknya di Web, adalah jenis JavaScript/Web API untuk string dan array dan sebagainya. Mampu berinteraksi dengan mereka jelas penting.

Saya tidak berpikir wasm memiliki kebutuhan yang lebih besar untuk solusi antar bahasa daripada platform lain

Saya pikir itu tidak. Digunakan di web secara default berarti kode dapat dan akan dijalankan dalam konteks yang berbeda. Dengan cara yang sama yang mungkin <script src="http://some.other.site/jquery.js"> , saya akan senang melihat orang-orang menggabungkan perpustakaan wasm dengan cara lintas-Asal. Karena sifat ephemerality dan composability yang disediakan web, nilai tambah untuk dapat berinteraksi dengan modul asing lebih tinggi daripada yang pernah ada di sistem asli.

dan saya tidak berpikir wasm memiliki kemampuan yang lebih besar untuk mengimplementasikan solusi seperti itu daripada platform lain.

Dan saya pikir itu benar. Karena wasm dijalankan oleh embedder / di host, pembuatan kode secara efektif diabstraksikan. Karena itu, VM memiliki lebih banyak alat + kelonggaran untuk mendukung konstruksi tingkat tinggi yang tidak mungkin dilakukan pada sistem asli.

Jadi saya pikir sesuatu di ruang ini lebih berharga dan lebih mungkin daripada di sistem lain, jadi itulah mengapa wasm istimewa dalam konteks ini. Bagi saya interop JS adalah kasus khusus dari gagasan yang lebih umum bahwa modul wasm harus dapat berbicara dengan hal-hal eksternal dengan pandangan dunia yang sangat berbeda.

Jalan ke depan untuk ini adalah mendorong ini sepenuhnya ke interop tingkat alat untuk saat ini, dan menunda standarisasi hingga kami memiliki format pemenang. Jadi jika tujuannya adalah untuk memiliki ekosistem pengelola paket wasm yang dominan menggunakan format antarmuka yang diberikan, (dan apakah itu NPM atau WAPM atau pengelola paket yang belum dibuat?) maka itu dapat terjadi terlepas dari standarisasi. Secara teori kita dapat menstandardisasi apa yang sudah dilakukan orang, untuk mendapatkan kinerja yang lebih baik, tetapi ergonomi dapat diterapkan di pengguna. Ada risiko bahwa format antarbahasa yang menang tidak cocok untuk pengoptimalan, dan kami berakhir dengan standar de facto yang kurang optimal. Jika kita dapat mendesain format dengan tujuan menstandarisasi nanti (gaya deklaratif di bagian kustom sebagian besar sudah cukup?) yang menghilangkan risiko itu, tetapi juga menunda peningkatan kinerja apa pun. Bagi saya kinerja adalah salah satu motivator yang kurang menarik untuk memiliki hal semacam ini jadi saya cukup setuju dengan itu, meskipun orang lain mungkin tidak setuju.

(dan apakah itu NPM atau WAPM atau pengelola paket yang belum dibuat?)

Saya pikir itu terlalu dini bagi WAPM untuk menjadi manajer paket yang layak. Kami membutuhkan fitur seperti integrasi ESM, WASI dan beberapa bentuk ikatan antar bahasa untuk distandarisasi sebelum pengelola paket wasm menjadi layak.

Karena itu, saya tidak berpikir WAPM bahkan memiliki manajemen ketergantungan.

Saya tidak berpikir wasm memiliki kebutuhan yang lebih besar untuk solusi antar bahasa daripada platform lain

Saya pikir itu tidak. Digunakan di web secara default berarti kode dapat dan akan dijalankan dalam konteks yang berbeda. Dengan cara yang sama yang mungkin terjadi

Konversi perlu diterapkan di userland. Lapisan pengikat hanya mengatur format yang harus dihasilkan/dikonsumsi oleh kode pengguna.

Itu ringkasan yang bagus untuk saya.

Saat saya sedang menulis draf baru, saya memiliki pertanyaan terbuka untuk semua orang di utas ini:

Apakah ada perpustakaan yang ada, yang jika dikompilasi ke WebAssembly, Anda ingin dapat menggunakannya dari bahasa apa pun?

Saya pada dasarnya mencari kasus penggunaan potensial untuk mendasarkan desain. Saya memiliki sendiri (khususnya, React, mesin Bullet, dan sistem plugin), tetapi saya ingin lebih banyak contoh untuk dikerjakan.

@PoignardAzur Banyak bahasa di C menggunakan perpustakaan Perl-Compatible Regular Expression (PCRE) yang sama, tetapi dalam penyematan browser mereka mungkin harus menggunakan API Regex JS.

@PoignardAzur BoringSSL dan libsodium muncul di pikiran.

Juga implementasi RPC Cap'n Proto, tetapi ini aneh: lapisan _serialisasi_ Cap'n Proto secara realistis harus diimplementasikan secara independen di setiap bahasa karena sebagian besar adalah lapisan API yang lebar tetapi dangkal yang perlu idiomatis dan sebaris -ramah. Lapisan RPC, OTOH, sempit tapi dalam. Pada prinsipnya harus dimungkinkan untuk menggunakan implementasi C++ RPC di belakang implementasi serialisasi bahasa arbitrer apa pun dengan meneruskan referensi array byte yang disandikan capnp melalui batas FFI ...

Saya pikir melakukan apa yang diusulkan pada akhirnya akan memerlukan beberapa perubahan yang cukup invasif pada Majelis Web itu sendiri seperti yang sudah ada - tetapi mungkin bisa dibilang sepadan.

Saya akan mencatat bahwa dunia SmallTalk memiliki beberapa pengalaman positif dengan upaya semacam itu yang dapat menjadi informasi dalam pengembangan State Replication Protocol (SRP) mereka yang merupakan protokol serialisasi efisien yang dapat mewakili semua jenis ukuran apa pun dengan lebih efisien. Saya telah mempertimbangkan untuk menjadikannya tata letak memori asli untuk VM atau bahkan FPGA tetapi belum sempat mencobanya. Saya tahu bahwa itu porting ke setidaknya satu bahasa lain, Squeak, dengan hasil yang baik. Tentu saja sesuatu untuk dibaca karena memiliki tumpang tindih yang kuat dengan masalah, tantangan, dan pengalaman proposal ini.

Saya mengerti mengapa Web IDL adalah proposal default sebagai bahasa pengikat: Ini adalah bahasa pengikat historis dan entah bagaimana matang untuk Web. Saya sangat mendukung keputusan itu, dan kemungkinan besar saya akan melakukan hal yang sama. Meskipun demikian, kami mungkin menyadari bahwa itu hampir tidak cocok dengan konteks lain (mengerti, host/bahasa lain). Wasm dirancang untuk menjadi host-agnostik, atau bahasa/platform-agnostik. Saya menyukai gagasan untuk menggunakan teknologi Web yang matang dan untuk menemukan kasus penggunaan untuk skenario non-Web, tetapi dalam kasus IDL Web, tampaknya sangat terkait dengan Web. Itulah alasan mengapa saya sangat mengikuti percakapan di sini.

Saya telah membuka https://github.com/WebAssembly/webidl-bindings/issues/40 , yang membuat saya mengajukan pertanyaan di sini karena saya belum pernah melihatnya (atau saya melewatkannya).

Dalam keseluruhan cerita yang mengikat, tidak jelas "siapa" yang bertanggung jawab untuk membuat ikatan:

  • Apakah itu kompiler (yang mengubah program menjadi modul Wasm)?
  • Apakah itu pembuat program (dan, apakah binding ditulis tangan)?

Saya pikir keduanya sah. Dan dalam kasus IDL Web, tampaknya menunjukkan beberapa batasan (lihat tautan di atas). Mungkin saya baru saja melewatkan langkah penting dalam prosesnya, jadi, pertimbangkan untuk melupakan pesan saya.

Bahkan jika tujuannya adalah untuk "memfokuskan kembali" Web IDL menjadi kurang Web-sentris, saat ini, _is_ sangat Web-sentris. Dan proposal muncul untuk mengusulkan alternatif, maka utas ini. Oleh karena itu, saya khawatir dengan potensi fragmentasi. Idealnya (dan ini adalah bagaimana Wasm dirancang sejauh ini), mengingat modul Wasm termasuk binding-nya, dimungkinkan untuk menjalankannya di mana saja apa adanya. Dengan binding yang ditulis dalam Web IDL, Cap'n' Proto, FlatBuffers, apa pun, saya cukup yakin tidak semua kompiler atau penulis program akan menulis binding yang sama dalam sintaks yang berbeda untuk benar-benar lintas platform. Lucunya, ini adalah argumen yang mendukung binding tulisan tangan: Orang dapat berkontribusi pada program dengan menulis binding untuk platform P. Tapi mari kita akui ini tidak ideal sama sekali.

Jadi kesimpulannya: Saya khawatir dengan kemungkinan fragmentasi antara ikatan Web dan non-Web. Jika bahasa yang mengikat non-Web dipegang, apakah itu akan diterapkan secara realistis oleh browser Web? Mereka harus menulis binding “Wasm binding language B Web IDL”. Perhatikan bahwa ini adalah skenario yang sama untuk semua host: Wasm bahasa pengikat B host API.

Bagi mereka yang penasaran, saya bekerja di Wasmer dan saya adalah penulis dari integrasi PHP- , Python- , Ruby- dan Go- Wasm. Kami mulai memiliki taman bermain yang bagus untuk meretas berbagai binding untuk host yang sangat berbeda. Jika ada yang ingin saya mengintegrasikan solusi yang berbeda, untuk mengumpulkan masukan, atau mencoba bereksperimen, kita semua terbuka dan siap untuk berkolaborasi dan memberikan lebih banyak sumber daya untuk itu.

Arah saat ini di 'binding webIDL' mungkin jauh dari webIDL
diri. Namun, dilemanya adalah ini:

Bahasa 'alami' untuk mengekspresikan antar-modul dan modul-host
interoperabilitas secara signifikan lebih kaya daripada bahasa alami WASM. Ini
berarti bahwa setiap setara IDL yang dapat digunakan akan terlihat cukup sewenang-wenang dari
POV WASM

Di sisi lain, untuk orang-orang yang melihat dunia dari lensa C/C++
(dan Rust dan sejenisnya) sesuatu yang lebih kaya dari model WASM berisiko menjadi
tidak dapat digunakan. Kita sudah bisa melihat ini dengan kesulitan mengintegrasikan ref
jenis ke dalam rantai alat.

Selain itu, WASM bukanlah mandat langsung untuk mendukung jenderal
interoperabilitas antar bahasa. Juga tidak seharusnya IMO.

(Ada versi interoperabilitas antar bahasa yang lebih terbatas yang saya
percaya tidak hanya didukung tetapi juga penting: itu ada di semua kami
kepentingan yang dapat dipenuhi oleh penyedia kemampuan dan pengguna kemampuan
dengan gesekan minimum. (Kemampuan berbicara manajemen tetapi saya punya
tidak menemukan istilah yang lebih baik.) Ini membutuhkan gaya IDL yang berbeda dan satu
yang lebih mudah diimplementasikan daripada yang dibutuhkan untuk antar-bahasa penuh
interop.)

Intinya: ada kasus untuk memiliki setara IDL, dan kami membutuhkannya
untuk mendukung interoperasi lintas batas kepemilikan. Apa akhirnya?
keberadaannya tidak jelas saat ini.

Pada hari Senin, 24 Jun 2019 pukul 07.02 Ivan Enderlin [email protected]
menulis:

Saya mengerti mengapa Web IDL adalah proposal default sebagai bahasa yang mengikat:
Ini adalah bahasa pengikat sejarah dan entah bagaimana matang untuk Web. Saya
sangat mendukung keputusan itu, dan kemungkinan besar saya akan membuat
sama. Meskipun demikian, kita mungkin menyadari bahwa itu hampir tidak cocok dengan konteks lain
(mengerti, host/bahasa lain). Wasm dirancang untuk menjadi tuan rumah-agnostik,
atau bahasa/platform-agnostik. Saya suka ide menggunakan Web dewasa
teknologi dan untuk menemukan usecase untuk skenario non-Web, tetapi dalam kasus
dari Web IDL, tampaknya sangat terkait dengan Web. Itulah alasan saya sangat
mengikuti percakapan itu di sini.

Saya telah membuka WebAssembly/webidl-bindings#40
https://github.com/WebAssembly/webidl-bindings/issues/40 , yang menuntun saya
untuk mengajukan pertanyaan di sini karena saya tidak pernah melihatnya (atau saya melewatkannya).

Dalam keseluruhan cerita yang mengikat, tidak jelas “siapa” yang bertanggung jawab
menghasilkan binding:

  • Apakah itu kompiler (yang mengubah program menjadi modul Wasm)?
  • Apakah itu pembuat program (dan, apakah binding ditulis tangan)?

Saya pikir keduanya sah. Dan dalam kasus IDL Web, tampaknya menunjukkan beberapa
batasan (lihat tautan di atas). Mungkin saya baru saja melewatkan langkah penting
prosesnya, dan karenanya, pertimbangkan untuk melupakan pesan saya.

Bahkan jika tujuannya adalah untuk "memfokuskan kembali" IDL Web agar tidak terlalu berpusat pada Web, saat ini,
sangat Web-centric. Dan proposal muncul untuk mengusulkan alternatif,
maka dari itu thread ini. Oleh karena itu, saya khawatir dengan potensi fragmentasi.
Idealnya (dan ini adalah bagaimana Wasm dirancang sejauh ini), diberikan modul Wasm
termasuk binding-nya, dimungkinkan untuk menjalankannya di mana saja apa adanya. Dengan
binding ditulis dalam Web IDL, Cap'n' Proto, FlatBuffers, apa pun, saya
cukup yakin tidak semua kompiler atau penulis program akan menulis hal yang sama
binding dalam sintaks yang berbeda menjadi benar-benar lintas platform. Lucunya, ini
argumen yang mendukung pengikatan tulisan tangan: Orang dapat berkontribusi untuk a
program dengan menulis binding untuk platform P. Tapi mari kita akui ini tidak akan terjadi
ideal sama sekali.

Jadi kesimpulannya: Saya khawatir dengan kemungkinan fragmentasi antara Web dan
binding non-Web. Jika bahasa pengikat non-Web diadakan, apakah itu
diimplementasikan secara realistis oleh browser Web? Mereka harus menulis
binding “Wasm binding language B Web IDL”. Perhatikan bahwa ini sama
skenario untuk semua host: Wasm bahasa pengikat B host API.

Bagi yang penasaran, saya bekerja di Wasmer dan saya penulis PHP-
https://github.com/wasmerio/php-ext-wasm , Python-
https://github.com/wasmerio/python-ext-wasm , Ruby-
https://github.com/wasmerio/ruby-ext-wasm dan Go-
https://github.com/wasmerio/go-ext-wasm Integrasi Wasm. Kami mulai
memiliki taman bermain yang bagus untuk meretas binding yang berbeda untuk yang sangat berbeda
tuan rumah. Jika ada yang ingin saya mengintegrasikan solusi yang berbeda, untuk mengumpulkan
masukan, atau mencoba bereksperimen, kita semua terbuka dan siap untuk berkolaborasi
dan menempatkan lebih banyak sumber daya di atasnya.


Anda menerima ini karena Anda disebutkan.
Balas email ini secara langsung, lihat di GitHub
Https://github.com/WebAssembly/design/issues/1274?email_source=notifications&email_token=AAQAXUD6WA22DDUS7PYQ6F3P4DHYRA5CNFSM4HJUHVG2YY3PNVWWK3TUL52HS4DFVREXG43VMVBWJKLODOR250EAWSZJJKLODN5WWZ2ZTODYLNMVXH37
atau matikan utasnya
https://github.com/notifications/unsubscribe-auth/AAQAXUGM66AWN7ZCIVBTXVTP4DHYRANCNFSM4HJUHVGQ
.

--
Francis McCabe
SW

Kita sudah bisa melihat ini dengan kesulitan mengintegrasikan tipe ref ke dalam rantai alat.

Saya tidak dapat berbicara untuk bahasa lain, tetapi Rust + wasm-bindgen sudah memiliki dukungan untuk:

Jadi saya ingin tahu: kesulitan apa yang Anda maksud?

Pemahaman saya tentang kesulitan lebih pada ujung C++. Rust memiliki metaprogramming yang cukup kuat untuk membuat ini lebih masuk akal di akhir bahasa, tetapi userland C++ memiliki waktu yang lebih sulit untuk menalar tentang anyrefs misalnya.

Saya ingin tahu lebih banyak tentang masalah spesifik C++ di sini. (Apakah mereka spesifik C++ atau LLVM spesifik?)

C++ tidak tahu apa itu tipe ref. Jadi Anda tidak dapat memilikinya di dalam
objek sewenang-wenang. Tidak benar-benar bagian dari bahasa; lebih seperti file
deskriptor. Tempat lucu untuk string.

Pada Mon, Jun 24, 2019 at 03:07 Alon Zakai [email protected] menulis:

Saya ingin tahu lebih banyak tentang masalah spesifik C++ di sini. (Apakah mereka
Khusus C++ atau khusus LLVM?)


Anda menerima ini karena Anda disebutkan.
Balas email ini secara langsung, lihat di GitHub
Https://github.com/WebAssembly/design/issues/1274?email_source=notifications&email_token=AAQAXUDW237MUBBUUJLKS6LP4FARJA5CNFSM4HJUHVG2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW3DYissuedN5ZWSWJKTcomOL5DMVXHJKTcom
atau matikan utasnya
https://github.com/notifications/unsubscribe-auth/AAQAXUB4O3ZX4LRQSQRL763P4FARJANCNFSM4HJUHVGQ
.

--
Francis McCabe
SW

Berbicara dengan @fgmccabe offline, memang benar baik C++ dan Rust tidak dapat secara langsung menyimpan tipe ref dalam suatu struktur, karena struktur tersebut akan disimpan dalam memori linier. Baik C++ dan Rust tentu saja dapat menangani tipe ref secara tidak langsung, dengan cara yang sama mereka menangani deskriptor file, tekstur OpenGL, dll. - dengan pegangan integer. Saya pikir maksudnya adalah bahwa tidak satu pun dari kedua bahasa tersebut yang dapat menangani tipe ref "baik"/"asli" (koreksi saya jika saya salah!) yang saya setujui - bahasa tersebut akan selalu merugikan pada tipe ref operasi, dibandingkan dengan bahasa GC.

Saya tetap ingin tahu apakah ada sesuatu yang spesifik untuk C++ di sini. Saya rasa tidak ada?

Pemahaman saya tentang apa yang membuat C++ sulit di sini adalah jika Anda mengatakan:

struct Anyref; // opaque declaration
void console_log(Anyref* item); // declaration of ref-taking external JS API
Anyref* document_getElementById(const char* str);

void wellBehaved() {
  // This should work
  Anyref* elem = document_getElementById("foo");
  console_log(elem);
}

void notSoWellBehaved() {
  // ????
  Anyref* elem = document_getElementById("bar");
  Anyref* what = (Anyref*)((unsigned int)elem + 1);
  console_log(what);
}

Kabar baiknya adalah bahwa contoh terakhir adalah UB yang saya yakini (pointer yang tidak valid adalah UB segera setelah dibuat), tetapi bagaimana kita mencoba memodelkannya di LLVM IR?

@jgravelle-google Saya pikir bahkan struct Anyref; mengandaikan itu adalah sesuatu yang masuk akal dalam memori linier. Sebagai gantinya, mengapa tidak memodelkannya dengan pegangan integer seperti yang disebutkan sebelumnya, seperti tekstur OpenGL, pegangan file, dan sebagainya?

using Anyref = uint32_t; // handle declaration
void console_log(Anyref item); // declaration of ref-taking external JS API
Anyref document_getElementById(const char* str);

void wellBehaved() {
  // This should work
  Anyref elem = document_getElementById("foo");
  console_log(elem);
}

Pegangan integer harus dicari di tabel saat akan digunakan - sekali lagi, ini hanya kelemahan dari bahasa yang menggunakan memori linier seperti C++ dan Rust. Namun, itu pasti dapat dioptimalkan setidaknya secara lokal - jika tidak oleh LLVM, maka pada tingkat wasm.

Itu akan berhasil, tetapi kemudian Anda harus memastikan untuk memanggil table_free(elem) atau Anda akan menyimpan referensi untuk itu selamanya. Yang tidak -itu- aneh untuk C++, memang.

Ini adalah pemetaan yang aneh karena saya pikir tidak berlapis dengan baik? Rasanya seperti perpustakaan ala OpenGL, tetapi bergantung pada sihir kompiler untuk disediakan - Saya tidak berpikir Anda dapat membangun anyref.h di C++ bahkan dengan inline-wasm, jika Anda bergantung pada mendeklarasikan terpisah meja.

Pokoknya saya pikir itu semua bisa dilakukan/dikerjakan, tetapi tidak langsung saja.

@kripken Memang benar bahwa dukungan anyref asli yang "tepat" akan memerlukan beberapa perubahan pada LLVM (dan di rustc), tetapi itu sebenarnya bukan halangan.

wasm-bindgen menyimpan wasm asli anyref s dalam tabel wasm, dan kemudian dalam memori linier menyimpan indeks integer ke dalam tabel. Sehingga kemudian dapat mengakses anyref dengan menggunakan instruksi wasm table.get .

Sampai wasm-gc diimplementasikan, bahasa GC perlu menggunakan strategi yang sama persis, sehingga Rust (et al) tidak ketinggalan.

Jadi apa yang akan didapat dari dukungan anyref di LLVM? Yah, itu akan memungkinkan langsung melewati/mengembalikan anyref dari fungsi, daripada perlu mengarahkan anyref melalui tabel wasm. Itu akan berguna, ya, tapi itu hanya pengoptimalan kinerja, itu tidak benar-benar mencegah penggunaan anyref .

@Pauan

wasm-bindgen menyimpan wasm anyrefs asli dalam tabel wasm, dan kemudian dalam memori linier menyimpan indeks integer ke dalam tabel. Sehingga kemudian dapat mengakses anyref dengan menggunakan instruksi wasm table.get.

Persis, ya, itulah model yang saya maksud.

Sampai wasm-gc diimplementasikan, bahasa GC perlu menggunakan strategi yang sama persis, sehingga Rust (et al) tidak ketinggalan.

Ya, saat ini bahasa GC tidak memiliki keunggulan karena kami tidak memiliki GC wasm asli. Tapi semoga itu akan berubah! :) Akhirnya saya berharap bahasa GC memiliki keuntungan yang jelas di sini, setidaknya jika kita melakukan GC dengan benar.

Jadi, apa yang akan diperoleh dengan dukungan anyref asli di LLVM? Yah, itu akan memungkinkan secara langsung meneruskan/mengembalikan anyref dari fungsi, daripada perlu mengarahkan anyref melalui tabel wasm. Itu akan berguna, ya, tapi itu hanya pengoptimalan kinerja, itu tidak benar-benar mencegah penggunaan anyref.

Setuju, ya, ini hanya akan menjadi keuntungan sempurna dari bahasa GC (akhirnya) dibandingkan C++ dan Rust dll. Itu tidak mencegah penggunaan.

Siklus adalah masalah yang lebih besar untuk C++ dan Rust, karena tabel root. Mungkin kita dapat memiliki API tracing atau "objek bayangan", pada dasarnya beberapa cara untuk memetakan struktur tautan GC di dalam C++/Rust sehingga GC luar dapat memahaminya. Tapi saya rasa belum ada proposal aktual untuk semua itu.

Akhirnya saya berharap bahasa GC memiliki keuntungan yang jelas di sini, setidaknya jika kita melakukan GC dengan benar.

Saya bisa saja salah, tetapi saya akan terkejut jika itu masalahnya: bahasa GC harus mengalokasikan struct GC wasm dan kemudian mesin wasm harus melacaknya saat mengalir melalui program.

Sebagai perbandingan, Rust tidak memerlukan alokasi (hanya menetapkan tabel), dan hanya perlu menyimpan bilangan bulat, dan mesin wasm hanya perlu melacak 1 tabel tidak bergerak statis untuk keperluan GC.

Saya kira itu mungkin bahwa anyref akses mungkin optimizable untuk bahasa GC, karena tidak perlu menggunakan table.get , namun saya berharap table.get untuk menjadi cukup cepat.

Jadi bisakah Anda menjelaskan lebih lanjut tentang bagaimana Anda mengharapkan program wasm-gc berkinerja lebih baik daripada program yang menggunakan tabel wasm?

PS Ini mulai melenceng dari topik, jadi mungkin kita harus memindahkan diskusi ini ke utas baru?

Benar-benar hanya itu: menghindari table.get/table.set . Dengan GC Anda harus memiliki pointer mentah di sana, menyimpan tipuan. Tapi ya, Anda benar bahwa Rust dan C++ hanya perlu menyimpan bilangan bulat, dan semuanya cukup cepat secara keseluruhan, jadi keuntungan GC apa pun mungkin tidak penting!

Saya setuju kita mungkin keluar dari topik, ya. Saya pikir apa yang ada di topik adalah poin @fgmccabe bahwa tipe ref tidak cocok secara alami dalam bahasa yang menggunakan memori linier. Itu dapat memengaruhi kita dengan cara tertentu dengan binding (khususnya siklus mengkhawatirkan karena C++ dan Rust tidak dapat menanganinya, tetapi mungkin binding dapat mengabaikannya?), jadi saya kira hanya sesuatu yang harus diperhatikan - keduanya untuk mencoba membuat segala sesuatunya berfungsi untuk sebanyak mungkin bahasa, dan tidak terlalu terpengaruh oleh batasan bahasa tertentu.

@kentonv

Lapisan serialisasi Cap'n Proto secara realistis harus diimplementasikan secara independen di setiap bahasa karena sebagian besar adalah lapisan API yang lebar tetapi dangkal yang perlu idiomatik dan ramah-inline. Lapisan RPC, OTOH, sempit tapi dalam

foldernya yang mana?

@PoignardAzur Maaf, saya tidak mengerti pertanyaan Anda.

@kentonv Saya mencari melalui repositori capnproto Github. Di mana lapisan serialisasi?

@PoignardAzur Jadi, ini kembali ke poin saya. Sebenarnya tidak ada satu tempat pun yang dapat Anda tunjuk dan katakan "itulah lapisan serialisasi". Sebagian besar, "serialisasi" Cap'n Proto hanyalah aritmatika penunjuk di sekitar beban/penyimpanan ke buffer yang mendasarinya. Diberikan file skema, Anda menggunakan pembuat kode untuk menghasilkan file header yang mendefinisikan metode sebaris yang melakukan aritmatika penunjuk yang tepat untuk bidang tertentu yang ditentukan dalam skema. Kode aplikasi kemudian perlu memanggil pengakses yang dihasilkan ini setiap kali membaca atau menulis bidang apa pun.

Inilah sebabnya mengapa tidak masuk akal untuk mencoba memanggil implementasi yang ditulis dalam bahasa yang berbeda. Menggunakan FFI mentah untuk setiap akses bidang akan sangat merepotkan, jadi tidak diragukan lagi Anda akan menulis generator kode yang membungkus FFI dalam sesuatu yang lebih cantik (dan khusus untuk skema Anda). Tetapi kode yang dihasilkan itu setidaknya akan serumit kode yang sudah diterapkan oleh Cap'n Proto -- mungkin lebih rumit (dan jauh lebih lambat!). Jadi lebih masuk akal untuk menulis generator kode untuk bahasa target secara langsung.

Mungkin ada beberapa fungsi pembantu internal dalam implementasi Cap'n Proto yang dapat dibagikan. Secara khusus, layout.c++ / layout.h berisi semua kode yang menginterpretasikan pengkodean pointer capnp, melakukan pemeriksaan batas, dll. Pengakses kode yang dihasilkan memanggil kode tersebut saat membaca/menulis bidang pointer. Jadi saya mungkin bisa membayangkan membungkus bagian itu dalam FFI untuk dipanggil dari berbagai bahasa; tapi saya masih berharap untuk menulis pembuat kode dan sejumlah pustaka dukungan runtime di setiap bahasa target.

Ya, maaf, maksud saya sebaliknya ^^ (lapisan RPC)

@PoignardAzur Ohhh, dan saya kira Anda secara khusus tertarik untuk melihat antarmuka, karena Anda berpikir tentang cara membungkusnya dalam FFI. Maka Anda ingin:

  • capability.h : Antarmuka abstrak untuk merepresentasikan kapabilitas dan pemanggilan RPC, yang secara teori dapat didukung oleh berbagai implementasi. (Ini adalah bagian terpenting.)
  • rpc.h : Implementasi RPC melalui jaringan.
  • rpc-twoparty.h : Mengangkut adaptor untuk RPC melalui koneksi sederhana.

Proposal ini sekarang digantikan oleh #1291: OCAP binding.

Apakah halaman ini membantu?
0 / 5 - 0 peringkat

Masalah terkait

lukewagner picture lukewagner  ·  44Komentar

JakeTrock picture JakeTrock  ·  44Komentar

kripken picture kripken  ·  37Komentar

jfbastien picture jfbastien  ·  244Komentar

dcodeIO picture dcodeIO  ·  48Komentar