Tamamen işlevsel diller modülerliği nasıl ele alır?


23

Sınıfların, en azından nesneler yapmak için kullanılan veya mirasta kullanılan kodun kolay geri dönüşümünü sağlayan bir soyutlama katmanı yapmak için kullanılabileceğini veya en azından kullanılabileceğini öğrendiğim nesne yönelimli bir arka plandan geliyorum.

Mesela bir hayvan sınıfına sahip olabilirim ve sonra bu kedi ve köpeklerden miras alabilir ve hepsi aynı özelliklerin birçoğunu miras alacak ve bu alt sınıflardan sonra bir hayvan cinsi, hatta adını belirleyebilecek nesneler yapabilirim. bunun.
Veya aynı kodun işleyen veya biraz farklı şeyler içeren birden fazla örneğini belirtmek için sınıfları kullanabilirim; arama ağacındaki düğümler veya birden çok farklı veritabanı bağlantısında olduğu gibi.

Son zamanlarda işlevsel programlamaya geçiyorum, bu yüzden merak etmeye başlamıştım:
Tamamen işlevsel diller böyle şeyleri nasıl ele alıyor? Yani, herhangi bir sınıf ve nesne kavramı olmayan diller.


1
Neden işlevselliğin sınıf anlamına gelmediğini düşünüyorsunuz? İlk sınıflardan bazıları LISP'den geldi - CLOS Clojure ad alanlarına , türlerine veya modüllerine ve haskell'e bakın .

DON'T dersleri olmayan fonksiyonel dillere atıfta bulundum, DO
Electric Coffee

1
Örnek olarak Caml, kız kardeşi OCaml nesneleri ekler, ancak Caml'in kendisi de yoktur.
Elektrikli Kahve

12
"Tamamen işlevsel" terimi, referans şeffaflığını koruyan ve dilin nesneye yönelik özelliklere sahip olup olmadığına ilişkin olmayan işlevsel dilleri ifade eder.
sepp2k

2
Kek bir yalandır, OO'da kodun tekrar kullanımı FP'dekinden çok daha zordur. OO'nun yıllar içinde yeniden kod kullanımının iddia ettiği her şey için, en azından birkaç kez takip ettiğini gördüm. (yanlış yaptığımı söylemekten çekinmeyin, yıllarca OO sistemleri tasarlamak ve korumak zorunda kaldığım OO kodunu ne kadar iyi yazabildiğim konusunda rahatım, kendi sonuçlarımın kalitesini biliyorum)
Jimmy Hoffa

Yanıtlar:


19

Birçok işlevsel dilde bir modül sistemi vardır. (Nesneye yönelik birçok dil de bu arada.) Ancak, bir tane bile olmasa bile, işlevleri modüller olarak kullanabilirsiniz.

JavaScript iyi bir örnek. JavaScript'te, işlevler hem modülleri uygulamakta hem de nesne yönelimli kapsülleme işlemlerinde kullanılır. JavaScript için en büyük ilham kaynağı olan Scheme'de sadece fonksiyonlar var. Neredeyse her şeyi uygulamak için işlevler kullanılır: nesneler, modüller ( Raket içindeki birimler olarak adlandırılır ), hatta veri yapıları.

OTOH, Haskell ve ML ailesi açık bir modül sistemine sahiptir.

Nesne Oryantasyonu veri soyutlama ile ilgilidir. Bu kadar. Modülerlik, Kalıtım, Polimorfizm, hatta değişken durum bile dikey kaygılardır.


8
Bu şeylerin nasıl işlediğini oop ile ilgili biraz daha açıklayabilir misiniz? Kavramların var olduğunu belirtmek yerine ...
Electric Coffee

Sidenote - Modüller ve birimler Racket'te iki farklı yapıya sahiptir - modüller ad alanlarıyla karşılaştırılabilir ve birimler ad alanları ve OO arayüzleri arasında yarı yarıyadır. Dokümanlar farklılıklar konusunda çok daha fazla ayrıntıya giriyor
Jack

@Jack: Raket'in aynı zamanda bir kavramı olduğunu bilmiyordum module. Bence Racket’in modulemodül olmayan bir konsept ve modül olmayan ama denen bir kavram olması talihsiz bir durum module. Her neyse, şunu yazdın: "birimler ad alanları ve OO arayüzleri arasında yarı yarıyadır". Modülün tanımı bu şekilde değil mi?
Jörg W Mittag

Modüller ve birimler, değerlere bağlı her iki isim grubudur. Modüller, diğer spesifik bağlama kümelerine bağımlı olabilirken, üniteler , üniteyi kullanan herhangi bir diğer kodun sağlaması gereken bazı genel bağlama kümelerine bağımlı olabilir . Birimler ciltler üzerinden parametrelenir , modüller değildir. Bir bağlanmaya bağlı bir modül ve bir bağlanmaya mapbağlı bir birim map, modülün farklı kullanıcıları birimin farklı tanımlarını verebilirken , modülün kendisinden bir olan gibi belirli bir bağlanmaya değinmesi gerektiği için farklıdır . mapracket/basemap
Jack,

4

İki soru soruyor gibisiniz: "İşlevsel dillerde modülerliği nasıl elde edebilirsiniz?" hangisi başka cevaplarda ele alındı ​​ve "işlevsel dillerde soyutlamaları nasıl oluşturabilirsiniz?" hangi cevap vereceğim.

OO dillerinde, isim, "hayvan", "posta sunucusu", "bahçe çatalı", vb. Üzerinde yoğunlaşmaya meyillidirsiniz. , "eşya", vb.

Öyleyse, işlevsel dillerdeki soyutlamaların nesneler yerine fiillerin veya işlemlerin üzerinde olma eğilimi yoktur. Bunu açıklamaya çalışırken her zaman ulaşabileceğim bir örnek ayrıştırmadır. İşlevsel dillerde, ayrıştırıcı yazmak için iyi bir yol, bir dilbilgisi belirtip sonra yorumlamaktır. Tercüman ayrıştırma işlemi üzerinde bir soyutlama yaratır.

Bunun bir başka somut örneği, uzun zaman önce üzerinde çalıştığım bir proje. Haskell'de bir veritabanı yazıyordum. İşlemleri en düşük düzeyde belirlemek için bir 'gömülü dil' vardı; örneğin, depolama ortamından bir şeyler yazmama ve okumama izin verdi. İşlemleri en üst düzeyde belirtmek için başka, ayrı bir 'gömülü dil' vardı. Ardından, işlemleri daha yüksek seviyeden daha düşük seviyeye dönüştürmek için esasen tercüman olan şey vardı.

Bu oldukça genel bir soyutlama şeklidir, ancak işlevsel dillerde mevcut olan tek şey değildir.


4

Her ne kadar "fonksiyonel programlama" modülerlik meseleleri için geniş kapsamlı sonuçlar doğurmasa da, belirli diller geniş çaplı programlamayı farklı şekillerde ele almaktadır. Kodun yeniden kullanımı ve soyutlama, etkileşimi ne kadar az zorlarsanız, kodu yeniden kullanmak o kadar zorlaşır. Soyutlamayı bir kenara bırakmak gerekirse, iki tekrar kullanım konusuna değineceğim.

Statik olarak yazılmış OOP dilleri, geleneksel olarak, nominal alt tiplendirme kullandı; bu, sınıf / modül / arabirim A için tasarlanan bir kodun, B açıkça A'dan bahsettiğinde, yalnızca sınıf / modül / arabirim B ile başa çıkabileceği anlamına gelir. A için tasarlanan kod, B, A'nın tüm yöntem ve / veya alanlarına sahip olduğunda B'yi ele alabilir. B, daha genel bir sınıf / arayüz A'ya ihtiyaç duyulmadan önce, farklı bir ekip tarafından yaratılmış olabilir. Örneğin, OCaml'de, yapısal alt tipleme. modül sistemi, OOP benzeri nesne sistemi ve oldukça benzersiz polimorfik varyant tipleri için geçerlidir.

OOP ve FP arasında en belirgin fark wrt. modülerlik, OOP'deki varsayılan "ünite" nin, aynı değerler durumunda çeşitli işlemleri bir nesne olarak biraraya getirdiği, FP'deki varsayılan "ünite" ise çeşitli değerler için aynı işlemi bir fonksiyon olarak bir araya getirdiğidir. FP'de işlemleri, örneğin modül olarak bir araya getirmek hala çok kolaydır. (BTW, ne Haskell ne de F # tam teşekküllü bir ML-ailesi modül sistemine sahip değildir.) İfade SorunuTüm değerler üzerinde çalışan yeni işlemlerin (örneğin mevcut nesnelere yeni bir yöntem eklemek) artımlı olarak eklenmesi ve tüm işlemlerin desteklemesi gereken yeni değer durumları (örn. aynı arayüze yeni bir sınıf eklenmesi) görevidir. Aşağıdaki ilk Ralf Laemmel dersinde tartışıldığı gibi (C # 'da geniş örnekleri vardır), OOP dillerinde yeni işlemler eklemek sorunludur.

Scala'daki OOP ve FP kombinasyonu onu en güçlü dillerden biri haline getirebilir. modülerlik. Ancak OCaml hala en sevdiğim dil ve kişisel, öznel görüşüme göre Scala'dan yoksun değil. Aşağıdaki iki Ralf Laemmel dersi Haskell'deki anlatım probleminin çözümünü tartışıyor. Bence bu çözüm, kusursuz çalışmasına rağmen, elde edilen verilerin parametrik polimorfizm ile kullanılmasını zorlaştırıyor. İfade probleminin OCaml'daki polimorfik varyantlarla çözülmesi, aşağıda bağlantılı Jaques Garrigue makalesinde açıklandığı gibi, bu eksikliğe sahip değil. OCaml'deki OOP dışı ve OOP modülerliğinin kullanımlarını karşılaştıran ders kitabı bölümlerine de bağlantı veriyorum.

Aşağıda, İfade Sorununda genişleyen Haskell ve OCaml'a özgü bağlantılar bulunmaktadır :


2
Bu kaynakların ne yaptığını ve neden sorulan soruyu yanıtlarken bunları neden tavsiye edersiniz? Yığın Borsası'nda "yalnızca bağlantı yanıtları" oldukça açık değildir
gnat

2
Bir düzenleme olarak sadece bağlantılar yerine gerçek bir cevap verdim.
lukstafi

0

Aslında, OO kodu daha az tekrar kullanılabilir ve tasarım gereğidir. OOP'un ardındaki fikir, belirli veri parçaları üzerindeki işlemleri, sınıfta veya miras hiyerarşisindeki uygun yerdeki belirli ayrıcalıklı kodlarla sınırlandırmaktır. Bu değişkenliğin olumsuz etkilerini sınırlar. Bir veri yapısı değişirse, kodda sorumlu olabilecek çok fazla yer vardır.

Değişmezlikle, belirli bir veri yapısı üzerinde kimin çalışabileceği umrunda değil, çünkü hiç kimse veri kopyanızı değiştiremez. Bu, mevcut veri yapıları üzerinde çalışmak için yeni fonksiyonlar yaratmayı çok daha kolaylaştırır. Siz sadece fonksiyonları yaratır ve etki alanı açısından uygun görünen modüller halinde gruplandırırsınız. Miras hiyerarşisine nasıl sığacakları konusunda endişelenmenize gerek yok.

Diğer bir kod yeniden kullanımı, mevcut işlevler üzerinde çalışmak için yeni veri yapıları oluşturmaktır. Bu, jenerik ve tip sınıfları gibi özellikler kullanılarak işlevsel dillerde işlenir. Örneğin, Haskell'in Ord tipi sınıfı, sortişlevi bir Ordörnekle herhangi bir türde kullanmanıza izin verir . Zaten mevcut değilse, örnekleri oluşturmak kolaydır.

AnimalÖrnek alın ve bir besleme özelliği uygulamayı düşünün. Anlaşılır OOP uygulaması, bir Animalnesne koleksiyonunu sürdürmek ve feedher biri için yöntemi çağırarak, hepsinin içinden geçen döngüdür .

Ancak, ayrıntılara indiğinizde işler zorlaşıyor. Bir Animalnesne doğal olarak ne tür bir yemek yediğini ve tam hissetmek için ne kadar ihtiyacı olduğunu bilir. O mu değil gıda tutulur nerede doğal bilmek ve böylece ne kadar, mevcut FoodStorenesne sadece her bir bağımlılık haline geldi Animalya bir alan olarak, Animalnesnenin veya bir parametre olarak geçirilen feedyöntemle. Alternatif olarak, Animalsınıfı daha uyumlu tutmak için nesneye hareket feed(animal)edebilir FoodStoreveya bir veya başkaları adı verilen bir sınıfın suiistimalini oluşturabilirsiniz AnimalFeeder.

FP'de, Animalyeniden kullanılabilirlik için bazı ilginç çıkarımlar bulunan, her zaman bir arada gruplanmış olma alanları için bir eğilim yoktur . Eğer bir listesi var Say Animalalanlar gibi birlikte, kayıtların name, species, location, food type, food amount, vb Ayrıca bir listesi var FoodStoregibi alanlarla kayıtların location, food typeve food amount.

Beslenmedeki ilk adım, bu kayıt listelerinin her birini (food amount, food type)hayvanların miktarları için negatif sayılarla çiftlerin listelerine eşlemek olabilir . Daha sonra bu tür çiftlerle her çeşit şeyi yapmak için işlevler oluşturabilirsiniz, örneğin her bir yiyecek türünün toplamı. Bu işlevler bir Animalveya bir FoodStoremodüle mükemmel şekilde ait değildir , ancak her ikisi tarafından da yeniden kullanılabilir.

[(Num A, Eq B)]Bununla yeniden kullanılabilir ve modüler olan faydalı şeyler yapan bir grup fonksiyonla bitirdiniz, ancak onları nereye koyacağınızı ya da grup olarak neyi arayacağınızı bulmakta zorlanıyorsunuz. Bunun etkisi, FP modüllerinin sınıflandırılmasının daha zor olmasıdır, ancak sınıflandırma çok daha az önemlidir.


-1

Popüler çözümlerden biri, kodu modüllere ayırmak, işte JavaScript’te nasıl yapıldığı:

    media.podcast = (function(name) {
    var fileExtension = 'mp3';        

     function determineFileExtension() {
         console.log('File extension is of type ' + fileExtension);
     }

     return {
         download: function(episode) {
            console.log('Downloading ' + episode + ' of ' + name);
            determineFileExtension();
        }
    }    
}('Astronomy podcast'));

JavaScript bu modeli açıklayan Tam makale , bunun dışında gibi bir modülü tanımlamak için başka yollar, sayısı vardır RequireJS , CommonJS , Google Kapatma. Diğer bir örnek ise her ikisine de sahip Erlang olduğu modülleri ve davranışları OOP Arayüz gibi benzer bir rol oynuyor, API ve desen uygulamak.

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.