İşlevsel programlama dilleri yan etkilere izin vermiyor mu?


10

Wikipedia'ya göre, Deklaratif olan fonksiyonel programlama dilleri , yan etkilere izin vermiyorlar. Genel olarak deklaratif programlama , yan etkileri en aza indirmeye veya ortadan kaldırmaya çalışır.

Ayrıca, Wikipedia'ya göre, bir yan etki durum değişiklikleriyle ilgilidir. Yani, işlevsel programlama dilleri, bu anlamda, hiçbir durumu kaydetmedikleri için aslında yan etkileri ortadan kaldırırlar.

Ancak, ek olarak, bir yan etkinin başka bir tanımı vardır. Yan etki

bir değer döndürmenin yanı sıra çağırma işlevleriyle veya dış dünyayla gözlemlenebilir bir etkileşime sahiptir. Örneğin, belirli bir işlev bir genel değişkeni veya statik değişkeni değiştirebilir, bağımsız değişkenlerinden birini değiştirebilir, bir istisna oluşturabilir, bir ekrana veya dosyaya veri yazabilir, verileri okuyabilir veya diğer yan etki işlevlerini çağırabilir.

Bu anlamda, Fonksiyonel programlama dilleri aslında yan etkilere izin verir, çünkü dış dünyalarını etkileyen, diğer işlevleri çağıran, istisnalar yaratan, dosyalara yazma vb.

Son olarak, İşlevsel programlama dilleri yan etkilere izin veriyor mu yoksa vermiyor mu?

Ya da neyin "yan etki" olarak nitelendirildiğini anlamıyorum, bu yüzden Zorunlu diller onlara izin veriyor ve Deklaratif izin vermiyor. Yukarıdakilere ve elde ettiğim şeye göre, hiçbir dil yan etkileri ortadan kaldırmaz, bu yüzden ya yan etkiler hakkında bir şey eksik ya da Wikipedia tanımı yanlış geniş.

Yanıtlar:


26

Fonksiyonel programlama birçok farklı teknik içerir. Bazı teknikler yan etkilerle iyidir. Ancak önemli bir husus denklemsel akıl yürütmedir : Eğer aynı değerde bir fonksiyon çağırırsam, daima aynı sonucu alırım. Böylece bir işlev çağrısını döndürme değeri ile değiştirebilir ve eşdeğer davranış elde edebilirim. Bu, özellikle hata ayıklama sırasında program hakkında akıl yürütmeyi kolaylaştırır.

Fonksiyonun yan etkileri varsa, bu tam olarak geçerli değildir. Dönüş değeri fonksiyon çağrısına eşdeğer değildir, çünkü dönüş değeri yan etkileri içermez.

Çözüm, yan etkileri kullanmayı bırakmak ve bu etkileri dönüş değerinde kodlamaktır . Farklı diller farklı efekt sistemlerine sahiptir. Örneğin Haskell, IO veya Durum mutasyonu gibi belirli etkileri kodlamak için monad kullanır. C / C ++ / Rust dilleri, bazı değerlerin mutasyonuna izin vermeyen bir tür sistemine sahiptir.

Zorunlu bir dilde, bir print("foo")işlev bir şey yazdırır ve hiçbir şey döndürmez. Haskell gibi saf işlevsel bir dilde, bir printişlev aynı zamanda dış dünyanın durumunu temsil eden bir nesneyi alır ve bu çıktıyı gerçekleştirdikten sonra durumu temsil eden yeni bir nesne döndürür. Benzer bir şey newState = print "foo" oldState. Eski eyaletten istediğim kadar yeni eyalet oluşturabilirim. Ancak, ana işlev tarafından yalnızca bir tanesi kullanılacaktır. Bu yüzden fonksiyonları zincirleyerek durumları birden çok eylemden sıralamam gerekiyor. Yazdırmak için foo barbenzer bir şey söyleyebilirim print "bar" (print "foo" originalState).

Bir çıkış durumu kullanılmazsa, Haskell tembel bir dil olduğu için bu duruma giden eylemleri gerçekleştirmez. Tersine, bu tembellik sadece mümkündür, çünkü tüm efektler açıkça dönüş değerleri olarak kodlanmıştır.

Haskell'in bu rotayı kullanan tek yaygın olarak kullanılan işlevsel dil olduğunu unutmayın. Diğer fonksiyonel diller dahil. Lisp ailesi, ML ailesi ve Scala gibi yeni işlevsel diller cesaretini kırıyor, ancak yine de yan etkilere izin veriyor - bunlara zorunlu-fonksiyonel diller denilebilir.

G / Ç için yan etkiler kullanmak muhtemelen iyidir. Genellikle, G / Ç (günlük kaydı dışında) yalnızca sisteminizin dış sınırında yapılır. İş mantığınızda dış iletişim olmaz. Daha sonra bir dış kabukta saf G / Ç işlemi gerçekleştirirken, yazılımınızın çekirdeğini saf bir tarzda yazmak mümkündür. Bu ayrıca çekirdeğin vatansız olabileceği anlamına gelir.

Vatansızlık, artan mantıksallık ve ölçeklenebilirlik gibi bir dizi pratik avantaja sahiptir. Bu web uygulaması arka uçları için çok popüler. Her durum dışarıda, paylaşılan bir veritabanında tutulur. Bu yük dengelemeyi kolaylaştırır: Oturumları belirli bir sunucuya yapıştırmak zorunda değilim. Daha fazla sunucuya ihtiyacım olursa ne olur? Sadece bir tane daha ekleyin, çünkü aynı veritabanını kullanıyor. Bir sunucu çökerse ne olur? Bekleyen istekleri başka bir sunucuda yeniden yapabilirim. Tabii ki, hala - devlet veritabanında. Ama ben bunu açıkça belirttim ve çıkardım ve eğer istersem dahili olarak saf işlevsel bir yaklaşım kullanabilirdim.


Detaylı cevap için teşekkürler. Sonuç olarak tuttuğum şey, yan etkilerin denklemsel akıl yürütme nedeniyle fonksiyon değerini etkilememesidir, bu yüzden "fonksiyonel diller yan etkilere izin vermez / en aza indirmez". İşlev değerlerine katıştırılmış efektler, kaydedilen veya programın çekirdeğinin dışında kaydedilen durumu etkiler ve değiştirir. Ayrıca, I / O iş mantığının dış sınırında olur.
codebot

3
@codebot, Bence tam olarak değil. Düzgün uygulanırsa, işlevsel programlamadaki yan etkiler işlevin dönüş türüne yansıtılmalıdır. Örneğin, bir işlev başarısız olursa (belirli bir dosya yoksa veya veritabanı bağlantısı kurulamıyorsa), işlevin bir istisna atmak yerine işlevin dönüş türünün hatayı kapsaması gerekir. Bir örnek için Demiryolu Odaklı Programlamaya bir göz atın ( fsharpforfunandprofit.com/posts/recipe-part2 ).
Aaron M. Eshbach

“... bunlara zorunlu-işlevsel diller denilebilir.”: Simon Peyton Jones “... Haskell dünyanın en iyi zorunlu programlama dilidir” diye yazdı.
Giorgio

5

Hiçbir programlama dili yan etkileri ortadan kaldırmaz . Zorunlu diller içermezken, bildirici dillerin yan etkiler içerdiğini söylemek daha iyi olur . Ancak, yan etkilerle ilgili bu konuşmalardan herhangi birinin iki dil türü arasındaki temel farktan o kadar emin değilim ve gerçekten aradığınıza benziyor.

Farkı bir örnekle açıklamaya yardımcı olduğunu düşünüyorum.

a = b + c

Yukarıdaki kod satırı hemen hemen her dilde yazılabilir, bu yüzden zorunlu veya bildirimsel bir dil kullanıp kullanmadığımızı nasıl belirleyebiliriz? Bu kod satırının özellikleri iki dil sınıfında nasıl farklıdır?

Zorunlu bir dilde (C, Java, Javascript & c.) Bu kod satırı yalnızca bir işlemdeki bir adımı temsil eder. Bize herhangi bir değerin temel doğası hakkında hiçbir şey söylemez. Bize bu kod satırından sonra (ama bir sonraki satırdan önce) aeşit bartı olacağını csöyler ama bize adaha geniş anlamda bir şey söylemez .

Bildirici bir dilde (Haskell, Scheme, Excel, vb.) Bu kod satırı çok daha fazlasını söylüyor. Bu arasında değişmez bir ilişki kurar ave diğer iki de ki gibi nesneleri her durumda aeşittir bartı c. Excel'i bildirici diller listesine eklediğime dikkat edin, çünkü değeri değiştirse bveya cdeğiştirse bile , gerçek yine ade toplamlarına eşit olacak.

Bana göre bu , iki tür dili farklı kılan yan etkiler veya durum değil. Zorunlu bir dilde, belirli bir kod satırı, söz konusu değişkenlerin genel anlamı hakkında hiçbir şey söylemez. Başka bir deyişle, a = b + csadece çok kısa bir süre aiçin bve ' nin toplamına eşit olduğu anlamına gelir c.

Bu arada, açıklayıcı dillerde her kod satırı, programın tüm ömrü boyunca var olacak temel bir gerçeği oluşturur. Bu dillerde, a = b + ckodunun başka hattında ne olursa olsun o size söyler ahep toplamına eşit olacak bve c.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.