İşlevsel Programlama savunucuları bu ifadeye Kod Tamamlama'da nasıl cevap verebilir?


30

İkinci baskının 839. sayfasında, Steve McConnell, programcıların büyük programlarda "karmaşıklığı ele geçirme" yollarını tartışıyor. Tüyoları bu ifadeyle sonuçlandı:

"Nesneye yönelik programlama , aynı zamanda algoritmalar ve veriler için geçerli olan bir soyutlama düzeyi sağlar , yalnızca işlevsel ayrışmanın tek başına sağlamadığı bir tür soyutlama sağlar."

"Karmaşıklığın azaltılması tartışmalı olarak etkili bir programcı olmanın en önemli anahtarıdır" (aynı sayfa) olduğu sonucuyla birleştiğinde, bu durum işlevsel programlama için oldukça zorlu görünüyor.

FP ve OO arasındaki tartışma genellikle, özellikle eşzamanlılık veya paralelleştirme zorluklarından türetilen karmaşıklık meseleleri etrafındaki FP savunucuları tarafından çerçevelenir. Ancak eşzamanlılık kesinlikle programcıların fethetmesi gereken tek karmaşıklık yazılımı değildir. Belki bir çeşit karmaşıklığı azaltmaya odaklanmak, diğer boyutlarda onu büyük ölçüde arttırır, öyle ki çoğu durumda kazancınız maliyete değmez.

FP ve OO arasındaki karşılaştırma şartlarını eşzamanlılık veya yeniden kullanılabilirlik gibi belirli konulardan küresel karmaşıklığın yönetimine kaydırırsak, bu tartışma nasıl görünür?

DÜZENLE

Vurgulamak istediğim karşıtlık, OO'nun hem veri hem de algoritmaların karmaşıklığını kapsamaya ve soyutlamaya başlamış gibi görünmesine karşın, işlevsel programlama, program boyunca veri yapılarının uygulama ayrıntılarını daha "ortaya çıkaran" bırakmaya teşvik ediyor gibi görünüyor.

Bakınız, mesela, Stuart Halloway (a Clojure FP savunucusu) burada "veri türleri aşırı şartname" "deyimsel OO tarzı negatif sonucu" olduğunu söyleyerek ve basit bir vektörü olarak bir AddressBook kavramsallaştırma ya da bunun yerine daha zengin OO nesnesinin haritasına lehine ek (vectorish olmayan ve maplike olmayan) özellikler ve yöntemler ile. (Ayrıca, OO ve Etki Alanına Dayalı Tasarım taraftarları, bir Adres Defterini bir vektör veya harita olarak göstermenin, kapsüllenmiş verileri, etki alanı açısından alakasız veya hatta tehlikeli olan yöntemlere maruz bıraktığını söyleyebilirler).


3
Sorunun oldukça çelişkili bir şekilde çerçevelenmesine rağmen +1, bu iyi bir soru.
mattnz

16
Cevaplarda da belirtildiği gibi, İşlevsel ayrışma ve İşlevsel programlama iki farklı hayvandır. Bu nedenle, “bu, işlevsel programlamaya oldukça zor görünüyor” sonucunun açıkça yanlış olduğu, bunun onunla hiçbir ilgisi olmadığı sonucuna varıldı.
Fabio Fracassi

5
Açıkçası McConnel'ın modern fonksiyonel veri tipi sistemler ve birinci sınıf birinci sınıf modüller konusundaki bilgileri biraz yamalı. İfadesi tamamen saçmalık, çünkü birinci sınıf modülleri ve functorları (SML'ye bakınız), sınıfları yazdığımızı (bkz. Haskell). OO'nun düşünme biçiminin saygılı bir tasarım metodolojisinden çok bir din olduğuna sadece bir başka örnek. Ve bu arada, eşzamanlılıkla ilgili bu şeyi nereden buldun? İşlevsel programcıların çoğu paralelliğe hiç aldırış etmiyorlar.
SK-mantığı

6
@ SK-mantık Tüm McConnell, "tek başına işlevsel ayrışmanın", OOP ile aynı soyutlama araçlarını sağlamadığını söyledi, bu bana oldukça güvenli bir ifade gibi görünüyor. Hiçbir yerde, FP dillerinin OOP kadar güçlü soyutlama araçlarına sahip olmadığını söylemez. Aslında FP dillerinden hiç bahsetmiyor. Bu sadece OP'nin yorumu.
sepp2k

2
@ sepp2k, tamam, anlıyorum. Ancak yine de, modüllerin davranışını simüle ederek, neredeyse saf lambda hesabı için işlevsel bir ayrıştırma dışında, hiçbir şeyin üstüne çok karmaşık ve iyi katmanlı bir veri yapıları ve işleme soyutlamaları sistemi kurulabilir. OO soyutlamalarına hiç gerek yok.
SK-mantığı

Yanıtlar:


13

Kitabın 20 yıldan uzun bir süredir yazıldığını unutmayın. Günün profesyonel programcıları için FP yoktu - tamamen akademisyenler ve araştırmacılar dünyasındaydı.

İşin uygun bağlamında "işlevsel ayrışmayı" çerçevelemeliyiz. Yazar işlevsel programlamadan bahsetmiyor. Bunu “yapılandırılmış programlama” ya da GOTOondan önce gelen doldurulmuş karışıklığa bağlamamız gerekiyor. Referans noktanız eski bir FORTRAN / COBOL / BASIC ise, işlevleri olmayan (belki de şanslıysanız GOSUB seviyesini alırsınız) ve tüm değişkenleriniz globaldir, programınızı bozabilir fonksiyonların katmanları içine büyük bir nimet olduğunu.

OOP, bu tür 'işlevsel ayrışma' üzerine bir başka ayrıntılandırmadır. Komutları sadece fonksiyonlarda bir araya getirmekle kalmaz, aynı zamanda ilgili fonksiyonları üzerinde çalıştıkları verilerle gruplayabilirsiniz . Sonuç, verilerinizde başka neler çalışabileceğini bulmak için kod tabanınızın her yerinde kovalamak zorunda kalmadan bakabileceğiniz ve anlayabileceğiniz (ideal olarak) açıkça tanımlanmış bir kod parçasıdır.


27

İşlevsel programlama destekçilerinin çoğu FP dilinin "yalnızca işlevsel ayrıştırma" dan daha fazla soyutlama aracı sağladığını ve aslında Nesne Yönelimli Dillerinkilerle karşılaştırılabilir soyutlama araçlarına izin verdiğini iddia edeceğini düşünüyorum. Örneğin, Haskell'in tip sınıflarını veya ML'nin yüksek dereceli modülleri bu tür soyutlamalar olarak gösterebilir. Bu nedenle (eminim nesne yönelimi ile işlemsel programlama değil işlevsel programlama hakkındaydı) ifadesi onlar için geçerli değildir.

Ayrıca, FP ve OOP'nin dikgen kavramlar olduğu ve karşılıklı olarak dışlanmadıkları belirtilmelidir. Bu yüzden onları birbirleriyle karşılaştırmanın anlamı yoktur. "İşlemsel OOP" (örneğin Java) ve "işlevsel OOP" (örneğin Scala) ile çok iyi bir şekilde karşılaştırabilirsiniz, ancak alıntı yaptığınız ifade bu karşılaştırma için geçerli olmaz.


10
+1 "İşlevsel ayrışma"! = "İşlevsel programlama". Birincisi, (veya sadece elle sarılmış) kalıtım, kapsülleme ve polimorfizm olmadan vanilya veri yapıları kullanan klasik sıralı kodlamaya dayanır. İkinci lambda hesabını kullanarak çözümleri ifade eder. İki tamamen farklı şeyler.
İkili Korku

4
Özür dilerim, ama "prosedürel programlama" ifadesi inatla daha erken aklıma gelmeyi reddetti. Bana "İşlevsel ayrışma", işlevsel programlamaya göre prosedürel programlamanın göstergesidir.
İkili Korku

Evet haklısın. İşlevsel Programlamanın, aynı basit veri yapıları (listeler, ağaçlar, haritalar) üzerinde tekrar tekrar çalışan tekrar kullanılabilir işlevleri tercih ettiğini ve aslında bunun OO üzerinde bir satış noktası olduğunu iddia ettim. “Veri türlerinin fazla belirtilmesi” nin “deyimatik OO stilinin olumsuz bir sonucu” olduğunu söyleyen Stuart Halloway'a (Clojure FP savunucusu) bakın ve bir Adres Defterini diğerleriyle daha zengin bir OO nesnesi yerine bir vektör veya harita olarak kavramlaştırmayı tercih edin (olmayan -vektör ve non-maplike) özellikleri ve yöntemleri.
dan

Stuart Halloway'ın alıntı için link: thinkrelevance.com/blog/2009/08/12/…
dan

2
@ dan Dinamik olarak yazılmış bir dilde nasıl yapılır belki Clojure (Bilmiyorum, Clojure kullanmıyorum), ama genel olarak FP'de böyle yapılacağı sonucuna varmanın tehlikeli olduğunu düşünüyorum. Örneğin Haskell insanları soyut türler ve bilgi gizleme konusunda çok büyük görünüyorlar (belki de Java kadar değil).
sepp2k

7

İşlevsel programlamayı karmaşıklığı yönetmede son derece yararlı buluyorum. Karmaşıklık hakkında farklı düşünmeyi düşünüyorsunuz, bunu bir OOP anlamında kapsülleme yerine farklı seviyelerde değişmez veriler üzerinde etki eden fonksiyonlar olarak tanımlayabilirsiniz.

Örneğin yakın zamanda Clojure'da bir oyun yazdım ve oyunun tamamı tek bir değişmez veri yapısında tanımlandı:

(def starting-game-state {:map ....
                          :player ....
                          :weather ....
                          :other-stuff ....}

Ve ana oyun döngüsü, bazı saf fonksiyonların bir oyun döngüsünde oyun durumuna uygulanması olarak tanımlanabilir:

 (loop [initial-state starting-game-state]
   (let [user-input (get-user-input)
         game-state (update-game initial-state user-input)]
     (draw-screen game-state)
     (if-not (game-ended? game-state) (recur game-state))))

Çağrılan anahtar işlevi update-game, önceki bir oyun durumu ve bazı kullanıcı girişi verilen bir simülasyon adımını çalıştıran ve yeni oyun durumunu döndüren işlevdir .

Peki karmaşıklık nerede? Bana göre oldukça iyi yönetildi:

  • Kesinlikle güncelleme oyunu işlevi çok fazla iş yapar, ancak diğer işlevleri bir araya getirerek oluşturulmuştur, bu yüzden aslında oldukça basit bir işlemdir. Birkaç seviyeye indiğinizde, fonksiyonlar hala oldukça basittir ve "harita döşemesine nesne ekleme" gibi bir şey yapar.
  • Kesinlikle oyun durumu büyük bir veri yapısıdır. Fakat yine de, daha düşük seviyeli veri yapıları oluşturarak oluşturulmuştur. Ayrıca gömülü herhangi bir yöntem veya sınıf tanımına ihtiyaç duymak yerine "saf veri" dir (eğer istersen bunu çok etkili bir JSON nesnesi olarak düşünebilirsin) bu yüzden çok az kazan var.

OOP, karmaşıklığı enkapsülasyon yoluyla da yönetebilir, ancak bunu OOP ile karşılaştırırsanız, işlevselliğin çok büyük avantajları vardır:

  • Oyun durumu veri yapısı değişkendir, bu nedenle birçok işlem kolayca paralel olarak yapılabilir. Örneğin, oyun ekranından oyun mantığından farklı bir iş parçacığına ekran oluşturma çağrısı yapmak tamamen güvenlidir - birbirlerini etkileyemezler veya tutarsız bir durum göremezler. Bu, büyük bir değişken nesne grafiği ile şaşırtıcı derecede zordur
  • İstediğiniz zaman oyun durumunun anlık görüntüsünü alabilirsiniz. Replay'ler önemsizdir (Clojure'un kalıcı veri yapıları sayesinde, verilerin çoğu paylaşıldığı için kopyalar neredeyse hiç hafıza almaz). Ayrıca AI'nın farklı hareketleri değerlendirmesine yardımcı olmak için "geleceği tahmin etmek" için güncelleme oyununu çalıştırabilirsiniz.
  • Hiçbir yerde, katı bir sınıf heirarşi tanımlamak gibi OOP paradigmasına uyması için herhangi bir zorlu takas yapmam gerekmedi. Bu anlamda, işlevsel veri yapısı daha esnek bir prototip tabanlı sistem gibi davranır.

Son olarak, işlevselliğe ve OOP dillerinde karmaşıklığın nasıl yönetileceği hakkında daha fazla bilgi edinmek isteyen insanlar için, Rich Hickey'in ana konuşması Simple Made Easy ( Strange Loop teknolojisi konferansında çekildi) ana konuşması videosunu şiddetle tavsiye ediyorum.


2
Bir oyunun zorla taklit edilemezliğin sözde "faydalarını" göstermek için mümkün olan en kötü örneklerden biri olduğunu düşünüyorum. Bir oyunda işler sürekli değişiyor, bu da oyun durumunuzu her zaman yeniden inşa etmeniz gerektiği anlamına geliyor. Ve eğer her şey değişmez ise, bu sadece oyun durumunu yeniden inşa etmek zorunda değil, aynı zamanda bir referansı olan ya da tümüyle geri dönüşüme girene kadar tekrarlı bir şekilde bir referansı olan ya da buna atıfta bulunan grafikteki her şeyi tekrarlamanız gerektiği anlamına gelir. programa 30'dan fazla FPS programı, tonlarca GC çalkalama! Bundan iyi bir performans elde etmenin yolu yok ...
Mason Wheeler

7
Tabii ki oyunlar değişmezlik konusunda zor - bu yüzden hala işe yarayabileceğini göstermek için seçtim! Bununla birlikte, kalıcı veri yapılarının neler yapabileceğini görünce şaşıracaksınız - oyun durumunun çoğunun yeniden yapılması gerekmiyor, sadece değişen şeyler. Ve bazı ek yükler olduğundan emin olun, ancak bu sadece küçük bir sabit faktördür. Bana yeterince çekirdek ver ve tek dişli C ++ oyun motorunu
yeneceğim

3
@Mason Wheeler: Aslında olduğunu neredeyse eşit almak mümkün çok hiç GC olmadan değişmez nesnelerle performans, (mutasyon olduğu gibi iyi gibi). Clojure'deki numara kalıcı veri yapıları kullanıyor : programlayıcıya göre değişken değiller ama aslında başlık altında değişiyorlar. Her iki dünyanın en iyisi.
Joonas Pulakka

4
@quant_dev Daha çekirdekler daha iyi çekirdek ... daha ucuzdur escapistmagazine.com/news/view/...
deworde

6
@quant_dev - bu bir bahane değil, performansınızı çekirdek sayıları ile doğrusal olarak ölçeklendirerek telafi etmek için sabit bir ek yüke sahip olmanızın daha iyi olduğu matematiksel ve mimari bir gerçektir. İşlevsel dillerin nihayetinde üstün performans sunmasının nedeni, tek çekirdekli performans çizgisinin sonuna gelmemiz ve bunların hepsi de eşzamanlılık ve paralellik ile ilgili olacaktır. Bu çalışmanın yapılmasında fonksiyonel yaklaşımlar (ve özellikle değişmezlik) önemlidir.
mikera

3

"Nesneye yönelik programlama, aynı zamanda algoritmalar ve veriler için geçerli olan bir soyutlama düzeyi sağlar, yalnızca işlevsel ayrışmanın tek başına sağlamadığı bir tür soyutlama sağlar."

İşlevsel ayrıştırma tek başına herhangi bir algoritma veya program yapmak için yeterli değildir: verileri de temsil etmeniz gerekir. Yukarıdaki ifadenin, işlevsel durumdaki "verinin" ilkel türden olduğunu (yani en basit şekilde) olduğunu ima ettiği (en azından anlaşılabileceği gibi) olduğunu düşünüyorum: sadece sembollerin listesi ve başka hiçbir şey yok. Böyle bir dilde programlama açıkça pek uygun değildir. Bununla birlikte, birçok, özellikle Clojure gibi yeni ve modern, işlevsel (veya çok paradigma) diller zengin veri yapıları sunar: yalnızca listeler değil, aynı zamanda dizeler, vektörler, haritalar ve kümeler, kayıtlar, yapılar - ve nesneler! - meta veri ve polimorfizm ile.

OO soyutlamalarının devasa pratik başarısı pek tartışılmaz. Ama bu son söz mü? Yazdığınız gibi, eşzamanlılık sorunları zaten en büyük acı ve klasik OO hiç bir eşzamanlılık fikri içermiyor. Sonuç olarak, eşzamanlılıkla uğraşmak için fiili OO çözümleri sadece üst üste bindirilmiş koli bandıdır: çalışır, ancak sökülmesi kolaydır, eldeki asıl görevden önemli miktarda beyin kaynağı alır ve iyi ölçeklenemez. Belki birçok dünyanın en iyisini elde etmek mümkündür. Modern çoklu paradigma dillerinin peşinde olduğu şey bu.


1
Ben bir yerde "büyük OO, küçük FP" ifadesini duydum - Ben Michael Feathers alıntı. Yani, bu FP büyük bir programın belirli bölümleri için iyi olabilir, fakat genel olarak OO olması gerekir.
dan

Ayrıca, her şey için Clojure kullanmak yerine, daha geleneksel OO sözdiziminde daha net ifade edilen şeyler bile, daha temiz olduğu yerlerde veri işleme bitleri için Clojure kullanmaya ve diğer bitler için Java veya başka bir OO dili kullanmaya ne dersiniz. Programın tüm bölümlerinde aynı dilde çoklu dil programlama yerine çoklu dil programlama. (Çoğu web uygulamasının farklı katmanlar için SQL ve OO'ları nasıl kullandığı gibi sıralayın.)?
dan

@ dan: İşe en uygun aracı kullanın. Polyglot programlamada, en önemli faktör diller arasında uygun iletişimdir ve Clojure ve Java birlikte zor oynayabilirler . En önemli Clojure programlarının burada ve orada en azından JDK'nın standart Java kitaplıklarının bazı parçalarını kullandığına inanıyorum.
Joonas Pulakka

2

Değişken durum, programlama ve yazılım / sistem tasarımı ile ilgili çoğu karmaşıklığın ve problemin köküdür.

OO değişken durumu kucaklar. FP değişken durumdan nefret eder.

Hem OO hem de FP kendi kullanımlarına ve tatlı noktalarına sahiptir. Akıllıca seçim. Ve atasözünü hatırlayın: "Kapaklar zavallı adamın nesneleridir. Nesneler fakir adamın kapanışıdır."


3
Açılış iddianın doğru olduğundan emin değilim. Karmaşıklıkların "en" kökü? Yaptığım ya da gördüğüm programlamada, sorun soyutlama eksikliği ve kod boyunca bir ayrıntı fazlalığı olarak çok değişken bir durum değildir.
dan

1
@Dan: İlginç. Aslında tam tersini gördüm: Sorunlar aşırı soyutlamanın kullanımından kaynaklanıyor, bu da hem olanları hem de gerçekte neler olup bittiğinin ayrıntılarını düzeltmeyi zorlaştırıyor.
Mason Wheeler

1

İşlevsel Programlamanın nesneleri olabilir, ancak bu nesneler değişmez olma eğilimindedir. Saf fonksiyonlar (yan etkisi olmayan fonksiyonlar) daha sonra bu veri yapılarında çalışır. Nesne yönelimli programlama dillerinde değişmez nesneler yapmak mümkündür, ancak bunu yapmak için tasarlanmamışlardır ve kullanma eğilimi bu değildir. Bu, nesneye yönelik programlar hakkında akıl yürütmeyi zorlaştırır.

Çok basit bir örnek alalım. Diyelim ki Oracle, Java Dizelerinin ters yöntem kullanması gerektiğine karar verdi ve siz de aşağıdaki kodu yazdınız.

String x = "abc";
StringBuffer y = new StringBuffer(x);
y.reverse();
x.reverse();
x.toString().equals(y.toString());

Son satır neye değer veriyor? Bunun yanlış olarak değerlendirileceğini bilmek için String sınıfının özel bilgisine ihtiyacınız var.

Ya kendi sınıfımı WuHoString yaptıysam

String x = "abc";
WuHoString y = new WuHoString(x);
y.reverse();
x.reverse();
x.toString().equals(y.toString())

Son çizginin neye benzediğini bilmek imkansız.

İşlevsel Programlama tarzında aşağıdaki gibi daha çok yazılır:

String x;
equals(toString(reverse(x)), toString(reverse(WuHoString(x))))

ve doğru olmalı.

En temel sınıflardan birindeki 1 fonksiyonun akla gelmesi çok zorsa, o zaman bu değişken nesneler fikrini ortaya koyarsanız, karmaşıklığı arttırmış veya azaltmış olursunuz.

Açıkçası, nesne yönelimini neyin oluşturduğunun ve işlevsel olmanın ne anlama geldiğinin ve her ikisinin de ne anlama geldiğinin her türlü tanımı vardır. Bana göre, birinci sınıf işlevler gibi şeyler içermeyen ama başka diller için yapılmış dilbiliminde "işlevsel bir programlama stiline" sahip olabilirsiniz.


3
OO dillerinin değişmez nesneler için oluşturulmadığını ve daha sonra dizelerle bir örnek kullandığınızı söylemeniz biraz komik (Java da dahil olmak üzere çoğu OO dilinde değişmez). Ayrıca, değiştirilemeyen nesnelere vurgu yapan OO (ya da daha çok paradigma) dilleri olduğunu belirtmeliyim (örneğin Scala).
sepp2k

@ sepp2k: Buna alış. FP savunucuları, her zaman garip, gerçek dünya kodlaması ile ilgisi olmayan, tartışmalı örneklerin etrafında atıyorlar. Zorla değişmezlik gibi temel FP kavramlarını iyi görünmenin tek yolu budur.
Mason Wheeler

1
@Mason: Huh? Değişmezliğin iyi görünmesini sağlamanın en iyi yolu "Java (ve C #, python, vb.) Değişmez dizeleri kullanır ve harika çalışır" demek değil midir?
sepp2k

1
@ sepp2k: Değişmez dizeler çok iyi çalışıyorsa, neden StringBuilder / StringBuffer stil sınıfları her yerde görünmeye devam ediyor? Bu, yolunuza çıkan soyutlama inversiyonunun bir başka örneği.
Mason Wheeler

2
Nesneye yönelik birçok dil değiştirilemez nesneler yapmanızı sağlar. Ancak metotları sınıfa bağlama kavramı benim açımdan gerçekten cesareti kırmaktadır. String örneği gerçekten tartışılan bir örnek değil. Java'da herhangi bir mehtod çağırdığımda, parametrelerimin bu işlev içinde değişip değişmeyeceği konusunda bir şans alıyorum.
WuHoUnited

0

Bence çoğu zaman klasik OOP soyutlaması eşzamanlılık karmaşıklığını kapsamıyor. Bu nedenle OOP (orijinal anlamıyla) FP'yi dışlamaz ve bu yüzden scala gibi şeyleri görürüz.


0

Cevap dile bağlıdır. Lisps, örneğin, kod düzgün gerçekten düzgün olması ise aslında sadece Lisp listeleri yazma algoritmaları - veri! Verileri, programı yazdığınız gibi saklarsınız. Bu soyutlama, eşzamanlı olarak OOP'tan daha basit ve daha ayrıntılıdır ve gerçekten güzel şeyler yapmanıza izin verir (makroları kontrol edin).

Haskell (ve benzer bir dil, sanırım) tamamen farklı bir cevabı var: cebirsel veri türleri. Bir cebirsel veri türü bir Cyapıya benzer, ancak daha fazla seçeneğe sahiptir. Bu veri türleri, verileri modellemek için gereken soyutlamayı sağlar; fonksiyonlar, algoritmaları modellemek için gereken soyutlamayı sağlar. Tip sınıfları ve diğer gelişmiş özellikler, her ikisinde de daha yüksek bir soyutlama düzeyi sağlar.

Örneğin, eğlence için TPL adlı bir programlama dili üzerinde çalışıyorum. Cebirsel veri türleri değerleri göstermeyi gerçekten kolaylaştırır:

data TPLValue = Null
              | Number Integer
              | String String
              | List [TPLValue]
              | Function [TPLValue] TPLValue
              -- There's more in the real code...

Ne bu diyor - Çok görsel bir şekilde - bir TPLValue (dilimde herhangi bir değer) bir olabilmesidir Nullveya Numberbir ile Integerdeğer veya hatta bir Functiondeğerler (parametreler) listesiyle ve nihai değere (vücut ).

Sonra bazı ortak davranışları kodlamak için type sınıflarını kullanabilirim. Örneğin, bir dize dönüştürülebilir anlamına gelir TPLValueve bunun örneği Showyapabilir.

Ek olarak, bazı tiplerin davranışını (kendimi uygulamadığımlar da dahil) belirtmem gerektiğinde kendi tip sınıflarımı kullanabilirim. Örneğin, Extractablea alan TPLValueve uygun normal bir değer döndüren bir işlev yazmama izin veren bir tür sınıfım var . Bu nedenle extract, bir dönüştürme Numberbir etmek Integerya da bir Stringa Stringuzun kadar Integerve Stringörnekleridir Extractable.

Son olarak, benim programın ana mantık gibi çeşitli fonksiyonlarda olduğu evalve apply. Bunlar gerçekte çekirdektir - durumları ve hataları ele almanın yanı sıra, TPLValues'yi alır ve daha fazla TPLValues'a dönüştürürler .

Genel olarak, Haskell kodumda kullandığım soyutlamalar aslında OOP dilinde kullandıklarımdan daha güçlü.


Evet, sevmeliyim eval. “Hey, bana bak! Kendi güvenlik deliklerimi yazmaya ihtiyacım yok; doğrudan programlama diline yerleştirilmiş bir rasgele kod yürütme güvenlik açığı var !” Verileri kodla birleştirmek, tüm zamanların en popüler iki güvenlik açığı sınıfından birinin temel nedenidir. Bir SQL enjeksiyon saldırısı nedeniyle birinin saldırıya uğradığını gördüğünüzde (diğer birçok şeyin yanı sıra) bunun nedeni, bazı programcıların verileri koddan nasıl düzgün bir şekilde ayıracağını bilmemesidir.
Mason Wheeler

evalLisp'in yapısına pek bağlı değil - evalJavaScript ve Python gibi dillerde olabilir . Asıl güç, temelde veri gibi programlar üzerinde etki yapan ve diğer programları çıkaran programlar olan makrolar yazmaktır. Bu, dili çok esnek kılar ve güçlü soyutlamalar yaratır.
Tikhon Jelvis

3
Evet, "Makrolar harika" konuşmasını daha önce defalarca duydum. Ancak, 1) pratik ve gerçek dünya kodunda yapmak isteyeceğiniz ve 2) fonksiyonlarını destekleyen herhangi bir dilde kolayca gerçekleştirilemeyecek bir şeyi yapan gerçek bir Lisp makrosu örneği görmedim.
Mason Wheeler

1
@MasonWheeler kısa devre yapıyor and. kısa devre or. let. let-rec. cond. defn. Bunların hiçbiri, geçerli emir dillerindeki işlevlerle uygulanamaz. for(liste kavramalar). dotimes. doto.

1
@MattFenwick: Tamam, gerçekten yukarıdaki iki benim için üçüncü bir nokta eklemeliydim: 3) zaten herhangi bir aklı başında bir programlama diline yerleşik değildir . Çünkü şimdiye kadar gördüğüm tek gerçekten yararlı makro örnekleri ve "Hey bana bak, dilim o kadar esnek ki kendi kısa devremi uygulayabiliyorum and!" "Hey bana bak, dilim o kadar sakattı ki, kısa devre ile gelmiyor andve her şey için tekerleği yeniden icat etmeliyim !"
Mason Wheeler,

0

Alıntılanan cümlenin, görebildiğim kadarıyla artık bir geçerliliği yok.

Çağdaş OO dilleri, cinsi * olmayan türler üzerinde soyut olamaz, yani yüksek türler bilinmemektedir. Tip sistemleri, "elemanlar üzerinde bir fonksiyonun haritalanmasına izin veren, Int elemanlı bazı kaplar" fikrinin ifade edilmesine izin vermez.

Dolayısıyla, bu temel fonksiyon Haskells gibi

fmap :: Functor f => (a -> b) -> f a -> f b 

Java ile kolayca yazılamaz *), örneğin, en azından güvenli bir şekilde değil. Bu nedenle, temel işlevsellik elde etmek için çok sayıda kazan plakası yazmanız gerekir, çünkü

  1. Bir listenin elemanlarına basit bir fonksiyon uygulamak için bir yöntem
  2. Bir dizinin elemanlarına aynı basit işlevi uygulamak için bir yöntem
  3. Aynı basit işlevi karma değerlerine uygulamak için bir yöntem,
  4. .... ayarla
  5. .... ağaç
  6. ... 10. aynı ünite testleri

Yine de, bu beş yöntem temelde aynı koddur, verir veya alır. Aksine, Haskell'de ihtiyacım olan şey:

  1. Liste, dizi, harita, küme ve ağaç için bir Functor örneği (çoğunlukla önceden tanımlanmış veya derleyici tarafından otomatik olarak türetilebilir)
  2. basit işlev

Bunun Java 8 ile değişmeyeceğine dikkat edin (yalnızca biri işlevleri daha kolay uygulayabilir, ancak o zaman tam olarak yukarıdaki sorun ortaya çıkacaktır. Daha üst düzey işlevlere sahip olmadığınız sürece büyük olasılıkla daha yüksek türler için neyin iyi olduğunu anlayabilecektir.)

Ceylon gibi yeni OO dilleri bile daha yüksek türlere sahip değildir. (Son zamanlarda Gavin King'e sordum ve bana şu anda önemli olmadığını söyledi.) Yine de Kotlin'i bilmiyorum.

*) Adil olmak gerekirse, fmap yöntemi olan bir arayüz Functor'a sahip olabilirsiniz. Kötü bir şey, söyleyemezsiniz: Hey, SuperConcurrentBlockedDoublyLinkedDequeHasMap kütüphane sınıfı için fmap'ın nasıl uygulanacağını biliyorum, sevgili derleyici, lütfen bundan sonra tüm SuperConcurrentBlockedDoublyLinkedDequeHasMaps'ın Functors olduğunu kabul edin.


FTR: Seylan typechecker ve JavaScript şimdi arkayüz yapmak daha yüksek destek eli türlerini (ve aynı zamanda daha yüksek rütbe türleri). "Deneysel" bir özellik olarak kabul edilir. Ancak, topluluğumuz bu işlevsellik için pratik uygulamalar bulmakta zorlandı, bu yüzden bunun dilin "resmi" bir parçası olup olmayacağı açık bir soru. Ben do Java tarafından arka uç bir aşamada desteklenmesini bekliyoruz.
Gavin King,

-2

DBase'de programlanmış olan herkes, tek satırlı makroların yeniden kullanılabilir kod yapmak için ne kadar yararlı olduğunu bilirdi. Lisp'te programlanmadıysam da, derleme zamanı makrolarıyla yemin eden birçok insandan okudum. Derleme zamanında kodunuza kod ekleme fikri, "include" yönergesiyle her C programında basit bir biçimde kullanılır. Lisp bunu bir Lisp programıyla yapabilir ve Lisp oldukça yansıtıcı olduğundan, çok daha esnek içerikler elde edersiniz.

Web'den rasgele bir metin dizesi alıp veritabanına ileten herhangi bir programcı bir programcı değildir. Aynı şekilde, "kullanıcı" verilerinin otomatik olarak çalıştırılabilir kod haline gelmesine izin verebilecek olan herkes aptaldır. Bu, programların yürütme sırasında verileri değiştirmesine ve ardından kodu kod olarak çalıştırmasına izin vermenin kötü bir fikir olduğu anlamına gelmez. Gelecekte, çoğu program yazan "akıllı" kodlara sahip olacak olan bu tekniğin vazgeçilmez olacağına inanıyorum. Tüm "veri / kod sorunu" olup olmaması, dilde güvenlik konusudur.

Çoğu dilde yaşanan sorunlardan biri, bir satır öteye gidene, bazı işlevleri kendileri için yürütebilecek şekilde yapılmasıdır. Gerçek dünya programları, birçok kişinin her zaman ve aynı anda birden fazla Çekirdek ve çoklu bilgisayar kümesinden erişebilmesini gerektirir. Güvenlik, işletim sistemi yerine dilin bir parçası olmalı ve çok uzak olmayan bir gelecekte olacaktır.


2
Programcılara Hoşgeldiniz. Lütfen cevabınızdaki söylemleri not almayı ve taleplerinizden bazılarını dış referanslarla desteklemeyi düşünün.

1
Kullanıcı verilerinin otomatik olarak çalıştırılabilir kod olmasına izin verecek herhangi bir programcı açık bir şekilde cahildir . Aptal değil. Bu şekilde yapmak genellikle kolay ve açıktır ve neden bunun kötü bir fikir olduğunu ve daha iyi bir çözüm bulunduğunu bilmiyorlarsa, bunu yapmak için onları gerçekten suçlayamazsınız. (Ancak bunu öğrenmeye devam eden ve daha iyi bir yol olduğunu öğrenen herkes aptalcadır.)
Mason Wheeler
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.