API'lar ve fonksiyonel programlama


15

Clojure gibi işlevsel programlama dillerine (kuşkusuz sınırlı) maruz kalmamdan, verilerin kapsüllenmesinin daha az önemli bir rolü olduğu görülmektedir. Genellikle haritalar veya kümeler gibi çeşitli yerel türler, nesneleri temsil etmek için tercih edilen para birimidir. Ayrıca, bu veriler genellikle değişmezdir.

Örneğin, işte bu konuyla ilgili bir röportajda Clojure şöhretinden Zengin Hickey'den daha ünlü alıntılardan biri :

Fogus: Bu fikri izleyerek — bazı insanlar Clojure'un türlerinde veri gizleme kapsüllemesine girmemesinden şaşırırlar. Neden veri gizlemeyi bırakmaya karar verdiniz?

Hickey: Clojure'un soyutlamalara programlamayı güçlü bir şekilde vurguladığını açıklığa kavuşturalım. Bir noktada, birisinin verilere erişmesi gerekecek. Ve eğer “özel” fikriniz varsa, karşılık gelen ayrıcalık ve güven kavramlarına ihtiyacınız vardır. Ve bu, bir ton karmaşıklık ve az değer katar, bir sistemde sağlamlık yaratır ve çoğu zaman işleri yapmamaları gereken yerlerde yaşamaya zorlar. Bu, basit bilgi sınıflara konulduğunda meydana gelen diğer kayıplara ek olarak yapılır. Verilerin değişmez olduğu ölçüde, birisinin değişebilecek bir şeye bağımlı hale gelmesinin dışında erişim sağlamada çok az zarar vardır. Tamam, insanlar bunu gerçek hayatta her zaman yaparlar ve işler değiştiğinde adapte olurlar. Ve eğer rasyonel iseler, Gelecekte adapte olabilmeleri için değiştirebilecekleri bir şeye dayanarak bir karar verdiklerini bilirler. Yani, bu bir risk yönetimi kararı, bence programcıların özgürce yapması gerektiğini düşünüyorum. Eğer insanlar soyutlamalara programlamak ve uygulama ayrıntılarıyla evlenmek konusunda dikkatli olmak gibi bir duyarlılığa sahip değillerse, asla iyi bir programcı olmayacaklardır.

OO dünyasından geliyor, bu yıllar boyunca öğrendiğim bazı ilkeleri karmaşıklaştırıyor gibi görünüyor. Bunlar arasında Bilgi Gizleme, Demeter Yasası ve Tekdüzen Erişim İlkesi sayılabilir. Bu ortak konu, kapsüllemenin, başkalarının neye dokunmaları gerektiğini ve dokunmamaları gerektiğini bilmesi için bir API tanımlamamıza olanak tanımasıdır. Temel olarak, bazı kodların bakımının, tüketicinin koduna nasıl hatalar getirebileceğinden endişe etmeden serbestçe değişiklik ve yeniden düzenleme yapmasına izin veren bir sözleşme oluşturmak (Açık / Kapalı prensibi). Ayrıca, diğer programcıların bu verilere ulaşmak veya bu verilere dayanmak için hangi araçları kullanabileceklerini bilmesi için temiz ve küratörlü bir arayüz sağlar.

Verilere doğrudan erişilmesine izin verildiğinde, söz konusu API sözleşmesi bozulur ve tüm bu kapsülleme avantajları ortadan kalkar gibi görünür. Ayrıca, kesinlikle değiştirilemez veriler, alana özgü yapıların (nesneler, yapılar, kayıtlar) etrafından geçmeyi, bir durumu temsil etme ve bu durumda gerçekleştirilebilecek eylemler kümesini daha az kullanışlı hale getirmektedir.

İşlevsel kod tabanları, bir kod tabanının boyutu muazzam bir şekilde büyüdüğünde ortaya çıkan bu sorunları, API'lerin tanımlanması gerektiği ve birçok geliştiricinin sistemin belirli bölümleriyle çalışmaya dahil olacağı şekilde nasıl ele alır? Bu tür kod tabanlarında bunun nasıl ele alındığını gösteren bu durumun örnekleri var mı?


2
Nesne kavramı olmadan resmi bir arayüz tanımlayabilirsiniz. Sadece bunları belgeleyen arayüzün işlevini yaratın. Uygulama ayrıntıları için belge vermeyin. Bir arayüz oluşturdunuz.
Scara95

@ Scara95 Bu, bir arabirim kodunu uygulamak ve tüketiciye ne yapması ve ne yapılmaması konusunda uyarmak için yeterli dokümantasyon yazmak için çalışmam gerektiği anlamına gelmiyor mu? Kod değişirse ve belgeler eskiyse ne olur? Bu nedenle genellikle kendi kendini belgeleyen kodu tercih ederim.
jameslk

Yine de arayüzü belgelemek zorundasınız.
Scara95

3
Also, strictly immutable data seems to make passing around domain-specific structures (objects, structs, records) much less useful in the sense of representing a state and the set of actions that can be performed on that state.Pek sayılmaz. Değişen tek şey, değişikliklerin yeni bir nesneye dönüşmesidir. Kod hakkında muhakeme söz konusu olduğunda bu büyük bir kazançtır; Değişken nesnelerin etrafından geçilmesi, onları kimin değiştirebileceğini takip etmek zorunda kalmak anlamına gelir, bu da kodun boyutu ile ölçeklenen bir sorundur.
Doval

Yanıtlar:


10

Her şeyden önce, ikinci Sebastian'ın işlevsel neyin doğru, dinamik yazmanın ne olduğu hakkındaki yorumlarına gideceğim. Daha genel olarak, Clojure fonksiyonel dil ve topluluğun bir lezzetidir ve buna dayanarak çok fazla genellememelisiniz. ML / Haskell perspektifinden daha fazla açıklama yapacağım.

Basile'den bahsedildiği gibi, erişim kontrolü kavramı ML / Haskell'de mevcuttur ve sıklıkla kullanılır. "Faktoring" geleneksel OOP dillerinden biraz farklıdır; OOP'de sınıf kavramı aynı anda tip ve modül rolünü oynarken işlevsel (ve geleneksel prosedürel) diller bunları ortogonal olarak ele alır.

Başka bir nokta, ML / Haskell'in tip silme özelliğine sahip jenerikler üzerinde çok ağır olmasıdır ve bu, OOP kapsüllemesinden farklı bir "bilgi gizleme" lezzeti sağlamak için kullanılabilir. Bir bileşen yalnızca bir veri öğesinin türünü bir tür parametresi olarak bildiğinde, bu bileşene bu türdeki değerleri güvenli bir şekilde verebilir ve yine de somut türlerini bilmediği ve bilemediği için onlarla çok fazla iş yapması engellenir. ( instanceofbu dillerde evrensel veya çalışma zamanı dökümleri yoktur ). Bu blog girişi , bu tekniklere en sevdiğim tanıtım örneklerinden biri.

Sonraki: FP dünyasında şeffaf veri yapılarını opak / kapsüllenmiş bileşenlere arayüz olarak kullanmak çok yaygındır. Örneğin, veri yapılarının mantığı tanımlayan sözdizimi ağaçları olarak kullanıldığı ve bunları "yürüten" koda beslendiği FP'de yorumlayıcı kalıpları çok yaygındır. Veri yapılarını tüketen yorumlayıcı çalıştığında, düzgün bir şekilde söylenen devlet geçici olarak var olur. Ayrıca, tercümanın uygulaması, müşterilerle aynı veri türleri açısından iletişim kurduğu sürece değişebilir.

Son ve en uzun: kapsülleme / bilgi gizleme, bir amaç değil , bir tekniktir . Ne sağladığını biraz düşünelim. Kapsülleme, sözleşmeyi uzlaştırma ve bir yazılım biriminin uygulanmasına yönelik bir tekniktir . Tipik durum şudur: Sistemin uygulanması, sözleşmesine göre var olmaması gereken değerleri veya durumları kabul eder.

Bu şekilde baktığınızda, FP'nin kapsüllemeye ek olarak aynı amaçla kullanılabilecek bir dizi ek araç sağladığını belirtebiliriz:

  1. Yaygın varsayılan olarak değişmezlik. Saydam veri değerlerini üçüncü taraf koduna verebilirsiniz. Onları değiştiremez ve geçersiz durumlara koyamazlar. (Karl'ın cevabı bu noktayı işaret eder.)
  2. Çok fazla kod yazmadan, türlerinizin yapısını hassas bir şekilde kontrol etmenizi sağlayan cebirsel veri türlerine sahip sofistike tip sistemler. Bu tesisleri akıllıca kullanarak, genellikle "kötü durumların" imkansız olduğu türler tasarlayabilirsiniz. (Slogan: "Yasadışı devletleri temsil edilemez hale getir." ) Bir sınıfın kabul edilebilir durumlarını dolaylı olarak kontrol etmek için kapsülleme kullanmak yerine, derleyiciye bunların ne olduğunu söylemeyi ve benim için garanti etmesini tercih ederim!
  3. Daha önce de belirtildiği gibi tercüman düzeni. İyi bir soyut sözdizimi ağacı türü tasarlamanın bir anahtarı:
    • Soyut sözdizimi ağacı veri türünü tüm değerlerin "geçerli" olacağı şekilde tasarlamaya çalışın.
    • Bunu yapmazsanız, yorumlayıcının geçersiz kombinasyonları açıkça algılamasını ve bunları temiz bir şekilde reddetmesini sağlayın.

Bu F # "Türlerle tasarım" serisi , bu konuların bazılarında, özellikle # 2, oldukça iyi bir okuma sağlar. (Yukarıdan "yasadışı devletleri temsil edilemez yap" bağlantısının geldiği yer burasıdır.) Yakından bakarsanız, ikinci bölümde yapıcıları gizlemek ve istemcilerin geçersiz örnekler oluşturmasını önlemek için kapsüllemeyi nasıl kullanacaklarını gösterdiklerini göreceksiniz. Yukarıda belirttiğimiz gibi, bu ise araç setinin bir parçası!


9

Değişebilirliğin yazılımda sorun yaratma derecesini gerçekten aşamıyorum. Kafalarımıza batırılan uygulamaların çoğu, değişebilirliğin neden olduğu problemler için tazminat almaktadır. Değişebilirliği ortadan kaldırdığınızda, bu uygulamalara çok fazla ihtiyacınız yoktur.

Değişmezliğe sahip olduğunuzda, çalışma süresinde veri yapınızın sizden beklenmedik bir şekilde değişmeyeceğini bilirsiniz, böylece programınıza özellikler ekledikçe kendi kullanımınız için kendi türev veri yapılarınızı yapabilirsiniz. Orijinal veri yapısının bu türev veri yapıları hakkında hiçbir şey bilmesine gerek yoktur.

Bu, temel veri yapılarınızın oldukça kararlı olma eğiliminde olduğu anlamına gelir . Yeni veri yapıları, gerektiğinde kenarların etrafından türetilir. Önemli bir fonksiyonel program yapana kadar açıklamak gerçekten zor. Kendinizi gizliliğe gittikçe daha az ilgi duyuyor ve sürekli genel genel veri yapıları oluşturmayı düşünüyorsunuz.


Eklemek istediğim bir şey, değişmez değişkenin, bir yapı varsa, programcıların dağıtılmış ve dağılmış veri yapısına bağlı kalmasına neden olmasıdır. Tüm veriler, ulaşım için değil, kolay keşif ve geçiş için bir mantık grubu oluşturmak üzere yapılandırılmıştır. Bu, yeterli işlevsel programlama yaptıktan sonra yapacağınız bir mantık ilerlemesidir.
Xephon

8

Clojure'un sadece hash ve ilkelleri kullanma eğilimi, bence, işlevsel mirasının bir parçası değil, dinamik mirasının bir parçası. Python ve Ruby'de benzer eğilimler gördüm (her ikisi de üst düzey işlevler için oldukça iyi bir desteğe sahip olsa da, hem nesne yönelimli, zorunlu ve dinamik), hem de Haskell'de (statik olarak yazılmış, ancak tamamen işlevsel) değil değişmezlikten kaçmak için gerekli özel yapılarla).

Bu yüzden sormanız gereken soru, işlevsel dillerin büyük API'leri nasıl ele aldığı değil, dinamik dillerin bunu nasıl yaptığıdır. Cevap: iyi dokümantasyon ve çok sayıda ünite testi. Neyse ki, modern dinamik diller genellikle her ikisi için de çok iyi bir destekle gelir; örneğin, hem Python hem de Clojure, dokümanları yalnızca yorumlara değil, kodun içine gömme yoluna sahiptir.


Statik olarak yazılan (tamamen) işlevsel diller hakkında, OO programlamasında olduğu gibi veri tipine sahip bir işlevi taşımanın (basit) bir yolu yoktur. Yani dokümantasyon zaten önemli. Mesele şu ki, bir arayüz tanımlamak için dil desteğine ihtiyacınız yok.
Scara95

5
@ Scara95 "Veri tipiyle fonksiyon taşıma" ile ne demek istediğinizi açıklayabilir misiniz?
Sebastian Redl

6

Bazı işlevsel diller, soyut veri türlerinde ve modüllerinde uygulama ayrıntılarını kapsülleme veya gizleme olanağı sağlar .

Örneğin, OCaml , adlandırılmış soyut türlerin ve değerlerin (özellikle bu soyut türlerde çalışan işlevler) bir koleksiyonuyla tanımlanan modüllere sahiptir. Belirli bir anlamda Ocaml'ın modülleri API'leri yeniden kullanıyor. Ocaml ayrıca bazı modülleri başka bir modüle dönüştüren ve böylece genel programlama sağlayan functorlara sahiptir. Yani modüller bileşimseldir.

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.