Python gibi sadece dinamik olarak yazılmış dillerde mümkün tasarım desenleri var mı?


30

İlgili bir soru okudum Python gibi dinamik dillerde gereksiz tasarım desenleri var mı? Wikiquote.org'daki bu alıntıyı hatırladı ve hatırladı

Dinamik yazma ile ilgili harika bir şey, hesaplanabilir olan her şeyi ifade etmenize izin vermesidir. Ve yazım sistemleri yazmaz - sistemler genellikle kararlaştırılabilir ve sizi bir alt kümeye kısıtlarlar. Statik tip sistemlerden hoşlananlar “iyi, yeterince iyi; yazmak istediğiniz tüm ilginç programlar tür olarak çalışacaktır ” Ama bu saçmalık - bir tür sisteminiz olduğunda, hangi ilginç programların olduğunu bile bilmiyorsunuz.

--- Yazılım Mühendisliği Radyo Bölüm 140: Gilad Bracha ile Gazete ve Takılabilir Tipler

Merak ediyorum, alıntı formülü kullanılarak "tür olarak çalışma" şeklinde faydalı tasarım kalıpları veya stratejileri var mı?


3
İkili gönderme ve Ziyaretçi deseninin statik olarak yazılmış dillerde başarılmasının çok zor olduğunu, ancak dinamik dillerde kolayca birleştirildiğini gördüm. Örneğin, bu cevaba (ve soruya bakınız) bakınız: programmers.stackexchange.com/a/288153/122079
user3002473

7
Tabii ki. Örneğin çalışma zamanında yeni sınıflar oluşturmayı içeren herhangi bir kalıp. (Java'da da mümkündür, ancak C ++ 'ta değil; kayan bir dinamizm ölçeği var).
kullanıcı253751

1
Tip sisteminizin ne kadar karmaşık olduğuna çok bağlı olacaktır :-) İşlevsel diller genellikle bu konuda oldukça başarılıdır.
Bergi

1
Herkes Haskell veya OCaml yerine Java ve C # gibi tip sistemler konuşuyor gibi görünüyor. Güçlü bir yazım sistemine sahip bir dil dinamik bir dil kadar özlü olabilir ancak yazım güvenliğini korur.
Andrew, Monica

@ immibis Bu yanlış. Statik tip sistemler çalışma zamanında kesinlikle yeni, "dinamik" sınıflar oluşturabilir. Programlama Dilleri İçin Pratik Temellerin 33. Bölümüne bakınız.
gardenhead,

Yanıtlar:


4

Birinci sınıf türleri

Dinamik yazma, birinci sınıf türleriniz olduğu anlamına gelir: dilin kendi türleri de dahil olmak üzere çalışma zamanında türleri inceleyebilir, oluşturabilir ve saklayabilirsiniz. Ayrıca , değişkenlerin değil , değerlerin yazıldığını gösterir .

Statik olarak yazılmış dil, yöntem gönderme, tür sınıfları vb. Gibi dinamik türlere de dayanan ancak genel olarak çalışma zamanına görünmeyen bir şekilde kod üretebilir. En iyisi, size iç gözlem yapmanın bir yolunu veriyorlar. Alternatif olarak, türleri değer olarak simüle edebilirsiniz ancak daha sonra geçici bir dinamik yazı sisteminiz vardır.

Ancak, dinamik tip sistemler nadiren yalnızca birinci sınıf tiplere sahiptir. Birinci sınıf sembollere, birinci sınıf paketlere, birinci sınıf ... her şeye sahip olabilirsiniz. Bu, derleyicinin dili ile çalışma zamanı dili arasındaki statik olarak yazılmış dillerdeki kesin ayrımın aksinedir. Derleyicinin veya tercümanın çalışma zamanını da ne yapabilir?

Şimdi, tür çıkarımının iyi bir şey olduğu ve çalıştırmadan önce kodumu kontrol ettirmeyi sevdiğim konusunda hemfikiriz. Ancak çalışma zamanında kod üretmeyi ve derlemeyi de seviyorum. Ben de derleme zamanında işleri önceden hesaplamayı seviyorum. Dinamik olarak yazılmış bir dilde, bu aynı dilde yapılır. OCaml'da, ana işlemci sisteminden farklı, önişlemci dilden farklı olan modül / işlev tipi türünüz vardır. C ++ 'da, yürütme sırasındaki türler genellikle cahil olan ana dil ile ilgisi olmayan bir şablon diline sahipsiniz. Ve bu dilde sorun yok, çünkü daha fazlasını sağlamak istemiyorlar.

Sonuçta, bu ne tür bir yazılım geliştirebileceğinizi gerçekten değiştirmez , ancak etkililik değişir nasıl onları geliştirdiğinizi ve zor olup olmadığını değiştirir.

desenler

Dinamik türlere dayanan kalıplar, dinamik ortamları içeren kalıplardır: açık sınıflar, gönderme, nesnelerin bellekteki veritabanları, serileştirme vb. Genel kapsayıcılar gibi basit şeyler çalışır; (parametrik türlere gerek yok).

Kodun Common Lisp'te değerlendirilmesinin yanı sıra olası statik analizlerin örneklerini sunmaya çalıştım (bu, SBCL'dir). Sanal alan örneği , ayrı bir dosyadan alınan küçük bir Lisp kodu alt kümesini derler . Makul bir şekilde güvenli olması için, okunabilir değeri değiştiririm, yalnızca standart sembollerin bir alt kümesine izin veririm ve zaman aşımı olan şeyleri kaydırırım.

;;
;; Fetching systems, installing them, etc. 
;; ASDF and QL provide provide resp. a Make-like facility 
;; and system management inside the runtime: those are
;; not distinct programs.
;; Reflexivity allows to develop dedicated tools: for example,
;; being able to find the transitive reduction of dependencies
;; to parallelize builds. 
;; https://gitlab.common-lisp.net/xcvb/asdf-dependency-grovel
;;
(ql:quickload 'trivial-timeout)

;;
;; Readtables are part of the runtime.
;; See also NAMED-READTABLES.
;;
(defparameter *safe-readtable* (copy-readtable *readtable*))
(set-macro-character #\# nil t *safe-readtable*)
(set-macro-character #\: (lambda (&rest args)
                           (declare (ignore args))
                           (error "Colon character disabled."))
                     nil
                     *safe-readtable*)

;; eval-when is necessary when compiling the whole file.
;; This makes the result of the form available in the compile-time
;; environment. 
(eval-when (:compile-toplevel :load-toplevel :execute)
  (defvar +WHITELISTED-LISP-SYMBOLS+ 
    '(+ - * / lambda labels mod rem expt round 
      truncate floor ceiling values multiple-value-bind)))

;;
;; Read-time evaluation #.+WHITELISTED-LISP-SYMBOLS+
;; The same language is used to control the reader.
;;
(defpackage :sandbox
  (:import-from
   :common-lisp . #.+WHITELISTED-LISP-SYMBOLS+)
  (:export . #.+WHITELISTED-LISP-SYMBOLS+))

(declaim (inline read-sandbox))

(defun read-sandbox (stream &key (timeout 3))
  (declare (type (integer 0 10) timeout))
  (trivial-timeout:with-timeout (timeout)
    (let ((*read-eval* nil)
          (*readtable* *safe-readtable*)
          ;;
          ;; Packages are first-class: no possible name collision.
          ;;
          (package (make-package (gensym "SANDBOX") :use '(:sandbox))))
      (unwind-protect
           (let ((*package* package))
             (loop
                with stop = (gensym)
                for read = (read stream nil stop)
                until (eq read stop)
                ;;
                ;; Eval at runtime
                ;;
                for value = (eval read)
                ;;
                ;; Type checking
                ;;
                unless (functionp value)
                do (error "Not a function")
                ;; 
                ;; Compile at run-time
                ;;
                collect (compile nil value)))
        (delete-package package)))))

;;
;; Static type checking.
;; warning: Constant 50 conflicts with its asserted type (MOD 11)
;;
(defun read-sandbox-file (file)
  (with-open-file (in file)
    (read-sandbox in :timeout 50)))

;; get it right, this time
(defun read-sandbox-file (file)
  (with-open-file (in file)
    (read-sandbox in)))

#| /tmp/plugin.lisp
(lambda (x) (+ (* 3 x) 100))
(lambda (a b c) (* a b))
|#

(read-sandbox-file #P"/tmp/plugin.lisp")

;; 
;; caught COMMON-LISP:STYLE-WARNING:
;;   The variable C is defined but never used.
;;

(#<FUNCTION (LAMBDA (#:X)) {10068B008B}>
 #<FUNCTION (LAMBDA (#:A #:B #:C)) {10068D484B}>)

Yukarıdaki hiçbir şey diğer dillerle yapmak için "imkansız" değildir. Blender, müzik yazılımında veya anında yeniden derleme yapan statik olarak derlenmiş diller için IDE'lerde bulunan eklenti yaklaşımı. Harici araçlar yerine, dinamik diller zaten orada bulunan bilgileri kullanan araçları tercih eder. FOO'nun bilinen tüm arayanları? BAR'ın tüm alt sınıfları? ZOT sınıfında uzmanlaşan tüm yöntemler? bu içselleştirilmiş veridir. Tipler bunun bir başka yönüdür.


(ayrıca bakınız: CFFI )


39

Kısa cevap: hayır, çünkü Turing denkliği.

Uzun cevap: Bu adam trol oluyor. Tür sistemlerinin "sizi bir alt kümeyle sınırlandırdığı" doğru olduğu halde, bu alt kümenin dışındaki öğeler, tanım gereği çalışmayan şeylerdir.

Herhangi bir Turing-komple programlama dilinde yapabileceğiniz her şey (genel amaçlı programlama için tasarlanmış bir dil, artı olmayanlar; temizlemesi oldukça düşük bir çubuktur ve sistemin dönmesi için birkaç örnek vardır.) istemeden tamamlayın) herhangi bir Turing-complete programlama dilinde yapabilirsiniz. Buna "Turing denkliği" denir ve yalnızca tam olarak ne dediği anlamına gelir. Önemlisi, diğer bir şeyi diğer dilde olduğu kadar kolay yapabileceğiniz anlamına gelmez - bazıları ilk sırada yeni bir programlama dili yaratmanın asıl amacının bu olduğunu söyler: Mevcut dillerin emdiği şeyler.

Örneğin, dinamik bir tip sistemi, tüm değişkenleri, parametreleri ve dönüş değerlerini temel olarak bildirerek statik OO tipi bir sistemin üzerine taklit edilebilir. Object tip ve ardından içindeki belirli verilere erişmek için yansıma kullanarak edilebilir, böylece bunu gerçekleştirdiğinizde Kelimenin tam anlamıyla, dinamik bir dilde yapabileceğiniz hiçbir şeyi statik bir dilde yapamayacağınızı görüyorsunuz. Ama bu şekilde yapmak elbette büyük bir karışıklık olurdu.

Alıntıdaki adam statik tiplerin yapabileceklerinizi kısıtladığına göre doğru, ama bu önemli bir özellik değil, sorun. Yoldaki çizgiler arabanızda neler yapabileceğinizi sınırlar, ancak onları kısıtlayıcı mı yoksa yardımcı mı buluyorsunuz? (Otomobillerin yanlarına doğru gitmelerini söyleyen hiçbir şeyin olmadığı ve araba sürdüğüm yere gelmeyeceklerini söylemediği yoğun, karmaşık bir yolda sürmek istemeyeceğimi biliyorum!) Ne olduğunu açıkça tanımlayan kuralları belirleyerek geçersiz davranış olarak kabul edilir ve bunun gerçekleşmemesini sağlamak, kötü bir çarpışma olasılığını büyük ölçüde azaltırsınız.

Ayrıca, diğer tarafı da kötüleştiriyor. Bu, "yazmak istediğiniz tüm ilginç programlar tür olarak çalışacaktır" değil, "yazmak istediğiniz tüm ilginç programlar tür gerektirecektir " değil. Belli bir karmaşıklık seviyesinin ötesine geçtiğinizde, iki nedenden dolayı sizi kodda tutmak için bir tür sistem olmadan kod tabanını korumak çok zorlaşır.

Birincisi, tür ek açıklamaları olmayan kodların okunması zor çünkü. Aşağıdaki Python'u düşünün:

def sendData(self, value):
   self.connection.send(serialize(value.someProperty))

Verilerin, bağlantının diğer ucundaki sistemin aldığı gibi görünmesini ne bekliyorsunuz? Ve tamamen yanlış görünen bir şey alıyorsa, neler olduğunu nasıl anlarsınız?

Hepsi yapısına bağlıdır value.someProperty. Ama neye benziyor? İyi soru! Ne çağırıyor sendData()? Ne geçiyor? Bu değişken neye benziyor? Nereden geldi? Yerel değilse, olup valuebitenleri takip etmek için tüm tarih boyunca izlemelisiniz . Belki de somePropertymülkiyeti olan başka bir şeyden geçiyorsunuzdur, fakat düşündüğünüzü yapmaz.

Şimdi buna, benzer bir sözdizimi kullanan ancak statik olarak yazılmış olan Boo dilinde görebileceğiniz gibi tür ek açıklamalarına bakalım:

def SendData(value as MyDataType):
   self.Connection.Send(Serialize(value.SomeProperty))

Yanlış giden bir şey varsa, aniden hata ayıklama işiniz daha büyük bir sipariş aldı: tanımına bakın MyDataType! Ayrıca, kötü davranış alma şansı, aynı adı taşıyan bir özelliği olan uyumsuz bir türden geçtiğinizden aniden sıfıra gider, çünkü tür sistemi bu hatayı yapmanıza izin vermez.

İkinci sebep ilke dayanıyor: Büyük ve karmaşık bir projede, büyük olasılıkla birden fazla katılımcınız var. (Ve eğer değilse, uzun zamandır kendiniz yapıyorsunuz, bu aslında aynı şey. Bana inanmıyorsanız 3 yıl önce yazdığınız kodu okumayı deneyin!) Bu, ne olduğunu bilmediğiniz anlamına gelir. kodun yazıldığı sırada hemen hemen herhangi bir bölümünü yazan kişinin kafasından geçiyordu, çünkü orada değildiniz ya da uzun zaman önce kendi kodunuz olup olmadığını hatırlamıyordunuz. Tip bildirimlerinin olması gerçekten kodun amacının ne olduğunu anlamanıza yardımcı olur!

Alıntıdaki adam gibi insanlar, neredeyse sınırsız donanım kaynaklarının her geçen yıl daha az ve daha az alakalı hale getirdiği bir dünyada, “derleyiciye yardım etme” ya da “tamamen verimlilik hakkında” olmaları gibi statik yazmanın yararlarını sıklıkla yanlış tanımlıyorlar. Ancak gösterdiğim gibi, bu faydalar kesinlikle mevcut olsa da, birincil fayda insan faktörlerinde, özellikle de okunabilirlik ve bakım kolaylığı. (Eklenen verimlilik, yine de kesinlikle güzel bir bonus!)


24
"Bu adam troll oluyor." - Bir reklam hominem saldırısının aksi takdirde iyi sunulan davanıza yardımcı olacağından emin değilim. Ve otorite argümanının ad hominem kadar eşit derecede kötü bir yanılgı olduğunun bilincindeyken, Gilad Bracha'nın muhtemelen daha fazla dil ve (bu tartışma için en uygun olanı) çoğundan daha fazla statik tasarım sistemi tasarladığını belirtmek isterim. Sadece küçük bir alıntı: Java Dil Belirtimi ve Java Sanal Makine Belirtimi'nin ortak yazarı olan Dart'ın ortak tasarımcısı Newspeak'in tek tasarımcısıdır, Java ve JVM'nin tasarımı üzerinde çalışmıştır…
Jörg W Mittag

10
Strongtalk (Smalltalk için statik tip bir sistem), Dart tip sistem, Newspeak tip sistem, modülerlik üzerine doktora tezi hemen hemen her modern modül sisteminin temelidir (örneğin, Java 9, ECMAScript 2015, Scala, Dart, Newspeak, Ioke). , Seph), mixins hakkındaki makaleleri, onlar hakkında düşünme biçimimizde devrim yarattı. Şimdi, bu mu değil haklı olduğu anlamına ama do birden fazla statik tip sistemleri "trol" biraz daha onu yapar tasarlanmış olan düşünüyorum.
Jörg W Mittag

17
“Bu tip sistemler doğru olsa da” sizi bir altküme sınırlandırıyor, “o altkümenin dışındaki öğeler, tanımı gereği, çalışmayan şeyler.” - Bu yanlış. Halting Sorununun Belirlenemezliği, Rice Teoremi ve diğer Belirlenemezlik ve Belirsizliğin sayısızlığından, statik tip kontrolcünün, tüm programlar için tip açısından güvenli veya tip açısından güvensiz olup olmadığına karar veremediğini biliyoruz. Bu programları kabul edemez (bazıları güvenli değildir), bu nedenle tek mantıklı tercih onları reddetmektir (ancak bazıları güvenlidir). Alternatif olarak, dilin…
Jörg W Mittag

9
… Programcının bu kararsız programları yazmasını imkansız kılacak şekilde, ancak yine de bazıları gerçekten güvenlidir. Dolayısıyla, nasıl dilimlendiğiniz önemli değil: programcının güvenli programlar yazması engellenir. Aslında bunların sonsuz sayıda olmadığından Ve (genellikle), biz onları en azından bazıları sadece şeyler olmadığını neredeyse emin olabiliriz yapar işi, aynı zamanda yararlı.
Jörg W Mittag

8
@MasonWheeler: Durma Sorunu, tam olarak statik tip kontrolü bağlamında ortaya çıkar. Diller, programcının belirli tür programları yazmasını engellemek için dikkatlice tasarlanmadıkça, statik tür kontrolü, Hızlanma Sorunu'nu çözme ile hemen eşdeğer olur. Ya onlar tip kontrolörü şaşırtmak olabilir veya programlar ile sona çünkü yazma izin verilmez programlarla sona edilir yazma izin ancak tip onay için zaman sonsuz miktarda alın.
Jörg W Mittag

27

'Model' kısmını bir kenara bırakacağım çünkü bir modelin ne olduğunun tanımı haline geldiğini düşünüyorum ve uzun süredir bu tartışmaya olan ilgimi yitirdim. Söyleyeceğim şey , bazı dillerde, başkalarında yapamayacağınız şeyler olduğu. Ben değilim, Açık konuşayım değildir olmadığını belirten çözebilecek sorunlar başka içinde çözemediği bir dilde. Mason, Turing'in eksiksiz olduğuna dikkat çekti.

Örneğin, python'da bir XML DOM öğesini saran ve birinci sınıf bir nesneye dönüştüren bir sınıf yazdım. Yani, kodu yazabilirsiniz:

doc.header.status.text()

ve bu yolun içeriğini ayrıştırılmış bir XML nesnesinden içeriyorsunuz. temiz ve düzenli, IMO. Ve eğer bir kafa düğümü yoksa, sadece kukla nesnelerden başka hiçbir şey içermeyen kukla nesneler döndürür (tamamen aşağı kaplumbağalar.) Bunu yapmanın gerçek bir yolu yoktur, yani, Java. XML'in yapısına ilişkin bazı bilgilere dayanan bir sınıfı önceden derlemiş olmanız gerekirdi. Bunun iyi bir fikir olup olmadığını bir kenara bırakmak gerekirse, bu tür bir şey, sorunları dinamik bir dilde çözme şeklinizi değiştirir. Bununla birlikte, mutlaka her zaman daha iyi olacak şekilde değiştiğini söylemiyorum. Dinamik yaklaşımların bazı kesin maliyetleri vardır ve Mason'un cevabı makul bir genel bakış sunar. İyi bir seçim olup olmadıkları birçok faktöre bağlıdır.

Bir yan not, sen yapabilirsiniz bir inşa edebilirsiniz, çünkü Java ile bunun Java piton tercüman . Belirli bir dilde belirli bir problemin çözülmesi, tercüman veya buna benzer bir şey inşa etmek anlamına gelebilir, insanlar Turing'in eksiksizliği hakkında konuştuğunda genellikle göz ardı edilir.


4
Bunu Java'da yapamazsınız, çünkü Java kötü tasarlanmıştır. C # kullanarak o kadar zor olmaz IDynamicMetaObjectProviderve Boo'da çok basit olur. ( İşte 100 satırdan daha az bir uygulama , GitHub'taki standart kaynak ağacının bir parçası olarak dahil edildi, çünkü bu kadar kolay!)
Mason Wheeler

6
@MasonWheeler "IDynamicMetaObjectProvider"? Bu, C # dynamicanahtar kelimesiyle ilişkili mi? ... etkili bir şekilde sadece C # 'a dinamik yazarak bağlanıyor? Eğer haklıysam savının geçerli olduğundan emin değilsin.
jpmc26

9
@MasonWheeler Semantics'e giriyorsunuz. Minutia hakkında bir tartışma yapmadan (burada SE ile ilgili matematiksel bir formalizm geliştirmiyoruz.), Dinamik yazma, türler etrafında önceden derleme zamanı kararlarının uygulanması, özellikle her bir türün programın eriştiği belirli üyelere sahip olduğunun doğrulanmasıdır. dynamicC # ile elde edilen amaç budur . "Yansıma ve sözlük aramaları", derleme zamanı değil çalışma zamanında gerçekleşir. Gerçekten o dava için nasıl emin değilim gelmez dile dinamik yazarak ekleyin. Demek istediğim Jimmy'nin son paragrafı bunu kapsıyor.
jpmc26

44
Java büyük bir hayranıyım olmama rağmen, ben de çağıran Java "kötü tasarlanmış" olduğunu söylemek cesaret özellikle dinamik yazarak olduğunu ... overzealous eklemek yoktu çünkü.
jpmc26

5
Biraz daha uygun sözdiziminin yanı sıra, bunun sözlükten farkı nedir?
Theodoros Chatzigiannakis

10

Alıntı doğru, ama aynı zamanda gerçekten de aşağılayıcı. Nedenini görmek için onu parçalayalım:

Dinamik yazma ile ilgili harika bir şey, hesaplanabilir olan her şeyi ifade etmenize izin vermesidir.

Pek iyi değil. Dinamik yazmaya sahip bir dil , Turing tamamlandığı sürece , çoğu olan her şeyi ifade etmenizi sağlar . Tip sisteminin kendisi her şeyi ifade etmenize izin vermez. Ona burada şüphenin faydasını verelim.

Ve yazım sistemleri yazmaz - sistemler genellikle kararlaştırılabilir ve sizi bir alt kümeye kısıtlarlar.

Bu doğru, ancak tür sisteminin kullandığı dilin izin verdiğinden değil , tür sisteminin izin verdiği şeylerden bahsettiğimize dikkat edin . Derleme zamanında bir şeyler hesaplamak için bir tür sistemi kullanmak mümkün olsa da, bu genellikle Turing tamamlanmadı (tür sistemi genel olarak karar verilebildiği için), ancak hemen hemen herhangi bir statik yazılan dil çalışma zamanında tamamlanıyor (bağımlı yazılan diller değil, ama burada onlar hakkında konuştuğumuza inanmıyorum).

Statik tip sistemlerden hoşlananlar “iyi, yeterince iyi; yazmak istediğiniz tüm ilginç programlar tür olarak çalışacaktır ”. Ama bu saçmalık - bir tür sisteminiz olduğunda, hangi ilginç programların olduğunu bile bilmiyorsunuz.

Sorun dinamik olarak tür dillerinin statik bir türe sahip olmalarıdır. Bazen her şey bir dizedir ve daha genel olarak, her şeyin bir özellik çantası veya bir int veya çift gibi bir değer olduğu etiketli bir birlik vardır. Sorun, statik dillerin bunu da yapabilmesidir, tarihsel olarak bunu yapmak biraz zordu, ancak modern statik olarak yazılmış diller bunu dinamik olarak kullanılan bir dil kullanmak kadar kolay hale getiriyordu. programcı ilginç bir program olarak ne görebilir? Statik diller, diğer türlerin yanı sıra tamamen aynı etiketli sendikalara sahiptir.

Başlıktaki soruyu yanıtlamak için: Hayır, statik olarak yazılmış bir dilde uygulanamayan hiçbir tasarım deseni yoktur, çünkü bunları almak için her zaman yeterince dinamik bir sistem uygulayabilirsiniz. Dinamik bir dilde 'özgür' olmanın kalıpları olabilir; bu, YMMV için bu dillerin olumsuzluklarına katlanmaya değer olabilir veya olmayabilir .


2
Evet mi hayır mı diye cevap verip vermediğinden tam olarak emin değilim. Bana daha çok hayır gibi geliyor.
kullanici7610

1
@TheodorosChatzigiannakis Evet, dinamik diller başka nasıl uygulanabilir? Öncelikle, eğer dinamik bir sınıf sistemi veya biraz da dahil başka bir şeyi uygulamak istiyorsanız, bir astronot mimarı için geçeceksiniz. İkincisi, muhtemelen hata ayıklanabilir, tamamen göze çarpmayan, performans gösterecek kaynağa sahip değilsiniz (“sadece bir sözlük kullanın”, dillerin ne kadar yavaş uygulandığı). Üçüncüsü, bazı dinamik özellikler en iyi şekilde sadece bir kütüphane olarak değil, bütün dil ile bütünleşirken kullanılır: örneğin çöp koleksiyonunu düşünün ( kütüphaneler olarak GC'ler var , ancak yaygın olarak kullanılmıyorlar).
coredump,

1
@Theodoros Bir kere zaten buraya bağladığım kağıda göre, yapıların% 2,5'i dışında (Python modüllerinde araştırılan araştırmalar) yazılı bir dilde kolayca ifade edilebilir. Belki% 2,5, buna göre dinamik yazma maliyetini ödüyor. Aslında sorum, bununla ilgili. neverworkintheory.org/2016/06/13/polymorphism-in-python.html
user7610 13:16

3
@JiriDanek Söyleyebileceğim kadarıyla, statik olarak yazılmış bir dilin polimorfik çağrı noktalarına sahip olmasını ve bu sırada statik yazmayı sürdürmesini engelleyen hiçbir şey yok. Bkz . Çoklu Yöntemlerin Statik Tip Kontrolü . Belki de bağlantınızı yanlış anlıyorum.
Theodoros Chatzigiannakis,

1
"Çoğu olan, tam Turing oluyor gibi dinamik yazarak bir dil sürece hiçbir şey ifade sağlar." Bu kurs gerçek ifadenin olmakla birlikte, değil gerçekten çünkü "gerçek dünya" tutun miktar metin birinin sahiptir yazmak çok büyük olabilir.
Daniel Jour,

4

Sadece dinamik olarak yazılmış dillerde yapabileceğin şeyler var. Ama mutlaka iyi bir tasarım olmazlardı .

Önce 5, sonra bir dizgeyi 'five'veya Catnesneyi aynı değişkene atayabilirsiniz . Fakat sadece kodunuzun bir okuyucusunda neler olup bittiğini, her değişkenin amacının ne olduğunu bulmasını zorlaştırıyorsunuz.

Ruby sınıfına bir kitaplık ekleyebilir ve özel alanlarına erişebilirsiniz. Böyle bir kesmenin faydalı olabileceği durumlar olabilir, ancak bu, kapsülleme ihlali olur. (Yalnızca ortak arabirime dayanan yöntemler eklemeyi umursamıyorum, ancak statik olarak yazılan C # uzantısı yöntemlerinin yapamayacağı bir şey değil.)

Etrafında bazı ekstra veriler iletmek için başka birinin sınıfının nesnesine yeni bir alan ekleyebilirsiniz. Ancak, yalnızca yeni bir yapı oluşturmak veya orijinal türünü genişletmek daha iyi bir tasarımdır.

Genel olarak, kodunuzun daha düzenli kalmasını istediğinizde, tür tanımlarını dinamik olarak değiştirebilmeniz veya farklı değişkenlerin değerlerini aynı değişkene atayabilmeniz ne kadar az avantaj sağlar. Fakat kodunuz, statik olarak yazılmış bir dilde elde edebileceğinizden farklı değildir.

Dinamik dillerin iyi olduğu şey, sözdizimsel şekerdir. Örneğin, seri hale getirilmiş bir JSON nesnesini okurken, iç içe geçmiş bir değeri sadece obj.data.article[0].contentsöylenenden daha temiz olarak belirtebilirsiniz obj.getJSONObject("data").getJSONArray("article").getJSONObject(0).getString("content").

Özellikle Ruby geliştiricileri, uzunca bir süredir, uygulanarak elde edilebilecek sihir hakkında konuşabilir method_missing, bu da bildirilmemiş yöntemlere yapılan denemeleri yapmanıza izin veren bir yöntemdir. Örneğin, ActiveRecord ORM, User.find_by_email('joe@example.com')hiç bir find_by_emailyöntem bildirmeden arama yapabilmeniz için kullanır . Elbette UserRepository.FindBy("email", "joe@example.com"), statik olarak yazılmış bir dilde olduğu gibi elde edilemeyecek bir şey değil , ama onun düzenini inkar edemezsin.


4
Sadece statik olarak yazılmış dillerde yapabileceğiniz şeyler vardır. Ama mutlaka iyi bir tasarım olmazlardı.
coredump,

2
Sözdizimsel şeker hakkındaki nokta, dinamik yazma ve bunun sözdizimi ile ilgili her şeyle çok az ilgisi vardır.
leftaroundabout

@leftaroundabout Desenler sözdizimi ile ilgili her şeye sahiptir. Tip sistemleri de bununla ilgisi var.
kullanıcı253751

4

Dinamik Proxy modeli, proxy ihtiyacınız olan tür başına bir sınıfa ihtiyaç duymadan proxy nesnelerini uygulamak için kullanılan bir kısayoldur.

class Proxy(object):
    def __init__(self, obj):
        self.__target = obj

    def __getattr__(self, attr):
        return getattr(self.__target, attr)

Bunu kullanarak Proxy(someObject), aynı davranan yeni bir nesne oluşturur someObject. Açıkçası, bir şekilde ek işlevsellikler eklemek isteyeceksinizdir, ancak bu başlangıç ​​için faydalı bir temeldir. Tam bir statik dilde, proxy yapmak istediğiniz tür başına bir Proxy sınıfı yazmanız veya dinamik kod oluşturmayı kullanmanız gerekir (kuşkusuz, çoğu statik dilin standart kütüphanesine dahil edilmiştir, çünkü tasarımcıları büyük ölçüde bu nedeni yapamamakta olan problemler).

Dinamik dillerin bir başka kullanım durumu da “maymun yama” olarak adlandırılır. Birçok yönden, bu bir anti-desen yerine bir desen, ama olabilir dikkatli yapılırsa yararlı şekilde kullanılabilir. Ve maymun ekinin statik bir dilde uygulanamamasına dair teorik bir sebep olmasa da, aslında hiçbir zaman bir tane görmedim.


Go'da bunu taklit edebileceğimi düşünüyorum. Tüm proxy nesnelerinin sahip olması gereken bir dizi yöntem vardır (aksi halde ördek su sıçramayabilir ve hepsi ayrı düşebilir). Bu yöntemlerle bir Go arayüzü oluşturabilirim. Daha fazla düşünmek zorunda kalacağım, ama aklımdaki şeyin işe yarayacağını düşünüyorum.
user7610

Herhangi bir .NET dilinde benzer bir şeyi RealProxy ve generics ile yapabilirsiniz.
LittleEwok

@LittleEwok - RealProxy çalışma zamanı kodu oluşturma işlevini kullanır - dediğim gibi, birçok modern statik dilin buna benzer bir çözümü vardır, ancak dinamik bir dilde hala daha kolaydır.
Jules,

C # uzatma yöntemleri biraz güvenli yapılmış maymun yama gibi. Mevcut yöntemleri değiştiremezsiniz, ancak yenilerini ekleyebilirsiniz.
Andrew, Monica

3

Evet , yalnızca dinamik olarak yazılmış bir dilde mümkün olan birçok desen ve teknik vardır.

Maymun düzeltme eki , çalışma zamanında nesnelere veya sınıflara özelliklerin veya yöntemlerin eklendiği bir tekniktir. Statik olarak yazılmış bir dilde bu teknik mümkün değildir, çünkü bu, türlerin ve işlemlerin derleme zamanında doğrulanamayacağı anlamına gelir. Ya da başka bir deyişle, eğer bir dil maymun yamalamayı destekliyorsa, tanımı gereği dinamik bir dildir.

Bir dil maymun yamasını destekliyorsa (veya çalışma zamanında türleri değiştirmek için benzer teknikler varsa), statik olarak kontrol edilemediği kanıtlanabilir. Dolayısıyla, şu anda mevcut dillerde sadece bir sınırlama değil, statik yazımın temel bir sınırlamasıdır.

Bu nedenle, alıntı kesinlikle doğrudur - dinamik bir dilde, statik olarak yazılmış bir dilden daha fazla şey mümkündür. Öte yandan, belirli analiz türleri ancak statik olarak yazılmış bir dilde mümkündür. Örneğin, belirli bir türde hangi işlemlere izin verildiğini her zaman bilirsiniz; bu da derleme türünde geçersiz işlemleri algılamanıza olanak sağlar. Çalışma zamanında işlemler eklenip kaldırılabildiğinde dinamik bir dilde böyle bir doğrulama yapılamaz.

Bu nedenle statik ve dinamik diller arasındaki çatışmada açık bir "en iyi" yoktur. Statik diller, derleme zamanında farklı türde bir güç karşılığında çalışma zamanında belirli bir güçten vazgeçerler; bu, böcek sayısını azalttığına ve geliştirmeyi kolaylaştıracağına inanır. Bazıları takasın buna değdiğine inanıyor, bazıları buna değmiyor.

Diğer cevaplar, Turing-denkliğinin, bir dilde mümkün olan her dilde mümkün olduğu anlamına gelir. Ancak bu takip etmiyor. Statik bir dilde maymun eklemesi gibi bir şeyi desteklemek için, temel olarak statik dilin içine dinamik bir alt dil uygulamanız gerekir. Bu elbette mümkün, ancak anadilde mevcut olan statik tip kontrolünü de kaybettiğiniz için gömülü bir dinamik dilde programlama yaptığınızı iddia ediyorum.

C # sürümünden beri dinamik olarak yazılan nesneleri desteklemektedir. Açıkçası, dil tasarımcıları her iki tür yazı tipinin de mevcut olmasında fayda görüyorlar. Ama aynı zamanda pastanıza sahip olamayacağınızı ve ben de yiyemeyeceğinizi gösteriyor: C # 'da dinamik nesneler kullandığınızda maymun yama gibi bir şey yapma kabiliyetine sahip olursunuz, ancak bu nesnelerle etkileşim için statik tip doğrulamasını da kaybedersiniz.


+ 1'inci son paragrafa kadar + 1 sanırım çok önemli bir tartışma. Statik tiplerde olduğu gibi, nerede ve ne yama yapabileceğini tam olarak kontrol edersen
jk.

2

Merak ediyorum, alıntı formülü kullanılarak "tür olarak çalışma" şeklinde faydalı tasarım kalıpları veya stratejileri var mı?

Evet ve hayır.

Programcının bir değişkenin tipini, derleyiciden daha kesin olarak bildiği bir durum vardır. Derleyici bir şeyin bir Nesne olduğunu biliyor olabilir, ancak programcı (programın değişmezleri nedeniyle) gerçekte bir Dize olduğunu bilecektir.

Bunun bazı örneklerini göstereyim:

Map<Class<?>, Function<?, String>> someMap;
someMap.get(object.getClass()).apply(object);

Bazı Map'leri nasıl yapılandırdığımdan dolayı someMap.get(T.class)bunun geri döneceğini biliyorum Function<T, String>. Ancak Java, yalnızca bir İşlevim olduğuna emin.

Başka bir örnek:

data = parseJSON(someJson)
validate(data, someJsonSchema);
print(data.properties.rowCount);

Data.properties.rowCount öğesinin geçerli bir başvuru ve bir tam sayı olacağını biliyorum, çünkü verileri bir şemaya göre doğruladım. Bu alan eksik olsaydı, bir istisna atılırdı. Ancak bir derleyici yalnızca bunun bir istisna attığını veya bir tür genel JSONValue döndürdüğünü bilirdi.

Başka bir örnek:

x, y, z = struct.unpack("II6s", data)

"II6s", verilerin üç değişkeni kodlama şeklini tanımlar. Formatı belirttiğimden beri, hangi tiplerin iade edileceğini biliyorum. Bir derleyici yalnızca bir değer döndürdüğünü bilirdi.

Tüm bu örneklerin birleştirici teması, programcının türü bilmesidir, ancak bir Java seviye tipi sistemi bunu yansıtamaz. Derleyici türlerini bilmeyecek ve böylece statik olarak yazılmış bir dil onları aramama izin vermeyecek, oysa dinamik olarak yazılmış bir dil.

Asıl teklif aldığı şey bu:

Dinamik yazma ile ilgili harika bir şey, hesaplanabilir olan her şeyi ifade etmenize izin vermesidir. Ve yazım sistemleri yazmaz - sistemler genellikle kararlaştırılabilir ve sizi bir alt kümeye kısıtlarlar.

Dinamik yazmayı kullanırken, bildiğim en türetilmiş türü kullanabilirim, sadece dilimin tür sistemini bildiğim en türetilmiş türü değil. Yukarıdaki tüm durumlarda, anlamsal olarak doğru olan ancak statik bir yazma sistemi tarafından reddedilecek olan bir kodum var.

Ancak, sorunuza geri dönmek için:

Merak ediyorum, alıntı formülü kullanılarak "tür olarak çalışma" şeklinde faydalı tasarım kalıpları veya stratejileri var mı?

Yukarıdaki örneklerden herhangi biri ve hatta herhangi bir dinamik yazım örneği, uygun yazımlar eklenerek statik yazımda geçerli olabilir. Derleyicinizin bilmediği bir tür biliyorsanız, derleyiciye değeri kullanarak söyleyin. Bu nedenle, bir düzeyde, dinamik yazmayı kullanarak herhangi bir ek kalıp elde edemezsiniz. Statik olarak yazılmış kodları kullanabilmek için daha fazlasını kullanmanız gerekebilir.

Dinamik yazmanın avantajı, bu tarzları kendi tip sisteminizi geçerliliğine ikna etmek için aldatıcı olduğu gerçeğinden korkmadan kullanabilmenizdir. Kullanılabilir kalıpları değiştirmez, sadece uygulamalarını kolaylaştırır, çünkü yazım sisteminizin kalıbı nasıl tanımasını sağlamanız ya da yazım sistemini altüst etmek için kalıplar eklemek zorunda olmanız gerekmez.


1
Java neden 'daha gelişmiş / karmaşık tip bir sisteme' gitmemesi gereken kesme noktasını oluşturuyor?
jk.

2
@jk, sana söylediğim şeyin bu olduğunu düşündüren ne? Daha gelişmiş / karmaşık tipte bir sistemin değip değmeyeceğine dair taraf almaktan açıkça kaçındım.
Winston Ewert,

2
Bunlardan bazıları korkunç örneklerdir ve diğerleri yazılanlara göre değil, daha çok dil kararları gibi görünmektedir. Özellikle insanların neden serileştirmenin yazılan dillerde bu kadar karmaşık olduğunu düşündüğü konusunda kafam karıştı. Yazılan sonuç olacaktır data = parseJSON<SomeSchema>(someJson); print(data.properties.rowCount); ve birinin seri hale getirilmesi gereken bir sınıf yoksa geri dönebiliriz data = parseJSON(someJson); print(data["properties.rowCount"]);- ki bu hala yazılı ve aynı amacı ifade eder.
NPSF3000,

2
@ NPSF3000, parseJSON işlevi nasıl çalışır? Yansıma veya makro kullanmak gibi görünüyor. ["Properties.rowCount"] verileri statik bir dilde nasıl yazılabilir? Sonuç değerinin bir tamsayı olduğunu nasıl bilebilirdi?
Winston Ewert

2
@ NPSF3000, bir tamsayı bilmiyorsanız kullanmayı nasıl planlıyorsunuz? Bir dizi olduğunu bilmeden JSON'daki bir listedeki öğeler üzerinde döngü yapmayı nasıl planlıyorsunuz? Örneğimin amacı, bunun data.propertiesbir nesne olduğunu biliyordum ve data.properties.rowCountbunun bir tam sayı olduğunu biliyordum ve basitçe onları kullanan bir kod yazabiliyordum. Teklifin data["properties.rowCount"]aynı şeyi sağlamıyor.
Winston Ewert

1

İşte C ++ 'da (statik olarak yazılmış) mümkün olmayan Objective-C'den (dinamik olarak yazılmış) birkaç örnek:

  • Birkaç farklı sınıftaki nesneleri aynı kaba koymak.
    Tabii ki, bu daha sonra kabın içeriğini yorumlamak için çalışma zamanı türü denetimini gerektirir ve statik yazım arkadaşlarının çoğu, bunu en başta yapmamanız gerektiğine itiraz eder. Ancak, dini tartışmaların ötesinde, bunun işe yarayabileceğini buldum.

  • Alt sınıfı kullanmadan bir sınıfı genişletme.
    Objective-C'de, gibi diller de dahil olmak üzere mevcut sınıflar için yeni üye fonksiyonları tanımlayabilirsiniz NSString. Örneğin, bir yöntem ekleyebilirsiniz stripPrefixIfPresent:diyebilirsiniz ki, [@"foo/bar/baz" stripPrefixIfPresent:@"foo/"](kullanımına dikkat NSSringdeğişmezleri @"").

  • Nesne yönelimli geri aramaların kullanımı.
    Java ve C ++ gibi statik olarak yazılmış dillerde, bir kitaplığın kullanıcı tarafından sağlanan bir nesnenin rastgele bir üyesini çağırmasını sağlamak için önemli uzunluklara gitmeniz gerekir. Java'da geçici çözüm, arabirim / bağdaştırıcı çifti artı adsız bir sınıftır, C ++ 'da geçici çözüm genellikle şablon tabanlıdır; bu, kütüphane kodunun kullanıcı koduna maruz kalması gerektiğini gösterir. Objective-C'de, nesne referansını ve metodun seçicisini kütüphaneye iletmeniz yeterlidir; kütüphane basit ve doğrudan geri çağırmayı çağırabilir.


İlk önce C ++ 'ı void *' a çevirerek yapabilirim, ancak bu tip sistemini atlatıyor, bu sayılmaz. C # 'da ikincil yöntemleri, mükemmel bir tür sistem içinde yapabilirim. Üçüncüsü, "yöntem için seçicinin" bir lambda olabileceğini düşünüyorum, bu yüzden doğru anlamış olursam, lambdalarla statik olarak yazılmış herhangi bir dil aynı şeyi yapabilir. ObjC ile aşina değilim.
user7610

1
@JiriDanek "İlk önce C ++ 'ı boşluğa çevirerek yapabilirim *", tam olarak değil, elemanları okuyan kodun gerçek türü kendi başına alması mümkün değil. Yazılı etiketlere ihtiyacınız var. Ayrıca, "Bunu <dilde> yapabilirim" demenin buna bakmak için uygun / üretken bir yol olduğunu düşünmüyorum, çünkü onları her zaman taklit edebilirsiniz. Önemli olan, uygulamanın karmaşıklığına karşı uygulamanın karmaşıklığıdır. Ayrıca, bir dilin hem statik hem de dinamik yetenekleri varsa (Java, C #), bunun yalnızca "statik" dil ailesine ait olduğunu düşünüyor gibi görünüyorsunuz.
coredump,

1
@JiriDanek void*tek başına dinamik yazma değil, yazma eksikliğidir. Ancak evet, dynamic_cast, sanal tablolar vb. C ++ 'ı tamamen statik bir şekilde yazmaz. Kötümü?
coredump,

1
İhtiyaç duyulduğunda tip sistemini altüst etme seçeneğine sahip olmanın yararlı olduğunu göstermektedir. İhtiyacınız olduğunda bir kaçış kapısı olması. Ya da birileri onu yararlı buldu. Aksi halde, dili dile getirmezlerdi.
user7610

2
@JiriDanek Sanırım, son yorumunuzla oldukça çivilenmişsiniz. Bu kaçış kapakları, dikkatli kullanılırsa çok faydalı olabilir. Bununla birlikte, büyük bir güçle birlikte büyük bir sorumluluk kazanır ve birçoğu onu kötüye kullanan insanlardır ... Bu nedenle, diğer tüm sınıfların tanımı gereği türetilmiş olduğu genel bir temel sınıfa bir gösterici kullanmak çok daha iyi hissettirir. Hem Objective-C hem de Java'da) ve void*belirli bir nesne tipine atama yapmak yerine davaları anlatmak için RTTI'ya güvenmek . Eski, karışıklığa uğradıysanız bir çalışma zamanı hatası üretir, daha sonra tanımsız davranışla sonuçlanır.
cmaster
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.