1 poin oleh GN⁺ 3 jam lalu | 1 komentar | Bagikan ke WhatsApp
  • 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 menghasilkan false, melainkan gagal
  • Karena perbedaan ini, Foo.a(%{foo: 21}), Foo.a(37), dan Foo.b(%{foo: 21}) bernilai true, tetapi Foo.b(37) bernilai false
  • Ini bisa terlihat seolah hukum komutatif pada operasi boolean rusak, tetapi or yang 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 Foo mendefinisikan dua fungsi, a/1 dan b/1
    • a/1: memeriksa guard dengan urutan is_integer(x) or is_map_key(x, :foo)
    • b/1: memeriksa guard dengan urutan is_map_key(x, :foo) or is_integer(x)
    • Jika guard cocok, fungsi mengembalikan true; jika tidak, klausa berikutnya mengembalikan false
  • a/1: ketika kondisi yang aman diletakkan lebih dulu

    • Foo.a(%{foo: 21}) menghasilkan true
      • is_integer(x) adalah false
      • is_map_key(x, :foo) adalah true
      • Hasil or adalah true sehingga klausa pertama cocok
    • Foo.a(37) juga menghasilkan true
      • is_integer(x) adalah true
      • Karena or mengalami short-circuit evaluation, is_map_key(x, :foo) tidak dijalankan
  • b/1: ketika kondisi yang bisa gagal diletakkan lebih dulu

    • Foo.b(%{foo: 21}) menghasilkan true
      • is_map_key(x, :foo) adalah true
      • is_integer(x) setelahnya tidak dijalankan
    • Foo.b(37) menghasilkan false
      • Kondisi pertama is_map_key(x, :foo) tidak mengembalikan false, 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

Short-circuit dan tidak adanya peringatan

  • Bagi banyak pengembang Elixir, perilaku ini bisa terlihat seperti hukum komutatif operator boolean dilanggar
  • Namun, karena or melakukan 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

 
GN⁺ 3 jam lalu
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/2 di 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 menjadi false, mungkin itu juga akan lebih mengejutkan dalam kasus lain.

  • Berkat diskusi ini yang pernah saya lihat, saya sudah siap mengerjakan kuis ini, dan waktu itu saya belajar beberapa hal.

    • Saya terinspirasi oleh diskusi itu untuk menulis artikel ini.
  • 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_key tidak mencakup pemeriksaan is_map.
    Jika menambahkan variasi ketiga seperti is_map(x) and is_map_key(x, :corporal), hasilnya berjalan seperti yang diharapkan.
    Perilaku is_map_key terlihat agak tidak konsisten sehingga terasa mengejutkan, dan akan menarik untuk memeriksa guard is_... lain: mana yang aman dan mana yang harus dievaluasi dengan asumsi tipe tertentu.

    • Saya setuju soal referensi Pratchett, tetapi sekarang sedang gelombang panas, jadi otak saya tidak bekerja seperti yang diharapkan.
    • Karena penasaran, saya mengecek sendiri beberapa hal, dan secara garis besar tampaknya is_map_key adalah satu-satunya guard is_ yang meminta jenis argumen tertentu.
      Fungsi is_ lain mengandung sifat boolean dan selalu mengembalikan true | 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/…

    • Guard di Haskell sedikit berbeda.
      Di Haskell, Anda bisa memanggil fungsi arbitrer di dalam guard, sedangkan Erlang membatasi kumpulan fungsi yang diizinkan di dalamnya.