it-swarm.dev

Fonksiyonel programlamada yan etkiler neden kötü olarak değerlendiriliyor?

Yan etkilerin doğal bir fenomen olduğunu hissediyorum. Ama fonksiyonel dillerde tabu gibi bir şey. Sebepler neler?

Sorum işlevsel programlama stiline özgü. Tüm programlama dilleri/paradigmaları değil.

70
Gulshan

İşlevlerinizi/yöntemlerinizi yan etki olmadan yazmak - böylece bunlar saf işlevlerdir - programınızın doğruluğu hakkında mantık yürütmeyi kolaylaştırır.

Ayrıca, yeni davranışlar oluşturmak için bu işlevleri oluşturmayı da kolaylaştırır.

Ayrıca, derleyicinin işlevlerin sonuçlarını hatırlayabileceği veya Ortak Alt İfade Eliminasyonunu kullanabileceği belirli optimizasyonları da mümkün kılar.

Düzenleme: Benjol'un isteği üzerine: Eyaletinizin birçoğu yığınta saklandığından (veri akışı, akış kontrol değil, Jonas'ın çağırdığı gibi burada ), bu parçaların yürütülmesini paralelleştirebilir veya yeniden sıralayabilirsiniz birbirinizden bağımsız olan Bu bağımsız parçaları kolayca bulabilirsiniz çünkü bir parça diğerine girdi sağlamaz.

Yığını geri almanıza ve hesaplamaya (Smalltalk gibi) devam etmenize izin veren hata ayıklayıcıları olan ortamlarda, saf işlevlere sahip olmak, bir değerin nasıl değiştiğini kolayca görebileceğiniz anlamına gelir, çünkü önceki durumlar denetim için kullanılabilir. Mutasyon ağırlıklı bir hesaplamada, yapınıza veya algoritmanıza açıkça do/undo eylemleri eklemezseniz, hesaplamanın geçmişini göremezsiniz. (Bu, ilk paragrafa bağlanır: saf işlevler yazmak, programınızın doğruluğunu incelemeyi kolaylaştırır.)

73
Frank Shearar

Yanlış anladınız, fonksiyonel programlama, programların anlaşılmasını ve optimize edilmesini kolaylaştırmak için yan etkileri sınırlandırmayı teşvik ediyor. Haskell bile dosyalara yazmanıza izin verir.

Aslında söylediğim şey, işlevsel programcıların yan etkilerin kötü olduğunu düşünmemesi, sadece yan etkilerin kullanımını sınırlamanın iyi olduğunu düşünüyor. Bu kadar basit bir ayrım gibi göründüğünü biliyorum ama tüm farkı yaratıyor.

24
ChaosPandion

Fonksiyonel programlama ile ilgili bir makaleden:

Uygulamada, uygulamaların bazı yan etkileri olması gerekir. Haskell, işlevsel programlama diline büyük katkıda bulunan Simon Peyton-Jones şunları söyledi: "Sonunda, herhangi bir programın durumu manipüle etmesi gerekir. Hiçbir yan etkisi olmayan bir program bir tür kara kutudur. "kutu ısınıyor." ( http://oscon.blip.tv/file/324976 ) Anahtar, yan etkileri sınırlamak, net bir şekilde tanımlamak ve kod boyunca dağılmasını önlemek.

23
Peter Stuifzand

Birkaç not:

  • Yan etkileri olmayan işlevler önemsiz bir şekilde paralel olarak yürütülürken, yan etkileri olan işlevler tipik olarak bir tür senkronizasyon gerektirir.

  • Yan etkisi olmayan işlevler daha agresif bir optimizasyona izin verir (örneğin, bir sonuç önbelleğini şeffaf bir şekilde kullanarak), çünkü doğru sonucu elde ettiğimiz sürece, işlevin gerçekten olup olmadığı önemli değildir. infaz

13
user281377

Öncelikle fonksiyonel kodda çalışıyorum ve bu açıdan körü körüne açık görünüyor. Yan etkiler, kodu okumaya ve anlamaya çalışan programcılar üzerinde büyük zihinsel bir yük oluşturur. Bir süre ondan kurtulana kadar bu yükü fark etmiyorsunuz, sonra aniden yan etkileri olan kodu tekrar okumak zorundasınız.

Bu basit örneği düşünün:

val foo = 42
// Several lines of code you don't really care about, but that contain a
// lot of function calls that use foo and may or may not change its value
// by side effect.

// Code you are troubleshooting
// What's the expected value of foo here?

İşlevsel bir dilde, ben foo'ın hala 42 olduğunu biliyorum . Hatta arasındaki koda bakın , çok daha az anlayın veya çağırdığı fonksiyonların uygulamalarına bakın.

Eşzamanlılık ve paralelleştirme ve optimizasyon hakkında her şey Nice'tir, ancak bilgisayar bilimcilerinin broşür üzerine koyduğu şey budur. Değişkeni kimin mutasyona uğrattığını ve günlük uygulamada gerçekten ne zaman zevk aldığımı merak etmek zorunda değilim.

11
Karl Bielefeldt

Çok az dil yan etkilere neden olmayı imkansız kılmaktadır. Tamamen yan etkisi olmayan dillerin kullanımı, çok sınırlı bir kapasite haricinde, kullanımı imkansızdır (neredeyse imkansızdır).

Neden yan etkiler kötülük olarak kabul edilir?

Çünkü bir programın tam olarak ne yaptığını düşünmeyi ve programın yapmasını beklediğiniz şeyi yaptığını kanıtlamayı çok daha zorlaştırıyorlar.

Çok yüksek bir seviyede, 3 katmanlı bir web sitesinin tamamını yalnızca kara kutu testi ile test ettiğinizi düşünün. Tabii ki, ölçeğe bağlı olarak yapılabilir. Ama kesinlikle çok fazla yineleme devam ediyor. Ve is bir hata (bir yan etki ile ilgili) varsa, hata teşhis edilip düzeltilinceye ve düzeltme düzeltilinceye kadar tüm sistemi daha fazla test için kırabilirsiniz. test ortamına dağıtıldı.

Faydaları

Şimdi bunu küçültün. Yan etki ücretsiz kod yazma konusunda oldukça iyiyseniz, mevcut bazı kodların ne yaptığını düşünmede ne kadar hızlı olursunuz? Birim testleri ne kadar hızlı yazabilirsiniz? Hiçbir yan etkisi olmayan kodun hatasız garanti edildiğini ve kullanıcıların maruz kaldıkları herhangi bir hataya maruz kalmalarını sınırlayabildiğinden ne kadar emin olabilirsiniz did?

Kodun herhangi bir yan etkisi yoksa, derleyicinin gerçekleştirebileceği ek optimizasyonlar da olabilir. Bu optimizasyonları uygulamak çok daha kolay olabilir. Yan etki içermeyen kod için bir optimizasyonun kavramsallaştırılması bile çok daha kolay olabilir, bu da derleyici satıcınızın yan etkileri olan kodlarda imkansız olan optimizasyonları uygulayabileceği anlamına gelir.

Eşzamanlılık, kodun yan etkisi olmadığında uygulanması, otomatik olarak oluşturulması ve optimize edilmesi de büyük ölçüde daha basittir. Bunun nedeni, tüm parçaların herhangi bir sırada güvenli bir şekilde değerlendirilebilmesidir. Programcıların yüksek derecede eşzamanlı kod yazmalarına izin vermek, Bilgisayar Bilimi'nin üstesinden gelmesi gereken bir sonraki büyük zorluk ve Moore Yasası aleyhine kalan az sayıdaki çitlerden biri olarak kabul edilir.

6

Yan etkiler, kodunuzda sizin tarafınızdan veya bazı şüpheli olmayan iş arkadaşınız tarafından daha sonra ele alınması gereken "sızıntılar" gibidir.

İşlevsel diller, kodu daha az bağlama bağımlı ve daha modüler hale getirmenin bir yolu olarak durum değişkenlerini ve değiştirilebilir verileri önler. Modülerlik, bir geliştiricinin çalışmasının diğerinin çalışmasını etkilememesini/zayıflatmamasını sağlar.

Gelişim oranını ekip büyüklüğü ile ölçeklendirme, günümüzde yazılım geliştirmenin "kutsal bir kasesi" dir. Diğer programcılarla çalışırken modülerlik kadar az şey önemlidir. En basit mantıksal yan etkiler bile işbirliğini son derece zorlaştırır.

4
Ami

IMHO, bu oldukça ikiyüzlü. Kimse yan etkileri sevmez, ancak herkesin onlara ihtiyacı vardır.

Yan etkiler hakkında bu kadar tehlikeli olan şey, bir işlevi çağırırsanız, bunun muhtemelen işlevin bir dahaki sefere çağrıldığında davranış şekli üzerinde değil, muhtemelen diğer işlevler üzerinde de etkisi vardır. Böylece yan etkiler öngörülemeyen davranışlar ve önemsiz bağımlılıklar ortaya çıkarır.

OO ve fonksiyonel gibi programlama paradigmaları bu sorunu ele alır. OO endişelerin ayrılmasını empoze ederek sorunu azaltır. çok sayıda değişebilir veri, her biri yalnızca kendi durumunu korumaktan sorumlu olan nesnelere kapsüllenir.Bu şekilde bağımlılık riski azalır ve sorunlar çok daha izole ve izlemesi daha kolaydır.

Fonksiyonel programlama, uygulama durumunun programcı açısından basitçe değişmez olduğu çok daha radikal bir yaklaşım gerektirir. Bu güzel bir fikir, ancak dili kendi başına işe yaramaz hale getiriyor. Neden? Çünkü HERHANGİ BİR G/Ç işleminin yan etkileri vardır. Herhangi bir giriş akışından okuduğunuzda, uygulama durumunuz değişecektir, çünkü aynı işlevi bir sonraki başlatışınızda sonuç muhtemelen farklı olacaktır. Farklı veriler okuyor olabilirsiniz - ya da - bir olasılıkla - işlem başarısız olabilir. Aynı şey çıktı için de geçerlidir. Çıktı bile yan etkileri olan bir işlemdir. Bu, günümüzde sık sık fark ettiğiniz bir şey değil, ancak çıktınız için sadece 20K'nız olduğunu ve daha fazla çıktı alırsanız, disk alanınız veya başka bir şey olmadığından uygulamanız çöküyor.

Yani evet, yan etkiler bir programcının bakış açısından kötü ve tehlikelidir. Hataların çoğu, uygulama durumunun belirli bölümlerinin neredeyse belirsiz bir şekilde, dikkate alınmayan ve çoğu zaman gereksiz yan etkiler yoluyla birbirine kenetlenme biçiminden gelir. Kullanıcı açısından bakıldığında, yan etkiler bilgisayar kullanma noktasıdır. İçinde ne olduğu veya nasıl organize edildiği umurumda değil. Bir şey yaparlar ve bilgisayarın buna göre DEĞİŞMESİNİ beklerler.

4
back2dos

Herhangi bir yan etki, test sırasında dikkate alınması gereken ekstra giriş/çıkış parametreleri sunar.

Bu, kod doğrulamasını çok daha karmaşık hale getirir, çünkü çevre sadece doğrulanmakta olan kodla sınırlandırılamaz, ancak çevreleyen ortamın bir kısmını veya tamamını getirmelidir (güncellenen küresel, o kodda orada yaşar, bu da buna bağlıdır. Bu da tam bir Java EE sunucusu ....) içinde yaşamaya bağlı olan kod

Yan etkilerden kaçınmaya çalışarak kodu çalıştırmak için gereken dışsallık miktarını sınırlamış olursunuz.

2
user1249

Deneyimlerime göre, Nesne Yönelimli programlamada iyi tasarım, yan etkileri olan fonksiyonların kullanımını zorunlu kılmaktadır.

Örneğin, temel bir UI masaüstü uygulamasını alın. Öbek üzerinde benim programın etki alanı modelinin geçerli durumunu temsil eden bir nesne grafiği olan çalışan bir program olabilir. İletiler bu grafikteki nesnelere gelir (örneğin, UI katman denetleyicisinden çağrılan yöntem çağrıları aracılığıyla). Yığındaki nesne grafiği (etki alanı modeli) iletilere yanıt olarak değiştirilir. Modelin gözlemcileri herhangi bir değişiklik hakkında bilgilendirilir, kullanıcı arayüzü ve belki de diğer kaynaklar değiştirilir.

Kötülük olmaktan uzak, bu yığın değiştirici ve ekran değiştirici yan etkilerin doğru düzenlenmesi OO tasarımın (bu durumda MVC modeli) merkezindedir.

Tabii ki, bu yöntemlerinizin arbiter yan etkileri olması gerektiği anlamına gelmez. Yan etkisiz işlevler, kodunuzun okunabilirliğini ve bazen performansını iyileştirmede bir yere sahiptir.

1
flamingpenguin

Yukarıdaki soruların işaret ettiği gibi, işlevsel diller o kadar çok kodun yan etkilere sahip olmasını engellemez çünkü bize hangi yan etkilerin yönetilebileceğine dair araçlar sağlar belirli bir kod parçasında ve ne zaman olur.

Bunun çok ilginç sonuçları olduğu ortaya çıktı. İlk olarak ve en açık şekilde, daha önce açıklanmış olan yan etki ücretsiz koduyla yapabileceğiniz çok sayıda şey var. Ancak, yan etkileri olan kodlarla çalışırken bile yapabileceğimiz başka şeyler de var:

  • Değişken durumdaki kodda, devletin kapsamını, verilen bir fonksiyonun dışına sızmayacağından statik olarak sağlayabilecek şekilde yönetebiliriz, bu da referans sayımı veya işaretleme ve süpürme stili şemaları olmadan çöp toplamamıza olanak tanır. ancak yine de hiçbir referansın hayatta kalmadığından emin olun. Aynı garantiler, gizliliğe duyarlı bilgilerin vb. Muhafaza edilmesi için de yararlıdır. (Bu, haskell'deki ST monad kullanılarak elde edilebilir)
  • Birden çok iş parçacığında paylaşılan durumu değiştirirken, değişiklikleri izleyerek ve bir işlemin sonunda bir atomik güncelleme gerçekleştirerek ya da işlemi geri alıp başka bir iş parçacığı çakışan bir değişiklik yaptıysa tekrarlayarak kilit ihtiyacını önleyebiliriz. Bu, yalnızca kodun durum değişikliklerinden (mutlu bir şekilde vazgeçebileceğimiz) başka hiçbir etkisi olmadığından emin olabiliriz. Bu, Haskell'deki STM (Yazılım İşlemsel Bellek) monad tarafından gerçekleştirilir.
  • kodun etkilerini izleyebilir ve güvenli bir şekilde zımparalayabilir, güvenli olduğundan emin olmak için gerçekleştirmesi gereken etkileri filtreleyebilir, böylece (örneğin) kullanıcı tarafından girilen kodun bir web sitesinde güvenli bir şekilde yürütülmesine izin veririz)
0
Jules

Kötülük biraz üstte .. her şey dilin kullanım bağlamına bağlıdır.

Daha önce bahsedilenler için bir başka husus, işlevsel bir yan etki yoksa, bir programın doğruluğunun kanıtlarını çok daha basit hale getirmesidir.

0
Ilan

Karmaşık kod tabanlarında, yan etkilerin karmaşık etkileşimleri, aklıma gelen en zor şeydir. Kişisel olarak sadece beynimin çalışma şekli göz önüne alındığında konuşabilirim. Yan etkiler ve kalıcı durumlar ve mutasyona uğramış girdiler ve benzeri şeyler, her bir fonksiyonda sadece "ne" olup olmadığını değil, doğrulukla ilgili bir şeyin "ne zaman" ve "nerede" olduğunu düşünmemi sağlıyor.

Sadece "neye" odaklanamıyorum. Kullananlar kod boyunca güvenilirlik havası yaymak yan etkileri neden bir işlevi test ettikten sonra sonuç veremem, çünkü arayanlar yine de yanlış zamanda, yanlış iş parçacığından yanlış yanlış arayarak sipariş. Bu arada, hiçbir yan etkiye neden olmayan ve bir girdi (girişe dokunmadan) verildiğinde yeni bir çıktı döndüren bir işlev bu şekilde kötüye kullanmak neredeyse imkansızdır.

Ama ben pragmatik bir tipim, sanırım ya da en azından olmaya çalışıyorum ve kodumuzun doğruluğunu (en azından en azından) kanıtlamak için tüm yan etkileri mutlaka en düşük seviyeye damgalamamız gerektiğini düşünmüyorum. Bunu C) gibi dillerde yapmak çok zor olurdu. Doğruluk hakkında akıl yürütmeyi çok zor bulduğum yer, karmaşık kontrol akışları ve yan etkiler kombinasyonuna sahip olduğumuz zamandır.

Bana karmaşık kontrol akışları, doğası gereği grafik benzeri, genellikle özyinelemeli veya özyinelemeli olanlardır (olay kuyrukları, örneğin, olayları doğrudan özyinelemeli olarak çağırmayan ancak doğada "özyinelemeye benzer"), belki de bir şeyler yapıyor gerçek bir bağlantılı grafik yapısını çaprazlama veya bizi kod tabanının her türlü farklı kısmına götüren ve farklı yan etkileri tetikleyen olayların eklektik bir karışımını içeren homojen olmayan bir olay kuyruğunu işleme sürecinde. Eğer kodla sonuçlanacak tüm yerleri çizmeye çalışırsanız, karmaşık bir grafiğe benzeyecek ve potansiyel olarak grafikte hiç beklemediğiniz düğümler o anda orada olurdu ve hepsinin olduğu göz önüne alındığında yan etkilere neden olur, bu da sadece hangi işlevlerin adlandırıldığı değil, aynı zamanda hangi yan etkilerin meydana geldiği ve gerçekleşme sırası hakkında da şaşırabileceğiniz anlamına gelir.

İşlevsel diller son derece karmaşık ve özyinelemeli kontrol akışlarına sahip olabilir, ancak sonuç doğruluk açısından anlaşılması çok kolaydır, çünkü süreçte her türlü eklektik yan etki yoktur. Sadece karmaşık kontrol akışları eklektik yan etkilerle karşılaştığında, neler olup bittiğini ve her zaman doğru şeyi yapıp yapmayacağını anlamak için baş ağrısına neden olduğunu düşünüyorum.

Bu tür vakalar olduğunda, bu tür kodların doğruluğu konusunda çok emin hissetmemek, imkansız değilse bile, beklenmedik bir şeye girmeden bu kodda değişiklik yapabileceğime çok güveniyorum. Bu yüzden bana çözüm, kontrol akışını basitleştirin veya yan etkileri en aza indirgeyin/birleştirin (birleştirerek, sistemdeki belirli bir aşamada birçok şeye sadece bir tür yan etkiye neden olmak gibi, iki veya üç veya bir düzine). Simpleton beynimin var olan kodun doğruluğu ve tanıttığım değişikliklerin doğruluğu hakkında güvende hissetmesini sağlamak için bu iki şeyden birine ihtiyacım var. Yan etkiler tekdüze ve kontrol akışı ile birlikte basitse, yan etkilere neden olan kodun doğruluğundan emin olmak oldukça kolaydır:

for each pixel in an image:
    make it red

Böyle bir kodun doğruluğu hakkında mantıklı olmak kolaydır, ancak esas olarak yan etkiler çok düzgün olduğundan ve kontrol akışı çok basittir. Ama diyelim ki böyle bir kodumuz vardı:

for each vertex to remove in a mesh:
     start removing vertex from connected edges():
         start removing connected edges from connected faces():
             rebuild connected faces excluding edges to remove():
                  if face has less than 3 edges:
                       remove face
             remove Edge
         remove vertex

O zaman bu gülünç derecede aşırı basitleştirilmiş sahte koddur, bu genellikle çok daha fazla işlev ve iç içe döngüler ve devam etmesi gereken çok daha fazla şey içerir (çoklu doku haritalarını, kemik ağırlıklarını, seçim durumlarını vb. Güncellemek), ancak sözde kod bile karmaşık grafik benzeri kontrol akışının ve yan etkilerin etkileşimi nedeniyle doğrulukla ilgili neden. Bunu basitleştirmek için bir strateji, işlemi ertelemek ve sadece bir seferde bir tür yan etkiye odaklanmaktır:

for each vertex to remove:
     mark connected edges
for each marked Edge:
     mark connected faces
for each marked face:
     remove marked edges from face
     if num_edges < 3:
          remove face

for each marked Edge:
     remove Edge
for each vertex to remove:
     remove vertex

... basitleştirmenin tekrarı olarak bu etkiye bir şey. Bu, verileri bir çok kez geçirdiğimiz anlamına gelir, bu da kesinlikle bir hesaplama maliyetine neden olur, ancak çoğu zaman bu tür kodları daha kolay bir şekilde çok daha kolay okuyabiliyoruz, şimdi yan etkiler ve kontrol akışları bu tekdüze ve daha basit nitelikte. Ayrıca her döngü, bağlı grafiği geçmekten ve gittikçe yan etkilere neden olmaktan daha fazla önbellek dostu hale getirilebilir (örn: ertelenen geçişleri sıralı sıralı sırayla yapabilmemiz için neyin geçilmesi gerektiğini işaretlemek için bir paralel bit seti kullanın bit maskeleri ve FFS kullanarak). Ama en önemlisi, ikinci versiyonu hataya neden olmadan doğruluk ve değişim açısından akılda tutmak çok daha kolay buluyorum. Bu şekilde yine de bu şekilde yaklaşıyorum ve yukarıda örgü işlemeyi basitleştirmek için aynı tür bir zihniyet uyguluyorum ve olay işlemeyi basitleştiriyoruz ve böylece - homojen yan etkilere neden olan ölü basit kontrol akışlarına sahip daha homojen döngüler.

Ve sonuçta, bir noktada yan etkilere ihtiyacımız var, yoksa sadece veriyi hiçbir yere vermeyen fonksiyonlara sahip olurduk. Genellikle bir şeyi bir dosyaya kaydetmemiz, bir şeyi bir ekrana göstermemiz, verileri bir soket üzerinden göndermemiz, bu tür bir şey ve bunların hepsi yan etkilerdir. Ancak devam eden gereksiz yan etki sayısını kesinlikle azaltabiliriz ve ayrıca kontrol akışları çok karmaşık olduğunda devam eden yan etki sayısını azaltabiliriz ve bence eğer hatalardan kaçınmanın çok daha kolay olacağını düşünüyorum.

0
user204677