1 puan yazan GN⁺ 3 시간 전 | 1 yorum | WhatsApp'ta paylaş
  • Elixir’de guard ifadelerinde or koşullarının yerini değiştirmek bile, aynı mantık ifadesi gibi görünen kodun sonucunu değiştirebilir
  • is_integer(x) or is_map_key(x,:foo) sıralamasında tamsayı girdisinde kısa devre değerlendirmesi önce gerçekleşir ve riskli kontrol atlanır
  • Buna karşılık is_map_key(x,:foo) or is_integer(x) ifadesinde tamsayı girdisinde ilk koşul false dönmek yerine başarısız olur ve sonraki koşula geçilemez
  • Bu fark nedeniyle Foo.a(%{foo: 21}), Foo.a(37), Foo.b(%{foo: 21}) true iken Foo.b(37) false olur
  • Boolean işlemlerde değişme özelliği bozulmuş gibi görünse de, kısa devre değerlendirmesi olan or doğal olarak koşul sırasından etkilenir ve Elixir 1.20.1 ile OTP 29 itibarıyla bunun için bir uyarı yoktur

Koşul sırasının sonucu değiştirdiği örnek

  • Örnek modül Foo, a/1 ve b/1 olmak üzere iki fonksiyon tanımlar
    • a/1: guard’ı is_integer(x) or is_map_key(x, :foo) sırasıyla kontrol eder
    • b/1: guard’ı is_map_key(x, :foo) or is_integer(x) sırasıyla kontrol eder
    • Guard eşleşirse true, aksi halde sonraki maddede false döner
  • a/1: güvenli koşul önce geldiğinde

    • Foo.a(%{foo: 21}) sonucu true olur
      • is_integer(x) false döner
      • is_map_key(x, :foo) true döner
      • or sonucu true olduğu için ilk madde eşleşir
    • Foo.a(37) de true olur
      • is_integer(x) true döner
      • or kısa devre değerlendirmesi yaptığı için is_map_key(x, :foo) çalıştırılmaz
  • b/1: başarısız olabilen koşul önce geldiğinde

    • Foo.b(%{foo: 21}) sonucu true olur
      • is_map_key(x, :foo) true döner
      • sondaki is_integer(x) çalıştırılmaz
    • Foo.b(37) sonucu false olur
      • İlk koşul olan is_map_key(x, :foo), false döndürmek yerine başarısız olur
      • Tek bir guard fonksiyonunun başarısız olması false’a çevrilmez; tüm guard ifadesini başarısız kılar
      • Sonraki is_integer(x) çağrılmaz ve ilk madde de eşleşmez

Kısa devre değerlendirmesi ve uyarı eksikliği

  • Birçok Elixir geliştiricisi için bu davranış, boolean işleçlerinin değişme özelliği bozulmuş gibi görünebilir
  • Ancak or kısa devre değerlendirmesi yaptığı için, iki koşulun yerini değiştirmenin her zaman aynı sonucu vereceği söylenemez
  • Referans ortam Elixir 1.20.1, OTP 29 ve görünen o ki Elixir bu durum için uyarı vermiyor

1 yorum

 
GN⁺ 3 시간 전
Lobste.rs yorumları
  • Elixir programcısı değilim ama son örnekte en şaşırtıcı olan şey, guard ifadesindeki hatanın çağırana aktarılmaması ve o guard’ın “atlanması”.
    Neden böyle tasarlandığını anlayabiliyorum, ama sezgilere aykırı sonuçlar çıkması da şaşırtıcı değil.

  • Erlang’ın API tasarımının, Armstrong’un Erlang tezinde s.109/böl.4.5’te anlattığı niyetli programlamayı desteklemek için yapıldığını düşününce bu ironik.
    Tezde dict:fetch(Key, Dict), dict:search(Key, Dict), dict:is_key(Key, Dict) gibi, programcının “anahtar mutlaka var olmalı”, “olabilir, akışı buna göre ayıracağım”, “sadece var olup olmadığını kontrol edeceğim” niyetlerini ortaya koyan işlevler ayrı ayrı açıklanıyor.
    Ama Elixir’de is_map_key/2, “dict” argümanı dict değilse exception fırlatıyor; bu exception başarısızlığı da tüm guard clause’unun başarısız olmasına yol açarak bu ayrımı bozuyor gibi görünüyor.
    Öte yandan or’un exception’ı yakalayıp false ile birleştirdiği bir dil olsaydı, başka durumlarda bunun daha da şaşırtıcı olabileceğini de düşünüyorum.

  • Daha önce gördüğüm bu tartışma sayesinde bu quiz’i çözmeye hazırlıklıydım; o zaman birkaç şey öğrenmiştim.

    • Bu yazıyı yazmama o tartışma ilham verdi.
  • Bir şeyler öğrendim, ama neden Pratchett göndermesinden kaçınıldığına üzüldüm.
    Death bir yerlerde alnını ovuşturuyordur.
    Burada ilginç olan iki şey var: false değil, başarısız olan guard tüm ifadeyi başarısız kılıyor; ayrıca biraz sezgiye aykırı biçimde is_map_key, is_map kontrolünü içinde barındırmıyor.
    is_map(x) and is_map_key(x, :corporal) gibi üçüncü bir varyasyon eklerseniz beklendiği gibi çalışıyor.
    is_map_key’in davranışı biraz tutarsız görünüyor ve bu yüzden şaşırtıcı geliyor; diğer is_... guard’larında hangilerinin güvenli olduğunu, hangilerinin ise bir tip beklentisiyle değerlendirilmesi gerektiğini kontrol etmek ilginç olabilir.

    • Pratchett göndermesine katılıyorum ama şu an sıcak hava dalgası var, beynim beklendiği gibi çalışmıyor.
    • Merak edip birkaç şeyi kendim kontrol ettim; kabaca bakınca is_map_key, belirli türde bir argüman isteyen tek is_ guard’ı gibi görünüyor.
      Diğer is_ fonksiyonları boolean niteliği taşıyor, her zaman true | false döndürüyor ve başarısız olmuyor.
  • Burada ilginç bir Elixir stili sorusu ortaya çıkıyor.
    Örnek eğlenceli ve açıklaması da iyi, ama şahsen mümkün olduğunda guard yerine pattern matching’i tercih ederim.
    Elbette istisnalar var, ama bu tür fonksiyonları genelde def a(%{foo: _x}), do: true, def a(x) when is_integer(x), do: true, def a(_), do: false gibi birden fazla function clause ile yazardım.

  • Birlikte bakmaya değer: https://learnyouahaskell.github.io/syntax-in-functions.html/…

    • Haskell’in guard’ları biraz farklı.
      Haskell’de guard içinde keyfi fonksiyonlar çağırabilirsiniz, ama Erlang guard içinde izin verilen fonksiyon kümesini sınırlar.