Haxe 4: Apa yang Baru?

Saya membawa perhatian Anda pada terjemahan laporan Alexander Kuzmenko (sejak April tahun ini ia secara resmi bekerja sebagai pengembang kompiler Haxe) tentang perubahan dalam bahasa Haxe yang telah terjadi sejak rilis Haxe 3.4.


gambar

Lebih dari dua setengah tahun telah berlalu sejak rilis Haxe 3.4. Selama waktu ini, 7 rilis patch, 5 rilis preview Haxe 4 dan 2 kandidat rilis Haxe 4. dirilis. Ini masih jauh dari versi baru dan hampir siap ( sekitar 20 masalah masih harus diselesaikan).


Dahulu kala ...

Alexander berterima kasih kepada komunitas Haxe karena melaporkan bug, atas keinginan mereka untuk berpartisipasi dalam pengembangan bahasa. Berkat proyek evolusi haxe , hal-hal seperti Haxe 4 akan muncul dengan:


  • markup inline
  • fungsi inlining di lokasi panggilan
  • fungsi panah

gambar

Juga, dalam kerangka kerja proyek ini, diskusi sedang diadakan tentang inovasi yang mungkin terjadi seperti: Janji , ini polimorfik, dan tipe standar (parameter tipe default).


Selanjutnya, Alexander berbicara tentang perubahan dalam sintaksis bahasa .


Sintaks baru untuk menggambarkan tipe fungsi

Yang pertama adalah sintaks baru untuk menggambarkan sintaks tipe fungsi. Sintaksis lama agak aneh.
Haxe adalah bahasa pemrograman multi-paradigma, ia selalu memiliki dukungan untuk fungsi kelas satu, tetapi sintaks untuk menggambarkan tipe fungsi diwarisi dari bahasa fungsional (dan berbeda dari yang diadopsi dalam paradigma lain). Dan programmer yang akrab dengan pemrograman fungsional mengharapkan fungsi dengan sintaks ini untuk mendukung currying otomatis. Tetapi di Haxe hal ini tidak terjadi.
Kelemahan utama dari sintaksis lama, menurut Alexander, adalah ketidakmampuan untuk menentukan nama-nama argumen, itulah sebabnya Anda harus menulis komentar anotasi panjang dengan deskripsi argumen.
Tapi sekarang kami memiliki sintaks baru untuk menggambarkan tipe fungsi (yang, omong-omong, ditambahkan ke bahasa sebagai bagian dari inisiatif haxe-evolution), di mana ada kesempatan seperti itu (meskipun ini opsional, tetapi disarankan). Sintaks baru lebih mudah dibaca dan bahkan dapat dianggap sebagai bagian dari dokumentasi untuk kode.
Kelemahan lain dari sintaks lama untuk menggambarkan tipe fungsi adalah inkonsistensi - kebutuhan untuk menentukan tipe argumen fungsi bahkan ketika fungsi tidak menerima argumen: Void->Void (fungsi ini tidak mengambil argumen dan tidak mengembalikan apa-apa).
Dalam sintaks baru, ini diterapkan lebih elegan: ()->Void


Fungsi panah

Yang kedua adalah fungsi panah atau ekspresi lambda - bentuk singkat untuk menggambarkan fungsi anonim. Komunitas telah lama meminta untuk menambahkannya ke bahasa, dan akhirnya itu terjadi!
Dalam fungsi tersebut, alih-alih kata kunci return , urutan karakter -> (maka nama sintaksnya adalah "fungsi panah").
Dalam sintaks baru, masih dimungkinkan untuk mengatur tipe argumen (karena sistem inferensi tipe otomatis tidak selalu dapat melakukan seperti yang diinginkan programmer, misalnya, kompiler dapat memutuskan untuk menggunakan Float daripada Int ).
Satu-satunya batasan sintaks baru adalah ketidakmampuan untuk secara eksplisit mengatur tipe pengembalian. Jika perlu, maka Anda memiliki pilihan untuk menggunakan sintaks lama atau menggunakan sintaks tipe-centang di badan fungsi, yang akan memberi tahu kompilator jenis pengembalian.


Fungsi panah tidak memiliki representasi khusus di pohon sintaksis

Fungsi panah tidak memiliki representasi khusus di pohon sintaksis, mereka diproses dengan cara yang sama seperti fungsi anonim biasa. Urutan -> diganti oleh kata kunci return .


final - sekarang adalah kata kunci

Perubahan ketiga - final sekarang final menjadi kata kunci (di Haxe 3 final adalah salah satu dari meta tag yang ada di kompiler).
Jika Anda menerapkannya ke kelas, itu akan melarang warisan darinya, hal yang sama berlaku untuk antarmuka. Menerapkan kualifikasi final untuk metode kelas akan mencegahnya ditimpa di kelas anak-anak.
Namun, di Haxe, ada cara untuk mengatasi pembatasan yang diberlakukan oleh kata kunci final - Anda dapat menggunakan tag meta @:hack untuk ini (tetapi Anda hanya harus melakukan ini jika benar-benar diperlukan).


Mendeklarasikan bidang opsional dalam struktur anonim

Perubahan keempat adalah cara untuk mendeklarasikan bidang opsional dalam struktur anonim. Sebelumnya, tag meta @:optional digunakan untuk ini, sekarang tambahkan saja tanda tanya di depan nama bidang.


Daftar abstrak

Kelima, enumerasi abstrak telah menjadi anggota penuh dari keluarga tipe Haxe, dan alih-alih tag meta @:enum kata kunci @:enum sekarang digunakan untuk mendeklarasikannya.


Kata kunci eksternal

Perubahan serupa memengaruhi tag meta @:extern .


Sintaks baru untuk tipe gabungan (tipe persimpangan)

Ketujuh adalah sintaks persimpangan tipe baru yang lebih baik mencerminkan esensi dari struktur yang meluas.
Sintaks baru yang sama digunakan untuk membatasi batasan parameter tipe, lebih akurat menyampaikan batasan yang dikenakan pada suatu jenis. Untuk seseorang yang tidak terbiasa dengan Haxe, sintaksis lama MyClass<T:(Type1, Type2)> dapat dianggap sebagai persyaratan untuk jenis parameter T baik Type1 atau Type2 . Sintaks baru secara eksplisit memberitahu kita bahwa T harus menjadi Type1 dan Type2 bersamaan.


Sintaksis yang disederhanakan untuk mendeklarasikan kamus kosong

Kedelapan adalah kemampuan untuk menggunakan [] untuk mendeklarasikan wadah Map kosong (namun, jika Anda tidak secara eksplisit menentukan jenis variabel, kompiler akan menampilkan jenis sebagai array untuk kasus ini).


Setelah berbicara tentang perubahan dalam sintaksis, mari kita beralih ke deskripsi fungsi baru dalam bahasa .
Mari kita mulai dengan iterator nilai kunci yang baru


Iterator nilai kunci

Sintaks baru telah ditambahkan untuk penggunaannya.
Untuk mendukung iterator tersebut, tipe harus mengimplementasikan metode keyValueIterator():KeyValueIterator<K, V> , atau metode hasNext():Bool dan next():{key:K, value:V} . Pada saat yang sama, tipe KeyValueIterator<K, V> adalah sinonim untuk iterator reguler dalam struktur anonim Iterator<{key:K, value:V}> .
Iterator nilai kunci diimplementasikan untuk beberapa jenis dari pustaka standar Haxe ( String , Map , DynamicAccess ), dan pekerjaan juga sedang berlangsung untuk mengimplementasikannya untuk array.


Dukungan untuk iterator baru untuk tipe bawaan

Untuk string, indeks karakter dalam string digunakan sebagai kunci, dan kode karakter pada indeks yang diberikan digunakan sebagai nilai (jika karakter itu sendiri diperlukan, maka metode String.fromCharCode() dapat digunakan).
Untuk wadah Map , iterator baru bekerja sama dengan metode iterasi lama, yaitu, menerima array kunci dalam wadah dan melewatinya, meminta nilai untuk masing-masing kunci.
Untuk DynamicAccess (pembungkus untuk objek anonim), iterator bekerja menggunakan refleksi (untuk mendapatkan daftar bidang objek menggunakan metode Reflect.fields() dan untuk mendapatkan nilai bidang dengan namanya menggunakan metode Reflect.field() ).


Penerjemah makro baru

Haxe 4 menggunakan penerjemah makro yang sepenuhnya baru, "eval". Simon Krajewski, penulis penerjemah, menggambarkannya secara terperinci di blog resmi Haxe , dan juga dalam laporan perkembangannya tahun lalu .
Perubahan utama dalam pekerjaan juru bahasa:


  • beberapa kali lebih cepat dari penerjemah makro yang lama (rata-rata 4 kali)
  • mendukung debugging interaktif (sebelumnya, untuk makro, hanya output konsol yang dapat digunakan)
  • digunakan untuk menjalankan compiler dalam mode interpreter (sebelumnya neko digunakan untuk ini. Omong-omong, eval juga melampaui neko dalam kecepatan).

Dukungan Unicode

Dukungan Unicode untuk semua platform (dengan pengecualian neko) adalah salah satu perubahan terbesar dalam Haxe 4. Simon membicarakan hal ini secara rinci tahun lalu . Tapi di sini adalah gambaran singkat dari keadaan saat ini dukungan string Unicode di Haxe:


  • untuk dukungan unicode penuh Lua, PHP, Python dan eval (penerjemah makro) diimplementasikan (pengkodean UTF8)
  • untuk platform lain (JavaScript, C #, Java, Flash, HashLink dan C ++), digunakan pengkodean UTF16.

Dengan demikian, garis-garis di Haxe bekerja dengan cara yang sama untuk karakter yang termasuk dalam bidang multibahasa utama , tetapi untuk karakter di luar bidang ini (misalnya, untuk emoji), kode untuk bekerja dengan garis dapat menghasilkan hasil yang berbeda tergantung pada platform (tetapi ini masih lebih baik, daripada situasi yang kita miliki di Haxe 3, ketika setiap platform memiliki perilaku sendiri).


Iterator String Baru

Untuk string Unicode-encoded (baik dalam UTF8 dan UTF16), iterator khusus telah ditambahkan ke perpustakaan standar Haxe yang bekerja sama pada SEMUA platform untuk semua karakter (baik dalam bidang multibahasa utama dan seterusnya):


 haxe.iterators.StringIteratorUnicode haxe.iterators.StringKeyValueIteratorUnicode 

Fitur untuk Dukungan Unicode

Karena kenyataan bahwa penerapan string bervariasi dari platform ke platform, perlu diingat beberapa nuansa pekerjaan mereka. Di UTF16, setiap karakter membutuhkan 2 byte, jadi mengakses karakter dalam string dengan indeks cepat, tetapi hanya di dalam bidang multibahasa utama. Di sisi lain, di UTF8 semua karakter didukung, tetapi ini dicapai dengan mengorbankan pencarian lambat untuk karakter dalam string (karena karakter dapat menempati jumlah byte yang berbeda dalam memori, mengakses karakter berdasarkan indeks memerlukan pengulangan melalui garis setiap kali dari awal). Oleh karena itu, ketika bekerja dengan string besar dalam Lua dan PHP, Anda harus ingat bahwa akses ke karakter sewenang-wenang bekerja cukup lambat (juga pada platform ini, panjang string dihitung lagi setiap kali).
Namun, meskipun dukungan Unicode penuh dinyatakan untuk Python, pembatasan ini tidak berlaku untuk itu karena garis-garis di dalamnya diimplementasikan dalam cara yang sedikit berbeda: untuk karakter dalam bidang multibahasa utama, itu menggunakan pengkodean UTF16, dan untuk karakter yang lebih luas (3 dan lebih banyak byte) Python menggunakan UTF32.
Optimalisasi tambahan diterapkan untuk penerjemah makro eval: string "tahu" apakah itu berisi karakter Unicode. Jika tidak mengandung karakter seperti itu, string diinterpretasikan sebagai terdiri dari karakter ASCII (di mana setiap karakter membutuhkan 1 byte). Akses berurutan menurut indeks dalam eval juga dioptimalkan: posisi karakter terakhir yang diakses di-cache dalam baris. Jadi jika Anda pertama kali beralih ke karakter ke-10 dalam string, maka ketika Anda selanjutnya beralih ke karakter ke-20, eval akan mencarinya bukan dari awal baris, tetapi mulai dari 10. Selain itu, panjang string dalam eval di-cache, yaitu, itu dihitung hanya pada permintaan pertama.


Dukungan Namespace untuk flag kompilasi

Haxe 4 memperkenalkan dukungan untuk ruang nama untuk flag kompilasi, yang dapat berguna, misalnya, untuk mengatur kode saat menulis pustaka kustom.
Juga, namespace yang disediakan untuk bendera kompilasi muncul - target , yang digunakan oleh kompiler untuk menggambarkan platform target dan perilakunya:


  • target.name - nama platform (js, cpp, php, dll.)
  • target.utf16 - mengatakan bahwa dukungan Unicode diimplementasikan menggunakan UTF16
  • target.sys - menunjukkan apakah kelas dari paket sys tersedia (misalnya, untuk bekerja dengan sistem file)
  • target.static - menunjukkan apakah platform statis (pada platform statis, tipe dasar Int , Float dan Bool tidak boleh memiliki null sebagai nilainya)
  • target.threaded - menunjukkan apakah platform mendukung multithreading

Dukungan Namespace untuk tag meta

Demikian pula, dukungan namespace untuk tag meta telah muncul. Sejauh ini tidak ada ruang nama yang dicadangkan untuk tag meta dalam bahasa, tetapi situasinya mungkin berubah di masa mendatang.


Jenis Baru - ReadOnlyArray

Tipe ReadOnlyArray ditambahkan ke pustaka standar Haxe - abstraksi atas array biasa, di mana metode hanya tersedia untuk membaca data dari array.


Field Terakhir dan Variabel Lokal

Inovasi lain dalam bahasa ini adalah bidang terakhir dan variabel lokal.
Jika final digunakan sebagai pengganti kata kunci var ketika mendeklarasikan bidang kelas atau variabel lokal, ini berarti bahwa bidang atau variabel ini tidak dapat dipindahkan (kompiler akan memberikan kesalahan ketika mencoba melakukan ini). Tetapi pada saat yang sama, keadaannya dapat diubah, sehingga bidang terakhir atau variabel tidak konstan.


Kompiler mengontrol inisialisasi bidang terakhir

Nilai-nilai dari bidang terakhir harus diinisialisasi ketika mereka dideklarasikan, atau dalam konstruktor, jika tidak, kompiler akan melempar kesalahan.


Hashlink

HashLink adalah platform baru dengan mesin virtualnya sendiri, dibuat khusus untuk Haxe. HashLink mendukung apa yang disebut "kompilasi ganda" - kode dapat dikompilasi baik dalam bytecode (yang sangat cepat, mempercepat proses debugging aplikasi yang dikembangkan), atau dalam C-code (yang ditandai dengan peningkatan kinerja). Nicholas mendedikasikan HashLink untuk beberapa posting blog Haxe dan juga membicarakannya di konferensi Seattle tahun lalu . Teknologi HashLink digunakan dalam gim populer seperti Dead Cells dan Northgard.


Keamanan kosong

Fitur baru yang menarik dari Haxe 4 adalah keamanan Null, yang masih dalam tahap percobaan (karena positif palsu dan tidak cukupnya pemeriksaan keamanan kode).
Apa itu keamanan nol? Jika fungsi Anda tidak secara eksplisit menyatakan bahwa ia dapat menerima null sebagai nilai parameter, maka ketika Anda mencoba untuk meneruskannya, kompiler akan melempar kesalahan yang sesuai. Selain itu, untuk parameter fungsi yang dapat mengambil null sebagai nilai, kompiler akan meminta Anda untuk menulis kode tambahan untuk memverifikasi dan menangani kasus-kasus tersebut.
Fungsionalitas ini dinonaktifkan secara default, tetapi tidak memengaruhi kecepatan eksekusi kode (jika Anda mengaktifkannya), karena pemeriksaan yang dijelaskan hanya dilakukan pada tahap kompilasi. Itu dapat diaktifkan untuk semua kode, serta secara bertahap diaktifkan untuk masing-masing bidang, kelas, dan paket (sehingga memberikan transisi bertahap ke kode yang lebih aman). Anda dapat menggunakan meta tag dan makro khusus untuk ini.
Mode-mode di mana Null-security dapat bekerja adalah: Strict (paling ketat), Loose (mode default) dan Off (digunakan untuk menonaktifkan pemeriksaan untuk paket dan tipe individual).


Mode pemeriksaan keamanan nol standar

Untuk fungsi yang ditunjukkan pada slide, pemeriksaan keamanan Null diaktifkan. Kita melihat bahwa fungsi ini memiliki parameter opsional s , yaitu, kita dapat memasukkan null ke dalamnya sebagai nilai parameter. Saat mencoba mengkompilasi kode dengan fungsi seperti itu, kompiler akan menghasilkan sejumlah kesalahan:


  • ketika mencoba mengakses beberapa bidang objek s (karena mungkin null )
  • ketika mencoba untuk menetapkan str variabel, yang, seperti yang kita lihat, tidak boleh null (kalau tidak kita seharusnya menyatakan itu bukan sebagai String , tetapi sebagai Null<String> )
  • ketika mencoba mengembalikan objek s dari suatu fungsi (karena fungsi seharusnya tidak mengembalikan null )

Bagaimana cara memperbaiki kesalahan ini?


Perbaiki bug keamanan nol

Kita hanya perlu menambahkan pemeriksaan null ke kode (di dalam blok dengan pemeriksaan null , kompiler "tahu" bahwa s tidak dapat null dan dapat digunakan dengan aman dengannya), dan juga memastikan bahwa fungsi tersebut tidak mengembalikan null !


Saat melakukan pemeriksaan untuk keamanan Null, kompiler memperhitungkan urutan eksekusi program

Selain itu, ketika melakukan pemeriksaan untuk keamanan Null, kompiler memperhitungkan urutan di mana program dijalankan. Sebagai contoh, jika setelah memeriksa nilai parameter s ke null untuk menghentikan fungsi (atau melempar pengecualian), kompiler akan "tahu" bahwa setelah pemeriksaan seperti itu, parameter s tidak bisa lagi menjadi null , dan dapat digunakan dengan aman.


Mode ketat untuk melakukan pemeriksaan keamanan nol

Jika kompiler mengaktifkan mode pemeriksaan ketat untuk keamanan Null, itu akan memerlukan pemeriksaan tambahan untuk null dalam kasus di mana antara pemeriksaan awal dari nilai untuk null dan upaya untuk mengakses bidang objek kode apa pun yang dieksekusi kode yang dapat mengaturnya ke null .


Mode longgar untuk melakukan pemeriksaan keamanan nol

Dalam mode Loose (digunakan secara default), kompiler tidak akan memerlukan pemeriksaan seperti itu (omong-omong, perilaku ini juga digunakan secara default dalam TypeScript).


Memeriksa inisialisasi bidang ketika melakukan pemeriksaan untuk keamanan nol

Juga, ketika pemeriksaan untuk keamanan Null dihidupkan, kompiler memeriksa apakah bidang di kelas diinisialisasi (langsung ketika mereka dinyatakan atau dalam konstruktor). Jika tidak, kompiler akan melempar kesalahan ketika mencoba untuk melewatkan objek dari kelas seperti itu, serta ketika mencoba memanggil metode pada objek tersebut, sampai semua bidang objek diinisialisasi. Pemeriksaan tersebut dapat dimatikan untuk masing-masing bidang kelas dengan menandai mereka dengan tag meta @:nullSafety(Off)
Alexander berbicara lebih banyak tentang keamanan Null di Haxe Oktober lalu .


Menghasilkan kelas ES6 untuk JavaScript

Haxe 4 memperkenalkan kemampuan untuk menghasilkan kelas ES6 untuk JavaScript, ini diaktifkan menggunakan flag kompilasi js-es=6 .


Fungsi Di Tempat

Menanamkan fungsi di tempat panggilan (panggilan-situs inlining) menyediakan lebih banyak opsi untuk mengontrol keseimbangan antara kinerja kode dan ukuran. Fungsi ini juga digunakan di perpustakaan standar Haxe.
Seperti apa dia? Ini memungkinkan Anda untuk menyematkan badan fungsi (menggunakan inline ) hanya di tempat-tempat di mana ia diperlukan untuk memastikan kinerja tinggi (misalnya, jika perlu, memanggil metode yang cukup produktif dalam loop), sementara di tempat lain badan fungsi tidak tertanam. Akibatnya, ukuran kode yang dihasilkan akan sedikit meningkat.


Ekstensi Otomatis untuk Jenis

Penggunaan otomatis (ekstensi otomatis untuk tipe) berarti sekarang untuk tipe Anda dapat mendeklarasikan ekstensi statis di tempat deklarasi tipe. Ini menghilangkan kebutuhan untuk using type; konstruk using type; setiap kali using type; di setiap modul di mana jenis dan metode ekstensi digunakan. Saat ini, ekstensi jenis ini hanya diterapkan untuk transfer, tetapi pada rilis final (dan pada build malam) dapat digunakan tidak hanya untuk transfer.


Mengesampingkan operator untuk mengakses bidang objek

Dalam Haxe 4, dimungkinkan untuk mendefinisikan ulang operator untuk mengakses bidang objek untuk tipe abstrak (hanya untuk bidang yang tidak ada dalam tipe). Untuk melakukan ini, gunakan metode yang ditandai dengan tag meta @:op(ab) .


Markup bawaan

Markup bawaan adalah fitur eksperimental lain di Haxe. Kode markup bawaan tidak diproses oleh kompiler sebagai dokumen xml - kompilator melihatnya sebagai string yang dibungkus dengan tag meta @:markup . .



-, - @:markup , .


untyped-

( untyped ). . , , Js.build() - @:markup , <js> , js-.


-  -

Haxe 4 - - , — .



. , . , Int , , C.



— .


:


JVM-

JVM- JDK, Java-. . .



, async / await yield . ( C#, ). Haxe github.


,

Haxe , . ( ) . , .


API

API . , , API .


Haxe 4 !

Source: https://habr.com/ru/post/id455389/


All Articles