- Elixir’de guard ifadelerinde
orkoş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şulfalsedö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})trueikenFoo.b(37)falseolur - Boolean işlemlerde değişme özelliği bozulmuş gibi görünse de, kısa devre değerlendirmesi olan
ordoğ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/1veb/1olmak üzere iki fonksiyon tanımlara/1: guard’ıis_integer(x) or is_map_key(x, :foo)sırasıyla kontrol ederb/1: guard’ıis_map_key(x, :foo) or is_integer(x)sırasıyla kontrol eder- Guard eşleşirse
true, aksi halde sonraki maddedefalsedöner
-
a/1: güvenli koşul önce geldiğindeFoo.a(%{foo: 21})sonucutrueoluris_integer(x)falsedöneris_map_key(x, :foo)truedönerorsonucutrueolduğu için ilk madde eşleşir
Foo.a(37)detrueoluris_integer(x)truedönerorkısa devre değerlendirmesi yaptığı içinis_map_key(x, :foo)çalıştırılmaz
-
b/1: başarısız olabilen koşul önce geldiğindeFoo.b(%{foo: 21})sonucutrueoluris_map_key(x, :foo)truedöner- sondaki
is_integer(x)çalıştırılmaz
Foo.b(37)sonucufalseolur- İlk koşul olan
is_map_key(x, :foo),falsedö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
- İlk koşul olan
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
orkı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
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ıpfalseile birleştirdiği bir dil olsaydı, başka durumlarda bunun daha da şaşırtıcı olabileceğini de düşünüyorum.is_map_key/2aslında tamamen sıradan bir Erlang fonksiyonu.https://www.erlang.org/doc/apps/erts/erlang.html#is_map_key/2
Daha önce gördüğüm bu tartışma sayesinde bu quiz’i çözmeye hazırlıklıydım; o zaman birkaç şey öğrenmiştim.
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:
falsedeğil, başarısız olan guard tüm ifadeyi başarısız kılıyor; ayrıca biraz sezgiye aykırı biçimdeis_map_key,is_mapkontrolü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ğeris_...guard’larında hangilerinin güvenli olduğunu, hangilerinin ise bir tip beklentisiyle değerlendirilmesi gerektiğini kontrol etmek ilginç olabilir.is_map_key, belirli türde bir argüman isteyen tekis_guard’ı gibi görünüyor.Diğer
is_fonksiyonları boolean niteliği taşıyor, her zamantrue | falsedö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: falsegibi birden fazla function clause ile yazardım.Birlikte bakmaya değer: https://learnyouahaskell.github.io/syntax-in-functions.html/…
Haskell’de guard içinde keyfi fonksiyonlar çağırabilirsiniz, ama Erlang guard içinde izin verilen fonksiyon kümesini sınırlar.