- Dalam guard Elixir, hanya dengan menukar urutan kondisi
or, hasil dari kode yang tampak seperti ekspresi logis yang sama bisa berubah - Urutan
is_integer(x) or is_map_key(x,:foo)membuat short-circuit evaluation terjadi lebih dulu pada input bilangan bulat sehingga pemeriksaan yang berisiko dilewati - Sebaliknya,
is_map_key(x,:foo) or is_integer(x)pada input bilangan bulat tidak melanjutkan ke kondisi berikutnya karena kondisi pertama tidak menghasilkanfalse, melainkan gagal - Karena perbedaan ini,
Foo.a(%{foo: 21}),Foo.a(37), danFoo.b(%{foo: 21})bernilaitrue, tetapiFoo.b(37)bernilaifalse - Ini bisa terlihat seolah hukum komutatif pada operasi boolean rusak, tetapi
oryang memiliki short-circuit memang dipengaruhi urutan kondisi, dan berdasarkan Elixir 1.20.1 serta OTP 29 tidak ada peringatan
Contoh saat urutan kondisi mengubah hasil
- Modul contoh
Foomendefinisikan dua fungsi,a/1danb/1a/1: memeriksa guard dengan urutanis_integer(x) or is_map_key(x, :foo)b/1: memeriksa guard dengan urutanis_map_key(x, :foo) or is_integer(x)- Jika guard cocok, fungsi mengembalikan
true; jika tidak, klausa berikutnya mengembalikanfalse
-
a/1: ketika kondisi yang aman diletakkan lebih duluFoo.a(%{foo: 21})menghasilkantrueis_integer(x)adalahfalseis_map_key(x, :foo)adalahtrue- Hasil
oradalahtruesehingga klausa pertama cocok
Foo.a(37)juga menghasilkantrueis_integer(x)adalahtrue- Karena
ormengalami short-circuit evaluation,is_map_key(x, :foo)tidak dijalankan
-
b/1: ketika kondisi yang bisa gagal diletakkan lebih duluFoo.b(%{foo: 21})menghasilkantrueis_map_key(x, :foo)adalahtrueis_integer(x)setelahnya tidak dijalankan
Foo.b(37)menghasilkanfalse- Kondisi pertama
is_map_key(x, :foo)tidak mengembalikanfalse, melainkan gagal - Kegagalan satu fungsi guard tidak diubah menjadi
false, tetapi membuat seluruh ekspresi guard gagal is_integer(x)setelahnya tidak dipanggil dan klausa pertama juga tidak cocok
- Kondisi pertama
Short-circuit dan tidak adanya peringatan
- Bagi banyak pengembang Elixir, perilaku ini bisa terlihat seperti hukum komutatif operator boolean dilanggar
- Namun, karena
ormelakukan short-circuit, menukar posisi dua kondisi tidak berarti hasilnya akan selalu sama - Lingkungan acuannya adalah Elixir 1.20.1, OTP 29, dan tampaknya Elixir tidak memberikan peringatan untuk masalah ini
1 komentar
Komentar di Lobste.rs
Saya bukan programmer Elixir, tetapi hal yang paling mengejutkan pada contoh terakhir adalah error pada ekspresi guard tidak dipropagasikan ke pemanggil, melainkan guard itu “dilewati”.
Saya bisa memahami kenapa dibuat begitu, tetapi tidak heran juga jika hasilnya terasa tidak intuitif.
Ini ironis jika mengingat desain API Erlang dimaksudkan untuk membantu intentional programming seperti yang dibahas Armstrong dalam tesis Erlang p109/s4.5.
Dalam tesis itu dijelaskan pemisahan fungsi seperti
dict:fetch(Key, Dict),dict:search(Key, Dict),dict:is_key(Key, Dict)yang mengungkapkan niat programmer: “key ini harus ada”, “key ini mungkin ada jadi alurnya dibagi”, dan “hanya memeriksa apakah ada”.Namun
is_map_key/2di Elixir melempar exception jika argumen “dict”-nya bukan dict, dan kegagalan exception itu membuat seluruh klausa guard gagal, sehingga terlihat merusak pembedaan ini.Sebaliknya, kalau ada bahasa yang
or-nya menangkap exception lalu menggabungkannya menjadifalse, mungkin itu juga akan lebih mengejutkan dalam kasus lain.is_map_key/2sebenarnya adalah fungsi Erlang yang sepenuhnya biasa.https://www.erlang.org/doc/apps/erts/erlang.html#is_map_key/2
Berkat diskusi ini yang pernah saya lihat, saya sudah siap mengerjakan kuis ini, dan waktu itu saya belajar beberapa hal.
Ada hal yang dipelajari, tetapi sayang kenapa referensi Pratchett dihindari.
Death sepertinya sedang menepuk dahi di suatu tempat.
Ada dua hal menarik di sini: yang membuat seluruh ekspresi gagal bukan
false, melainkan guard yang gagal, dan, agak berlawanan dengan intuisi,is_map_keytidak mencakup pemeriksaanis_map.Jika menambahkan variasi ketiga seperti
is_map(x) and is_map_key(x, :corporal), hasilnya berjalan seperti yang diharapkan.Perilaku
is_map_keyterlihat agak tidak konsisten sehingga terasa mengejutkan, dan akan menarik untuk memeriksa guardis_...lain: mana yang aman dan mana yang harus dievaluasi dengan asumsi tipe tertentu.is_map_keyadalah satu-satunya guardis_yang meminta jenis argumen tertentu.Fungsi
is_lain mengandung sifat boolean dan selalu mengembalikantrue | false, tidak gagal.Di sini muncul pertanyaan menarik tentang gaya Elixir.
Contohnya menarik dan penjelasannya juga bagus, tetapi secara pribadi saya lebih memilih pattern matching daripada guard jika memungkinkan.
Tentu ada pengecualian, tetapi fungsi seperti ini biasanya akan saya tulis sebagai beberapa klausa fungsi seperti
def a(%{foo: _x}), do: true,def a(x) when is_integer(x), do: true,def a(_), do: false.Layak dibaca bersama: https://learnyouahaskell.github.io/syntax-in-functions.html/…
Di Haskell, Anda bisa memanggil fungsi arbitrer di dalam guard, sedangkan Erlang membatasi kumpulan fungsi yang diizinkan di dalamnya.