Vatansız programlamanın avantajları?


132

Son zamanlarda işlevsel programlama hakkında bilgi ediniyordum (özellikle Haskell, ancak Lisp ve Erlang üzerine de eğitimlerden geçtim). Kavramları çok aydınlatıcı bulsam da "yan etki yok" konseptinin pratik tarafını hala göremiyorum. Bunun pratik avantajları nelerdir? İşlevsel zihniyette düşünmeye çalışıyorum, ancak durumu kolay bir şekilde kaydetme yeteneği olmadan aşırı derecede karmaşık görünen bazı durumlar var (Haskell'in monad'larını 'kolay' olarak görmüyorum).

Haskell'i (veya tamamen işlevsel başka bir dili) derinlemesine öğrenmeye devam etmeye değer mi? İşlevsel veya devletsiz programlama aslında prosedürden daha verimli mi? Haskell'i veya başka bir işlevsel dili daha sonra kullanmaya devam etme olasılığım var mı, yoksa sadece anlamak için mi öğrenmeliyim?

Performansı üretkenlikten daha az önemsiyorum. Bu yüzden, esas olarak işlevsel bir dilde prosedürel / nesne yönelimli / her neyse, daha üretken olup olmayacağımı soruyorum.

Yanıtlar:


168

Kısaca Fonksiyonel Programlamayı Okuyun .

Değil en az olan vatansız programlama, avantajları çok var dramatik okuyuculu ve eşzamanlı kodu. Açıkça söylemek gerekirse, değişken durum çok iş parçacıklı kodun düşmanıdır. Değerler varsayılan olarak değişmez ise, programcıların bir iş parçacığının iki iş parçacığı arasındaki paylaşılan durumun değerini değiştirmesi konusunda endişelenmesine gerek yoktur, bu nedenle yarış koşullarıyla ilgili çok iş parçacıklı hataların tamamını ortadan kaldırır. Yarış koşulları olmadığından, kilit kullanmak için de bir neden yoktur, bu nedenle değişmezlik, kilitlenmelerle ilgili başka bir hata sınıfını da ortadan kaldırır.

İşlevsel programlamanın önemli olmasının en büyük nedeni budur ve muhtemelen işlevsel programlama trenine atlamak için en iyisidir. Ayrıca, basitleştirilmiş hata ayıklama (yani işlevler saftır ve bir uygulamanın diğer bölümlerinde durumu değiştirmez), daha kısa ve ifade edici kod, tasarım modellerine büyük ölçüde bağımlı olan dillere kıyasla daha az standart kod ve derleyici kodunuzu daha agresif bir şekilde optimize edebilir.


5
Ben buna katılıyorum! Paralel programlamaya uygunluğu nedeniyle fonksiyonel programlamanın gelecekte çok daha yaygın kullanılacağına inanıyorum.
Ray Hidayet

@Ray: Dağıtık programlama da eklerdim!
Anton Tykhyy

Hata ayıklama dışında çoğu doğrudur. Haskell'de bu genellikle daha zordur, çünkü gerçek bir çağrı yığınınız yoktur, sadece bir kalıp eşleştirme yığını vardır. Ve kodunuzun ne olduğunu tahmin etmek çok daha zordur.
hasufell

3
Ayrıca, işlevsel programlama gerçekten "vatansız" ile ilgili değildir. Özyineleme zaten örtük (yerel) durumdur ve haskell'de yaptığımız ana şeydir. Deyimsel haskell'de birkaç önemsiz olmayan algoritma uyguladığınızda (örn. Hesaplamalı geometri şeyler) ve bunların hatalarını ayıklamaktan zevk aldığınızda bu netleşir.
hasufell

2
Vatansız ile FP'yi eşitlemekten hoşlanmayın. Birçok FP programı durumla doludur, sadece bir nesneden ziyade bir kapanışta var olur.
mikemaccana

46

Programınızın ne kadar çok parçası devletsiz olursa, hiçbir şeyi bozmadan parçaları bir araya getirmenin o kadar çok yolu vardır . Devletsiz paradigmanın gücü, kendi başına devletsizlikte (veya saflıkta) değil , size güçlü, yeniden kullanılabilir işlevler yazma ve bunları birleştirme becerisinde yatar .

John Hughes'un Why Functional Programming Matters (PDF) adlı makalesinde çok sayıda örnek içeren iyi bir öğretici bulabilirsiniz .

Sen olacak gobs da cebirsel veri türleri ve desen eşleştirme (Caml, SML, Haskell) sahip olduğu fonksiyonel bir dil seçin, özellikle daha üretken.


Mixins de OOP ile benzer şekilde yeniden kullanılabilir kod sağlamaz mı? OOP'yi savunmuyorum, sadece şeyleri kendim anlamaya çalışıyorum.
mikemaccana

20

Diğer cevapların çoğu, çok önemli olduğuna inandığım fonksiyonel programlamanın performans (paralellik) tarafına odaklandı. Bununla birlikte, özellikle üretkenlik hakkında sordunuz, olduğu gibi, aynı şeyi işlevsel bir paradigmada zorunlu bir paradigmadan daha hızlı programlayabilir misiniz?

Aslında (kişisel deneyimlerime dayanarak) F # programlamanın benim daha iyi düşünme şeklimle eşleştiğini ve bu yüzden daha kolay olduğunu buluyorum. Bence en büyük fark bu. Hem F # hem de C # ile programladım ve F # 'da sevdiğim çok daha az "dille mücadele" var. F # 'daki ayrıntıları düşünmek zorunda değilsiniz. İşte gerçekten hoşlandığımı bulduğum birkaç örnek.

Örneğin, F # statik olarak yazılsa bile (tüm türler derleme zamanında çözülür), tür çıkarımı hangi türlere sahip olduğunuzu belirler, böylece söylemek zorunda kalmazsınız. Ve eğer çözemezse, otomatik olarak işlevinizi / sınıfınızı / genel ne olursa olsun yapar. Yani asla genel bir şey yazmanıza gerek yok, hepsi otomatik. Bunun, sorun hakkında düşünmeye daha fazla zaman harcadığım ve onu nasıl uygulayacağımı daha az zaman harcadığım anlamına geldiğini görüyorum. Aslında, C # 'a ne zaman dönsem, bu tür çıkarımları gerçekten özlediğimi anlıyorum, artık yapmanız gerekmeyene kadar bunun ne kadar dikkat dağıtıcı olduğunu asla anlamıyorsunuz.

Ayrıca F # 'da döngü yazmak yerine işlevleri çağırırsınız. Bu ince bir değişiklik, ancak önemli çünkü artık döngü yapısı hakkında düşünmek zorunda değilsiniz. Örneğin, işte geçip bir şeyle eşleşecek bir kod parçası (ne olduğunu hatırlayamıyorum, bir Euler bulmacası projesinden):

let matchingFactors =
    factors
    |> Seq.filter (fun x -> largestPalindrome % x = 0)
    |> Seq.map (fun x -> (x, largestPalindrome / x))

C # 'da bir filtre yapıp sonra bir harita (bu her bir öğenin dönüşümüdür) yapmanın oldukça basit olacağını, ancak daha düşük bir düzeyde düşünmeniz gerektiğini fark ediyorum. Özellikle, döngünün kendisini yazmanız ve kendi açık if ifadenize ve bu tür şeylere sahip olmanız gerekir. F # öğrendiğimden beri, işlevsel bir şekilde kodlamayı daha kolay bulduğumu fark ettim, burada filtrelemek istiyorsanız, "filtre" yazarsınız ve haritalamak istiyorsanız, uygulamak yerine "harita" yazarsınız. ayrıntıların her biri.

F # 'ı ocaml'den ve muhtemelen diğer işlevsel dillerden ayırdığını düşündüğüm |> operatörünü de seviyorum. Bu boru operatörüdür, bir ifadenin çıktısını başka bir ifadenin girdisine "aktarmanıza" izin verir. Kodun daha çok düşündüğümü takip etmesini sağlıyor. Yukarıdaki kod parçacığında olduğu gibi, "faktör sırasını alın, filtreleyin, sonra eşleyin" diyor. Bu çok yüksek bir düşünme seviyesi, ki bunu zorunlu bir programlama dilinde alamazsınız çünkü döngüyü ve if ifadelerini yazmakla çok meşgulsünüz. Başka bir dile gittiğimde en çok özlediğim şey bu.

Yani genel olarak, hem C # hem de F # ile programlayabilsem de, F # kullanmayı daha kolay buluyorum çünkü daha yüksek bir seviyede düşünebilirsiniz. Daha küçük detaylar fonksiyonel programlamadan kaldırıldığı için (en azından F # olarak) daha üretken olduğumu iddia ediyorum.

Düzenleme : İşlevsel bir programlama dilinde bir "durum" örneği istediğini yorumlardan birinde gördüm. F # zorunlu olarak yazılabilir, bu yüzden işte F #'da nasıl değiştirilebilir duruma sahip olabileceğinize dair doğrudan bir örnek:

let mutable x = 5
for i in 1..10 do
    x <- x + i

1
Gönderinize genel olarak katılıyorum, ancak |> tek başına işlevsel programlamayla hiçbir ilgisi yok. Aslında, a |> b (p1, p2)sadece sözdizimsel şekerdir b (a, p1, p2). Bunu doğru çağrışımla birleştirin ve anladınız.
Anton Tykhyy

2
Doğru, muhtemelen F # ile olan olumlu deneyimlerimin çoğunun, işlevsel programlamadan çok F # ile ilgisi olduğunu kabul etmeliyim. Ama yine de ikisi arasında güçlü bir ilişki var ve tür çıkarımı ve |> gibi şeyler kendiliğinden işlevsel programlama olmasa da, kesinlikle "bölgeye uygun olduklarını" iddia ediyorum. En azından genel olarak.
Ray Hidayet

|> başka bir üst düzey infix işlevidir, bu durumda bir işlev uygulama işleci. Kendi üst düzeyinizi tanımlayan infix operatörleri kesinlikle işlevsel programlamanın bir parçasıdır (bir Schemer değilseniz). Haskell'in $ 'ı aynıdır, ancak boru hattındaki bilgiler sağdan sola akar.
Norman Ramsey

15

Hata ayıklamak için uzun süre harcadığınız tüm zor hataları düşünün.

Şimdi, bu hatalardan kaç tanesi bir programın iki ayrı bileşeni arasındaki "istenmeyen etkileşimlerden" kaynaklanıyordu? (Neredeyse tüm iş parçacığı hataları şu biçime sahiptir: paylaşılan verilerin yazılmasını içeren yarışlar, kilitlenmeler, ... Ek olarak, genel durum üzerinde beklenmedik bir etkiye sahip olan kitaplıklar bulmak veya kayıt defterini / ortamı okumak / yazmak, vb. Yaygındır.) I 3 'sert hatadan' en az 1'inin bu kategoriye girdiğini varsayar.

Şimdi, durumsuz / değişmez / saf programlamaya geçerseniz, tüm bu hatalar ortadan kalkar. Bunun yerine bazı yeni zorluklarla sunulmaktadır (örneğin ne zaman yapmak çevre ile etkileştiği için farklı modülleri istiyorum) ama Haskell gibi bir dilde, bu etkileşimler açıkça sadece türüne bakabilirsiniz anlamına tip sistemi, içine şeyleşmiş olsun programın geri kalanıyla sahip olabileceği etkileşim türleri hakkında bir işlev ve neden.

Bu, 'değişmezlik' IMO'nun büyük kazanımıdır. İdeal bir dünyada, hepimiz müthiş API'ler tasarlardık ve her şey değiştirilebilir olduğunda bile, etkiler yerel olacak ve iyi belgelenecek ve 'beklenmeyen' etkileşimler minimumda tutulacaktı. Gerçek dünyada, küresel durumla sayısız şekilde etkileşime giren birçok API vardır ve bunlar en zararlı hataların kaynağıdır. Vatansızlığı hedeflemek, bileşenler arasındaki istenmeyen / örtük / perde arkası etkileşimlerden kurtulmayı amaçlamaktadır.


6
Bir zamanlar birisi, değişken bir değerin üzerine yazmanın, önceki değeri açıkça topladığınız / serbest bıraktığınız anlamına geldiğini söylemişti. Bazı durumlarda programın diğer bölümleri bu değer kullanılarak yapılmadı. Değerler mutasyona uğratılamadığında, bu hata sınıfı da ortadan kalkar.
şekil

8

Durum bilgisi olmayan işlevlerin bir avantajı, işlevin dönüş değerlerinin önceden hesaplanmasına veya önbelleğe alınmasına izin vermeleridir. Hatta bazı C derleyicileri, iyileştirilebilirliklerini artırmak için işlevleri açıkça durumsuz olarak işaretlemenize izin verir. Diğerlerinin de belirttiği gibi, devletsiz işlevlerin paralelleştirilmesi çok daha kolaydır.

Ancak verimlilik tek sorun değildir. Saf bir işlevin test edilmesi ve hata ayıklanması daha kolaydır çünkü onu etkileyen herhangi bir şey açıkça belirtilmiştir. Ve işlevsel bir dilde programlama yaparken, mümkün olduğunca az işlevi "kirli" (G / Ç vb. İle) yapma alışkanlığı edinir. Durum bilgisi olan şeyleri bu şekilde ayırmak, çok işlevsel olmayan dillerde bile programları tasarlamak için iyi bir yoldur.

İşlevsel dillerin "elde edilmesi" biraz zaman alabilir ve bu süreçten geçmemiş birine açıklamak zordur. Ancak yeterince uzun süre direnen çoğu insan, sonunda işlevsel dilleri çok fazla kullanmasalar bile, telaşa değdiğini anlar.


İlk bölüm gerçekten ilginç bir nokta, bunu daha önce hiç düşünmemiştim. Teşekkürler!
Sasha Chedygov

Diyelim ki sin(PI/3)kodunuzda PI bir sabittir, derleyici bu işlevi derleme zamanında değerlendirebilir ve sonucu üretilen koda yerleştirebilir.
Artelius

6

Durum olmadan, kodunuzu otomatik olarak paralel hale getirmek çok kolaydır (CPU'lar gittikçe daha fazla çekirdekle yapıldığından bu çok önemlidir).


Evet, kesinlikle araştırdım. Özellikle Erlang'ın eşzamanlılık modeli çok ilgi çekicidir. Ancak bu noktada, üretkenlik kadar eşzamanlılığı gerçekten önemsemiyorum. Durum olmadan programlamanın üretkenlik bonusu var mı?
Sasha Chedygov

2
@musicfreak, hayır bir verimlilik bonusu yok. Ancak bir not olarak, modern FP dilleri, gerçekten ihtiyacınız varsa state'i kullanmanıza izin verir.
Bilinmiyor

Gerçekten mi? Nasıl yapıldığını görebilmem için işlevsel bir dilde bir durum örneği verebilir misiniz?
Sasha Chedygov

Haskell'deki State Monad'a
göz atın

4
@Unknown: Katılmıyorum. Durum olmadan programlama, farklı bileşenlerin öngörülemeyen / istenmeyen etkileşimlerinden kaynaklanan hataların oluşumunu azaltır. Aynı zamanda daha iyi tasarımı teşvik eder (daha fazla yeniden kullanılabilirlik, mekanizma ile politikanın ayrılması ve benzeri şeyler). Eldeki görev için her zaman uygun değildir, ancak bazı durumlarda gerçekten parlar.
Artelius

6

Daha yüksek trafiğe sahip olmaya başladığınızda durum bilgisiz web uygulamaları çok önemlidir.

Örneğin, güvenlik nedenleriyle istemci tarafında saklamak istemediğiniz çok sayıda kullanıcı verisi olabilir. Bu durumda, sunucu tarafında saklamanız gerekir. Web uygulamaları varsayılan oturumunu kullanabilirsiniz, ancak uygulamanın birden fazla örneğine sahipseniz, her kullanıcının her zaman aynı örneğe yönlendirildiğinden emin olmanız gerekir.

Yük dengeleyiciler genellikle, yük dengeleyicinin, kullanıcıların hangi sunucudan istek göndereceğini bildiği "yapışkan oturumlar" yapma yeteneğine sahiptir. Yine de bu ideal değildir, örneğin web uygulamanızı her yeniden başlattığınızda, tüm bağlı kullanıcıların oturumlarını kaybedeceği anlamına gelir.

Daha iyi bir yaklaşım, web sunucularının arkasındaki oturumu bir tür veri deposunda saklamaktır, bu günlerde bunun için çok sayıda harika nosql ürünü bulunmaktadır (redis, mongo, elasticsearch, memcached). Bu şekilde web sunucuları durum bilgisizdir, ancak yine de durum sunucusu tarafına sahipsiniz ve bu durumun kullanılabilirliği, doğru veri deposu kurulumunu seçerek yönetilebilir. Bu veri depoları genellikle büyük bir yedekliliğe sahiptir, bu nedenle kullanıcıları etkilemeden web uygulamanızda ve hatta veri deponuzda neredeyse her zaman değişiklik yapmak mümkün olmalıdır.


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.