มีรูปแบบการออกแบบที่เป็นไปได้เฉพาะในภาษาที่พิมพ์แบบไดนามิกเช่น Python หรือไม่?


30

ฉันอ่านคำถามที่เกี่ยวข้องมีรูปแบบการออกแบบใดที่ไม่จำเป็นในภาษาไดนามิกเช่น Python หรือไม่? และจดจำคำพูดนี้บน Wikiquote.org

สิ่งมหัศจรรย์เกี่ยวกับการพิมพ์แบบไดนามิกคือช่วยให้คุณสามารถแสดงสิ่งที่คำนวณได้ และประเภทของระบบไม่พิมพ์ - ระบบประเภทมักจะตัดสินใจได้และพวกเขา จำกัด ให้คุณเป็นส่วนย่อย ผู้ที่ชื่นชอบระบบแบบสแตติกพูดว่า“ ใช้ได้เลยมันดีพอ โปรแกรมที่น่าสนใจทั้งหมดที่คุณต้องการเขียนจะใช้งานได้ตามประเภท” แต่นั่นไร้สาระ - เมื่อคุณมีระบบพิมพ์คุณจะไม่รู้ด้วยซ้ำว่าโปรแกรมที่น่าสนใจนั้นมีอะไรบ้าง

--- วิทยุวิศวกรรมซอฟต์แวร์ตอนที่ 140: Newspeak และประเภทที่เสียบได้ด้วย Gilad Bracha

ฉันสงสัยว่ามีรูปแบบการออกแบบหรือกลยุทธ์ที่มีประโยชน์โดยใช้สูตรของคำพูด "ไม่ทำงานเป็นประเภท" หรือไม่?


3
ฉันพบการแจกจ่ายสองครั้งและรูปแบบของผู้เข้าชมเป็นเรื่องยากมากที่จะสำเร็จในภาษาที่พิมพ์แบบคงที่ แต่ทำได้อย่างง่ายดายในภาษาแบบไดนามิก ดูคำตอบนี้ (และคำถาม) ตัวอย่างเช่น: programmers.stackexchange.com/a/288153/122079
3002473

7
แน่นอน. รูปแบบใด ๆ ที่เกี่ยวข้องกับการสร้างคลาสใหม่ที่รันไทม์ตัวอย่างเช่น (นั่นอาจเป็นไปได้ใน Java แต่ไม่ใช่ใน C ++ มันมีสเกลของการเลื่อน)
user253751

1
มันจะขึ้นอยู่กับว่าระบบการพิมพ์ของคุณมีความซับซ้อนเพียงใด :-) ภาษาที่ใช้งานได้มักทำสิ่งนี้ได้ดี
Bergi

1
ดูเหมือนว่าทุกคนจะพูดถึงระบบประเภทเช่น Java และ C # แทน Haskell หรือ OCaml ภาษาที่มีระบบการพิมพ์ที่ทรงพลังสามารถกระชับได้เช่นเดียวกับภาษาไดนามิก แต่รักษาความปลอดภัยของประเภท
แอนดรูพูดว่า Reinstate Monica

@immibis นั่นไม่ถูกต้อง ระบบชนิดสแตติกสามารถสร้างคลาส "ไดนามิก" ใหม่ได้ในเวลาทำงาน ดูบทที่ 33 ของพื้นฐานการปฏิบัติสำหรับการเขียนโปรแกรมภาษา
Gardenhead

คำตอบ:


4

ประเภทเฟิร์สคลาส

การพิมพ์แบบไดนามิกหมายความว่าคุณมีประเภทชั้นหนึ่ง: คุณสามารถตรวจสอบสร้างและจัดเก็บประเภทที่รันไทม์รวมถึงประเภทของภาษาเอง ก็หมายความว่าค่าพิมพ์ไม่ตัวแปร

ภาษาที่พิมพ์แบบคงที่อาจสร้างรหัสที่ขึ้นอยู่กับประเภทแบบไดนามิกเช่นการแจกจ่ายวิธีการเรียนประเภท ฯลฯ แต่ในลักษณะที่มักจะมองไม่เห็นรันไทม์ ที่ดีที่สุดพวกเขาให้วิธีการวิปัสสนา อีกทางหนึ่งคุณสามารถจำลองประเภทเป็นค่าได้ แต่จากนั้นคุณจะมีระบบพิมพ์แบบไดนามิก

อย่างไรก็ตามระบบการพิมพ์แบบไดนามิกมักจะมีเพียงประเภทชั้นแรก คุณสามารถมีสัญลักษณ์ชั้นหนึ่งแพ็คเกจชั้นหนึ่งทุกอย่าง .... สิ่งนี้ตรงกันข้ามกับการแยกอย่างเข้มงวดระหว่างภาษาของคอมไพเลอร์และภาษารันไทม์ในภาษาที่พิมพ์แบบคงที่ สิ่งที่คอมไพเลอร์หรือล่ามสามารถทำรันไทม์ได้เช่นกัน

ทีนี้เรามาตกลงกันว่าการอนุมานประเภทนั้นเป็นสิ่งที่ดีและฉันต้องการให้มีการตรวจสอบโค้ดก่อนเรียกใช้ อย่างไรก็ตามฉันยังชอบที่จะสามารถสร้างและรวบรวมรหัสที่รันไทม์ และฉันก็ชอบที่จะคำนวณสิ่งต่าง ๆ ในเวลารวบรวมด้วยเช่นกัน ในภาษาที่พิมพ์แบบไดนามิกนี้จะทำด้วยภาษาเดียวกัน ใน OCaml คุณมีระบบประเภทโมดูล / functor ซึ่งแตกต่างจากระบบประเภทหลักซึ่งแตกต่างจากภาษาพรีโปรเซสเซอร์ ใน C ++ คุณมีภาษาแม่แบบซึ่งไม่มีส่วนเกี่ยวข้องกับภาษาหลักซึ่งโดยทั่วไปจะไม่รู้ประเภทระหว่างการดำเนินการ และที่ดีในภาษาเหล่านั้นเพราะพวกเขาไม่ต้องการที่จะให้มากขึ้น

ในท้ายที่สุดที่ไม่เปลี่ยนจริงๆสิ่งที่ชนิดของซอฟต์แวร์ที่คุณสามารถพัฒนา แต่ expressivity การเปลี่ยนแปลงวิธีการที่คุณพัฒนาพวกเขาและไม่ว่าจะเป็นเรื่องยากหรือไม่

รูปแบบ

รูปแบบที่ขึ้นอยู่กับประเภทไดนามิกเป็นรูปแบบที่เกี่ยวข้องกับสภาพแวดล้อมแบบไดนามิก: เปิดชั้นเรียนเยี่ยงอย่างฐานข้อมูลในหน่วยความจำของวัตถุเป็นอันดับ ฯลฯ สิ่งที่ง่ายเช่นภาชนะบรรจุทั่วไปทำงานเพราะเวกเตอร์ไม่ลืมรันไทม์เกี่ยวกับชนิดของวัตถุที่เก็บ (ไม่จำเป็นสำหรับประเภทพารามิเตอร์)

ฉันพยายามที่จะแนะนำวิธีการหลายรหัสมีการประเมินใน Common LISP เช่นเดียวกับตัวอย่างของการวิเคราะห์แบบคงที่ที่เป็นไปได้ (นี่คือ SBCL) ตัวอย่าง sandbox รวบรวมชุดย่อยเล็ก ๆ ของรหัส Lisp ที่ดึงมาจากไฟล์แยกต่างหาก เพื่อความปลอดภัยที่สมเหตุสมผลฉันเปลี่ยน readtable อนุญาตเฉพาะเซ็ตย่อยของสัญลักษณ์มาตรฐานและล้อมสิ่งต่าง ๆ ด้วยการหมดเวลา

;;
;; 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}>)

ไม่มีสิ่งใดที่เป็นไปไม่ได้ที่จะทำกับภาษาอื่น วิธีการปลั๊กอินใน Blender ในซอฟต์แวร์เพลงหรือ IDE สำหรับภาษาที่รวบรวมแบบคงที่ซึ่งทำการคอมไพล์แบบออนไลน์ ฯลฯ แทนที่จะใช้เครื่องมือภายนอกภาษาแบบไดนามิกจะใช้เครื่องมือที่ใช้ข้อมูลที่มีอยู่แล้ว ผู้โทรที่รู้จักทั้งหมดของ FOO หรือไม่ คลาสย่อยทั้งหมดของ BAR หรือไม่ วิธีการทั้งหมดที่มีความเชี่ยวชาญโดยชั้น ZOT? นี่เป็นข้อมูลภายใน ประเภทเป็นอีกแง่มุมหนึ่งของสิ่งนี้


(ดูเพิ่มเติมที่: CFFI )


39

คำตอบสั้น ๆ : ไม่เพราะทัวริงเทียบเท่า

คำตอบที่ยาวนาน: ผู้ชายคนนี้กำลังหมุนรอบ ในขณะที่มันเป็นความจริงที่ระบบประเภท "จำกัด คุณให้กับชุดย่อย" สิ่งที่อยู่นอกชุดย่อยนั้นคือโดยนิยามสิ่งที่ไม่ทำงาน

ทุกสิ่งที่คุณสามารถทำได้ในภาษาการเขียนโปรแกรมทัวริงที่สมบูรณ์ (ซึ่งเป็นภาษาที่ออกแบบมาสำหรับการเขียนโปรแกรมวัตถุประสงค์ทั่วไปรวมถึงสิ่งอื่น ๆ ที่ไม่ได้เป็นแถบต่ำที่ค่อนข้างชัดเจนในการล้างและมีหลายตัวอย่างของระบบที่กลายเป็นทัวริง สมบูรณ์โดยไม่ตั้งใจ) คุณสามารถทำในภาษาโปรแกรมทัวริงที่สมบูรณ์อื่น ๆ สิ่งนี้เรียกว่า "ทัวริงเทียบเท่า" และมันหมายถึงสิ่งที่มันพูดเท่านั้น ที่สำคัญไม่ได้หมายความว่าคุณสามารถทำสิ่งอื่น ๆ ได้อย่างง่ายดายในภาษาอื่น - บางคนอาจแย้งว่านั่นเป็นจุดเริ่มต้นในการสร้างภาษาโปรแกรมใหม่ในตอนแรก: เพื่อให้คุณมีวิธีการที่ดีขึ้น สิ่งที่ภาษาที่มีอยู่ดูด

ยกตัวอย่างเช่นระบบพิมพ์แบบไดนามิกสามารถเลียนแบบที่ด้านบนของระบบประเภท OO แบบคงที่เพียงแค่ประกาศตัวแปรพารามิเตอร์และค่าส่งคืนทั้งหมดเป็นObjectประเภทฐานแล้วใช้การสะท้อนเพื่อเข้าถึงข้อมูลเฉพาะภายในดังนั้นเมื่อคุณตระหนักถึงสิ่งนี้ คุณเห็นว่าไม่มีอะไรที่คุณสามารถทำได้ในภาษาไดนามิกที่คุณไม่สามารถทำได้ในภาษาคงที่ แต่การทำอย่างนั้นจะเป็นเรื่องใหญ่แน่นอน

ผู้ชายจากเครื่องหมายคำพูดนั้นถูกต้องว่าประเภทคงที่จะ จำกัด สิ่งที่คุณทำได้ แต่นั่นเป็นคุณสมบัติที่สำคัญไม่ใช่ปัญหา บรรทัดบนถนน จำกัด สิ่งที่คุณสามารถทำได้ในรถของคุณ แต่คุณคิดว่ามันเข้มงวดหรือมีประโยชน์หรือไม่? (ฉันรู้ว่าฉันไม่ต้องการขับรถบนถนนที่วุ่นวายและซับซ้อนซึ่งไม่มีอะไรบอกให้รถวิ่งไปในทิศทางตรงกันข้ามเพื่อไปทางด้านข้างและไม่มาที่ที่ฉันขับรถ!) โดยการตั้งค่ากฎที่อธิบายสิ่งที่ชัดเจน ถือว่าเป็นพฤติกรรมที่ไม่ถูกต้องและทำให้มั่นใจได้ว่ามันจะไม่เกิดขึ้นคุณลดโอกาสที่จะเกิดความผิดพลาดอย่างมาก

นอกจากนี้เขายังเปลี่ยนลักษณะของอีกด้านหนึ่งไปอย่างผิด ๆ ไม่ใช่ว่า "โปรแกรมที่น่าสนใจทั้งหมดที่คุณต้องการเขียนจะทำงานเป็นประเภท" แต่ "โปรแกรมที่น่าสนใจทั้งหมดที่คุณต้องการเขียนจะต้องมีประเภท" เมื่อคุณมีระดับความซับซ้อนเกินกว่าระดับที่กำหนดแล้วจะเป็นการยากมากที่จะรักษา codebase โดยไม่ต้องใช้ระบบพิมพ์เพื่อให้คุณเข้าแถวด้วยเหตุผลสองประการ

ก่อนอื่นเพราะรหัสที่ไม่มีคำอธิบายประกอบประเภทอ่านยาก พิจารณา Python ต่อไปนี้:

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

คุณคาดหวังว่าข้อมูลจะดูเหมือนว่าระบบที่ปลายอีกด้านหนึ่งของการเชื่อมต่อได้รับอะไร และถ้ามันได้รับบางสิ่งที่ดูผิดปกติคุณจะรู้ได้อย่างไรว่าเกิดอะไรขึ้น?

value.somePropertyทั้งหมดขึ้นอยู่กับโครงสร้างของ แต่มันดูเหมือนอะไร? คำถามที่ดี! สิ่งที่เรียกเป็นsendData()? มันกำลังผ่านอะไร ตัวแปรนั้นมีลักษณะอย่างไร มันมาจากไหน ถ้าไม่ใช่ในพื้นที่คุณต้องติดตามประวัติทั้งหมดของvalueการติดตามสิ่งที่เกิดขึ้น บางทีคุณกำลังส่งผ่านสิ่งอื่นที่มีsomePropertyคุณสมบัติ แต่มันไม่ได้ทำในสิ่งที่คุณคิด

ตอนนี้เรามาดูด้วยคำอธิบายประกอบแบบที่คุณอาจเห็นในภาษา Boo ซึ่งใช้ไวยากรณ์ที่คล้ายกันมาก แต่พิมพ์แบบคงที่:

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

หากมีบางอย่างผิดปกติคุณก็จะสามารถแก้ไขข้อบกพร่องได้ง่ายขึ้น: ค้นหาคำจำกัดความของMyDataType! นอกจากนี้โอกาสที่จะได้รับพฤติกรรมที่ไม่ดีเนื่องจากคุณผ่านประเภทที่เข้ากันไม่ได้บางอย่างที่มีคุณสมบัติที่ชื่อเดียวกันเปลี่ยนเป็นศูนย์โดยทันทีเพราะระบบประเภทจะไม่ยอมให้คุณทำผิดพลาด

เหตุผลที่สองสร้างขึ้นในครั้งแรก: ในโครงการขนาดใหญ่และซับซ้อนคุณน่าจะมีผู้สนับสนุนหลายคน (และถ้าไม่คุณกำลังสร้างมันขึ้นมาเองเป็นเวลานานซึ่งเป็นสิ่งเดียวกันลองอ่านโค้ดที่คุณเขียนเมื่อ 3 ปีก่อนถ้าคุณไม่เชื่อฉัน!) ซึ่งหมายความว่าคุณไม่รู้ว่าเกิดอะไรขึ้น ผ่านหัวของบุคคลที่เขียนเกือบทุกส่วนของรหัสในเวลาที่พวกเขาเขียนเพราะคุณไม่ได้อยู่ที่นั่นหรือจำไม่ได้ว่ามันเป็นรหัสของคุณเมื่อนานมาแล้ว การมีการประกาศประเภทจริงๆช่วยให้คุณเข้าใจว่าเจตนาของรหัสคืออะไร!

คนที่ชอบผู้ชายในคำพูดมักจะใช้ประโยชน์จากการพิมพ์แบบคงที่บ่อยๆว่า "ช่วยคอมไพเลอร์" หรือ "หมดประสิทธิภาพ" ในโลกที่ทรัพยากรฮาร์ดแวร์แทบไม่ จำกัด ทำให้สิ่งนี้มีความเกี่ยวข้องน้อยลงในแต่ละปีที่ผ่านมา แต่อย่างที่ฉันได้แสดงให้เห็นในขณะที่ประโยชน์เหล่านั้นมีอยู่จริงประโยชน์หลักอยู่ในปัจจัยมนุษย์โดยเฉพาะอย่างยิ่งการอ่านรหัสและการบำรุงรักษา (ประสิทธิภาพที่เพิ่มขึ้นเป็นโบนัสที่ดีอย่างแน่นอน!)


24
"ผู้ชายคนนี้โทรลล์" - ฉันไม่แน่ใจว่าการโจมตีด้วยโฆษณาจะช่วยกรณีที่คุณนำเสนออย่างดี และในขณะที่ฉันทราบดีว่าการโต้แย้งจากผู้มีอำนาจเป็นความผิดพลาดที่ไม่เท่าเทียมกันเช่นเดียวกับโฆษณาฉันก็ยังอยากจะชี้ให้เห็นว่า Gilad Bracha อาจออกแบบภาษามากกว่าและระบบที่มีความคงที่มากกว่าแบบส่วนใหญ่ ข้อความที่ตัดตอนมาเพียงเล็กน้อย: เขาเป็นผู้ออกแบบ Newspeak แต่เพียงผู้เดียวผู้ร่วมออกแบบของ Dart ผู้ร่วมเขียน Java Language Specification และ Java Virtual Machine Specification ทำงานในการออกแบบ Java และ JVM ออกแบบ ...
Jörg W Mittag

10
Strongtalk (ระบบแบบสแตติกสำหรับ Smalltalk), ระบบ Dart type, ระบบพิมพ์ Newspeak, วิทยานิพนธ์ปริญญาเอกของเขาในแบบแยกส่วนเป็นพื้นฐานของระบบโมดูลที่ทันสมัยทุกระบบ (เช่น Java 9, ECMAScript 2015, Scala, Dart, Newspeak, Ioke , Seph), กระดาษ (s) ของเขาใน mixins ปฏิวัติวิธีที่เราคิดเกี่ยวกับพวกเขา ตอนนี้ไม่ได้หมายความว่าเขาเป็นขวา แต่ฉันทำคิดว่ามีการออกแบบระบบการพิมพ์แบบคงที่หลายที่ทำให้เขาเป็นบิตมากกว่า "โทรลล์"
Jörg W Mittag

17
"แม้ว่ามันจะเป็นความจริงที่ระบบการพิมพ์" จะ จำกัด คุณให้กับชุดย่อย "สิ่งที่อยู่นอกชุดย่อยนั้นคือโดยนิยามสิ่งที่ไม่ทำงาน" - นี่มันผิด เรารู้จากการไม่สามารถตัดสินใจได้ของปัญหาการหยุดชะงักทฤษฎีบทของข้าวและความไม่สามารถในการแยกแยะอื่น ๆ และผลลัพธ์ที่ไม่สามารถตรวจสอบได้จำนวนมากที่เครื่องตรวจสอบชนิดคงที่ไม่สามารถตัดสินใจได้สำหรับโปรแกรมทั้งหมดไม่ว่าจะปลอดภัยหรือไม่ปลอดภัย ไม่สามารถยอมรับโปรแกรมเหล่านี้ได้ (บางโปรแกรมเป็นแบบไม่ปลอดภัย) ดังนั้นทางเลือกเดียวที่มีเหตุผลคือการปฏิเสธโปรแกรมเหล่านั้น (แต่บางโปรแกรมนั้นเป็นแบบไม่ปลอดภัย) อีกทางเลือกหนึ่งภาษาจะต้องได้รับการออกแบบใน ...
Jörg W Mittag

9
…เป็นวิธีที่ทำให้ผู้เขียนโปรแกรมเป็นไปไม่ได้ที่โปรแกรมจะไม่สามารถเขียนได้ แต่ก็เป็นไปไม่ได้ที่โปรแกรมประเภทนี้จะปลอดภัย ดังนั้นไม่ว่าคุณจะเชือดมันอย่างไรโปรแกรมเมอร์ก็ป้องกันไม่ให้เขียนโปรแกรมที่เหมาะสำหรับการพิมพ์ และตั้งแต่มีจริงอนันต์มากของพวกเขา (ปกติ) เราสามารถเป็นเกือบจะแน่ใจว่าอย่างน้อยบางส่วนของพวกเขาเป็นสิ่งที่ไม่เพียง แต่ที่ไม่ทำงาน แต่ยังมีประโยชน์
Jörg W Mittag

8
@MasonWheeler: ปัญหาการหยุดชะงักเกิดขึ้นตลอดเวลาอย่างแม่นยำในบริบทของการตรวจสอบประเภทคงที่ นอกจากว่าภาษาได้รับการออกแบบอย่างระมัดระวังเพื่อป้องกันโปรแกรมเมอร์จากการเขียนโปรแกรมบางประเภทการตรวจสอบชนิดคงที่อย่างรวดเร็วกลายเป็นเทียบเท่ากับการแก้ปัญหาการหยุดชะงัก ไม่ว่าคุณจะจบลงด้วยโปรแกรมที่คุณจะไม่ได้รับอนุญาตให้เขียนเพราะพวกเขาอาจสร้างความสับสนให้ตรวจสอบการพิมพ์หรือคุณจะจบลงด้วยโปรแกรมที่คุณจะได้รับอนุญาตให้เขียน แต่พวกเขาใช้เวลาจำนวนเงินที่ไม่มีที่สิ้นสุดของเวลาในการตรวจสอบประเภท
Jörg W Mittag

27

ฉันจะไปที่ส่วน 'รูปแบบ' ด้านข้างเพราะฉันคิดว่ามัน devolves เป็นคำจำกัดความของสิ่งที่เป็นหรือไม่เป็นรูปแบบและฉันได้สูญเสียความสนใจในการอภิปรายที่ สิ่งที่ฉันจะพูดคือมีสิ่งที่คุณสามารถทำได้ในบางภาษาที่คุณไม่สามารถทำในคนอื่น ๆ ให้ฉันชัดเจนฉันไม่ได้พูดว่ามีปัญหาที่คุณสามารถแก้ไขได้ในภาษาหนึ่งที่คุณไม่สามารถแก้ไขได้ในอีกภาษาหนึ่ง เมสันได้ชี้ให้เห็นทัวริงครบถ้วนแล้ว

ตัวอย่างเช่นฉันได้เขียนคลาสในไพ ธ อนที่ใช้องค์ประกอบ XML DOM และทำให้มันเป็นออบเจ็กต์ชั้นหนึ่ง นั่นคือคุณสามารถเขียนรหัส:

doc.header.status.text()

และคุณมีเนื้อหาของเส้นทางนั้นมาจากวัตถุ XML แยกวิเคราะห์ เรียบร้อยและเป็นระเบียบเรียบร้อย IMO และถ้าไม่มีโหนดใหญ่ก็แค่คืนค่าวัตถุจำลองที่ไม่มีอะไรเลยนอกจากวัตถุจำลอง (เต่าลงมาจนสุด) ไม่มีทางที่จะทำเช่นนั้นจริง ๆ ได้ Java คุณต้องรวบรวมคลาสล่วงหน้าตามความรู้เกี่ยวกับโครงสร้างของ XML ทิ้งไว้ไม่ว่าจะเป็นความคิดที่ดีสิ่งนี้จะเปลี่ยนวิธีการแก้ปัญหาของคุณในภาษาไดนามิก ฉันไม่ได้บอกว่ามันจะเปลี่ยนไปในทางที่ดีกว่าเสมอไป มีค่าใช้จ่ายที่แน่นอนสำหรับวิธีการแบบไดนามิกและคำตอบของเมสันให้ภาพรวมที่ดี ไม่ว่าพวกเขาจะเป็นทางเลือกที่ดีหรือไม่นั้นขึ้นอยู่กับหลายปัจจัย

สังเกตด้านบนคุณสามารถทำเช่นนี้ใน Java เพราะคุณสามารถสร้างล่ามหลามในชวา ความจริงที่ว่าการแก้ปัญหาเฉพาะในภาษาที่กำหนดอาจหมายถึงการสร้างล่ามหรือสิ่งที่คล้ายกับมันมักจะมองข้ามเมื่อผู้คนพูดถึงทัวริงสมบูรณ์


4
คุณไม่สามารถทำสิ่งนี้ใน Java ได้เพราะ Java ออกแบบมาไม่ดี มันคงไม่ยากนักในการใช้ C # IDynamicMetaObjectProviderและมันง่ายมากใน Boo ( นี่คือการดำเนินการในเวลาน้อยกว่า 100 เส้นรวมเป็นส่วนหนึ่งของแหล่งต้นไม้มาตรฐานใน GitHub เพราะมันเป็นเรื่องที่ง่าย!)
เมสันล้อ

6
@MasonWheeler "IDynamicMetaObjectProvider"? สิ่งนี้เกี่ยวข้องกับdynamicคำหลักของ C # หรือไม่ ... ซึ่งมีประสิทธิภาพเพียง tacks ในการพิมพ์แบบไดนามิกเพื่อ C #? ไม่แน่ใจว่าข้อโต้แย้งของคุณถูกต้องถ้าฉันพูดถูก
jpmc26

9
@MasonWheeler คุณกำลังเข้าสู่ความหมาย โดยไม่ต้องมีการถกเถียงเกี่ยวกับ minutiae (เราไม่ได้พัฒนาวิธีการทางคณิตศาสตร์ใน SE ที่นี่) การพิมพ์แบบไดนามิกเป็นวิธีที่ใช้ในการตัดสินใจเกี่ยวกับการรวบรวมเวลาโดยเฉพาะการตรวจสอบว่าแต่ละประเภทมีสมาชิกเฉพาะที่โปรแกรมเข้าถึง นั่นคือเป้าหมายที่dynamicสำเร็จใน C # "การสะท้อนและการค้นหาพจนานุกรม" เกิดขึ้นที่รันไทม์ไม่ใช่เวลารวบรวม ฉันไม่แน่ใจจริงๆว่าคุณจะทำอย่างไรในกรณีที่ไม่เพิ่มการพิมพ์แบบไดนามิกในภาษา ประเด็นของฉันคือย่อหน้าสุดท้ายของ Jimmy ครอบคลุมเรื่องนั้น
jpmc26

44
แม้จะไม่ได้เป็นแฟนตัวยงของจาวา แต่ฉันก็กล้าพูดว่าการเรียกจาวาว่า "ออกแบบมาไม่ดี" โดยเฉพาะเพราะมันไม่ได้เพิ่มการพิมพ์แบบไดนามิกคือ ... overzealous
jpmc26

5
นอกเหนือจากไวยากรณ์ที่สะดวกกว่าเล็กน้อยแล้วสิ่งนี้แตกต่างจากพจนานุกรมอย่างไร
Theodoros Chatzigiannakis

10

คำพูดนั้นถูกต้อง แต่ก็ไม่ตรงตามความเป็นจริง มาแยกกันดูกันว่าทำไม:

สิ่งมหัศจรรย์เกี่ยวกับการพิมพ์แบบไดนามิกคือช่วยให้คุณสามารถแสดงสิ่งที่คำนวณได้

ก็ไม่มาก ภาษากับการพิมพ์แบบไดนามิกช่วยให้คุณสามารถแสดงอะไรตราบใดที่มันเป็นทัวริงสมบูรณ์ซึ่งส่วนใหญ่จะเป็น ระบบประเภทนั้นไม่อนุญาตให้คุณแสดงทุกสิ่ง ลองให้ประโยชน์แก่เขาจากความสงสัยที่นี่

และประเภทของระบบไม่พิมพ์ - ระบบประเภทมักจะตัดสินใจได้และพวกเขา จำกัด ให้คุณเป็นส่วนย่อย

นี่เป็นเรื่องจริง แต่ขอให้สังเกตว่าขณะนี้เรากำลังพูดถึงอย่างแน่นหนาเกี่ยวกับสิ่งที่ระบบการพิมพ์อนุญาตไม่ใช่ภาษาที่ใช้ระบบการพิมพ์อนุญาต ในขณะที่มีความเป็นไปได้ที่จะใช้ระบบพิมพ์เพื่อคำนวณสิ่งต่าง ๆ ในเวลารวบรวมสิ่งนี้โดยทั่วไปไม่ใช่ทัวริงสมบูรณ์ (ในขณะที่ระบบประเภททั่วไปคือ decidable) แต่เกือบทุกภาษาที่พิมพ์แบบคงที่ยังเป็นทัวริง ไม่ใช่ แต่ฉันไม่เชื่อว่าเรากำลังพูดถึงสิ่งเหล่านี้)

ผู้ที่ชื่นชอบระบบแบบสแตติกพูดว่า“ ใช้ได้เลยมันดีพอ โปรแกรมที่น่าสนใจทั้งหมดที่คุณต้องการเขียนจะใช้งานได้ตามประเภท” แต่นั่นไร้สาระ - เมื่อคุณมีระบบพิมพ์คุณจะไม่รู้ด้วยซ้ำว่าโปรแกรมที่น่าสนใจนั้นมีอะไรบ้าง

ปัญหาคือภาษาประเภทแบบไดนามิกมีประเภทคงที่ บางครั้งทุกอย่างเป็นสตริงและโดยทั่วไปจะมีการรวมกลุ่มที่ติดแท็กซึ่งทุกสิ่งเป็นทั้งถุงสมบัติหรือค่าเช่น int หรือ double ปัญหาก็คือภาษาแบบสแตติกสามารถทำสิ่งนี้ได้เช่นกันในอดีตมันค่อนข้างแปลกที่จะทำเช่นนี้ แต่ภาษาที่พิมพ์แบบสแตติกสมัยใหม่ทำให้สิ่งนี้ง่ายมากที่จะทำเช่นเดียวกับการใช้ภาษาแบบไดนามิกดังนั้นจึงมีความแตกต่าง สิ่งที่โปรแกรมเมอร์เห็นว่าเป็นโปรแกรมที่น่าสนใจ ภาษาแบบคงที่มีสหภาพที่ติดแท็กเหมือนกันเช่นเดียวกับประเภทอื่น ๆ

ในการตอบคำถามในชื่อเรื่อง: ไม่ไม่มีรูปแบบการออกแบบที่ไม่สามารถนำไปใช้ในภาษาที่พิมพ์แบบคงที่ได้เพราะคุณสามารถนำระบบไดนามิกมาใช้ให้เกิดประโยชน์ได้เสมอ อาจมีรูปแบบที่คุณได้รับ 'ฟรี' ในภาษาไดนามิก นี้อาจจะหรืออาจจะไม่คุ้มค่าการวางกับข้อเสียของภาษาเหล่านั้นสำหรับYMMV


2
ฉันไม่แน่ใจว่าคุณตอบว่าใช่หรือไม่ ฟังดูไม่เหมือนฉัน
user7610

1
@TheodorosChatzigiannakis ใช่ภาษาแบบไดนามิกจะมีการนำไปใช้อย่างไร ก่อนอื่นคุณจะต้องผ่านการเป็นสถาปนิกนักบินอวกาศถ้าคุณต้องการใช้ระบบคลาสแบบไดนามิกหรืออะไรก็ตามที่เกี่ยวข้อง ประการที่สองคุณอาจไม่มีทรัพยากรที่จะทำการ debuggable, introspectable, performant อย่างสมบูรณ์ ("ใช้เพียงพจนานุกรม" คือการใช้ภาษาที่ช้า) ประการที่สามคุณสมบัติแบบไดนามิกบางส่วนมีการใช้ที่ดีที่สุดเมื่อถูกรวมในภาษาทั้งไม่เพียง แต่เป็นห้องสมุด: คิดว่าการเก็บขยะเช่น (มีอยู่ GCs ห้องสมุด แต่พวกเขาจะไม่นิยมใช้)
coredump

1
@Theodoros จากบทความที่ฉันเชื่อมโยงไว้ที่นี่เพียงครั้งเดียวทั้งหมด 2.5% ของโครงสร้าง (ในโมดูล Python ที่การวิจัยดู) สามารถแสดงได้อย่างง่ายดายในภาษาที่พิมพ์ บางที 2.5% อาจจ่ายค่าใช้จ่ายในการพิมพ์แบบไดนามิกที่คุ้มค่า นั่นคือสิ่งที่คำถามของฉันเกี่ยวกับ neverworkintheory.org/2016/06/13/polymorphism-in-python.html
user7610

3
@JiriDanek เท่าที่ฉันสามารถบอกได้ว่าไม่มีอะไรที่ป้องกันภาษาที่พิมพ์แบบคงที่จากการมีจุดโทร polymorphic และคงการพิมพ์แบบคงที่ในกระบวนการ ดูแบบคงที่ประเภทการตรวจสอบของหลายวิธี บางทีฉันอาจจะเข้าใจผิดลิงค์ของคุณ
Theodoros Chatzigiannakis

1
"ภาษากับการพิมพ์แบบไดนามิกช่วยให้คุณสามารถแสดงอะไรตราบใดที่มันเป็นทัวริงสมบูรณ์ซึ่งส่วนใหญ่จะเป็น." ขณะนี้เป็นที่แน่นอนคำสั่งที่จริงมันไม่ได้จริงๆถืออยู่ใน "โลกจริง" เพราะจำนวนหนึ่งข้อความที่มี การเขียนอาจมีขนาดใหญ่มาก
Daniel Jour

4

มีบางสิ่งที่คุณสามารถทำได้ในภาษาที่พิมพ์แบบไดนามิกเท่านั้น แต่พวกเขาไม่จำเป็นต้องมีการออกแบบที่ดี

คุณอาจกำหนดจำนวนเต็ม 5 ก่อนจากนั้นสตริง'five'หรือCatวัตถุให้กับตัวแปรเดียวกัน แต่คุณจะทำให้มันยากขึ้นสำหรับผู้อ่านรหัสของคุณที่จะคิดออกว่าเกิดอะไรขึ้นวัตถุประสงค์ของตัวแปรทุกตัวคืออะไร

คุณอาจเพิ่มวิธีการใหม่ในไลบรารีคลาส Ruby และเข้าถึงฟิลด์ส่วนตัว อาจมีบางกรณีที่แฮ็คนั้นมีประโยชน์ แต่นี่อาจเป็นการละเมิดการห่อหุ้ม (ฉันไม่รังเกียจการเพิ่มเมธอดที่อาศัยอินเทอร์เฟซสาธารณะเท่านั้น แต่นั่นไม่ใช่สิ่งที่วิธีการขยาย C # แบบคงที่ไม่สามารถทำได้)

คุณอาจเพิ่มเขตข้อมูลใหม่ลงในวัตถุของคลาสของคนอื่นเพื่อส่งข้อมูลพิเศษรอบ ๆ แต่มันเป็นการออกแบบที่ดีกว่าเพียงแค่สร้างโครงสร้างใหม่หรือขยายชนิดดั้งเดิม

โดยทั่วไปยิ่งมีระเบียบมากขึ้นคุณต้องการให้โค้ดของคุณอยู่ประโยชน์ที่น้อยลงที่คุณควรทำคือไม่สามารถเปลี่ยนนิยามชนิดแบบไดนามิกหรือกำหนดค่าของชนิดต่าง ๆ ให้กับตัวแปรเดียวกันได้ แต่รหัสของคุณไม่แตกต่างจากสิ่งที่คุณสามารถทำได้ในภาษาที่พิมพ์แบบคงที่

ภาษาแบบไดนามิกที่ดีคือน้ำตาลประโยค ตัวอย่างเช่นเมื่ออ่านวัตถุ JSON deserialized คุณอาจจะหมายถึงค่าที่ซ้อนกันเป็นเพียงobj.data.article[0].content- neater obj.getJSONObject("data").getJSONArray("article").getJSONObject(0).getString("content")กว่าพูด

นักพัฒนาทับทิมโดยเฉพาะสามารถพูดคุยเกี่ยวกับเวทมนตร์ที่สามารถทำได้โดยการใช้method_missingซึ่งเป็นวิธีที่ช่วยให้คุณสามารถจัดการกับการโทรที่พยายามไปยังวิธีที่ไม่ได้ประกาศ ตัวอย่างเช่น ActiveRecord ORM ใช้เพื่อให้คุณสามารถโทรออกUser.find_by_email('joe@example.com')โดยไม่ต้องประกาศfind_by_emailวิธีการ แน่นอนว่ามันไม่มีอะไรที่ไม่สามารถเข้าใจได้เหมือนUserRepository.FindBy("email", "joe@example.com")ในภาษาที่พิมพ์แบบคงที่ แต่คุณไม่สามารถปฏิเสธได้ว่ามันเรียบร้อย


4
มีบางสิ่งที่คุณสามารถทำได้ในภาษาที่พิมพ์แบบคงที่เท่านั้น แต่พวกเขาไม่จำเป็นต้องออกแบบดี
coredump

2
ประเด็นเกี่ยวกับวากยสัมพันธ์น้ำตาลมีน้อยมากที่เกี่ยวกับการพิมพ์แบบไดนามิกและทุกสิ่งที่มีไวยากรณ์
leftaroundabout

@leftaroundabout Patterns มีทุกสิ่งที่เกี่ยวข้องกับไวยากรณ์ ระบบประเภทยังมีจำนวนมากที่จะทำกับมัน
user253751

4

รูปแบบไดนามิกพร็อกซีเป็นทางลัดสำหรับการนำวัตถุพร็อกซีไปใช้โดยไม่จำเป็นต้องมีหนึ่งคลาสต่อประเภทที่คุณต้องการใช้กับพร็อกซี

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

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

ใช้นี้สร้างวัตถุใหม่ที่มีลักษณะการทำงานเช่นเดียวกับProxy(someObject) someObjectเห็นได้ชัดว่าคุณต้องการเพิ่มฟังก์ชันการทำงานเพิ่มเติม แต่อย่างใดนี่เป็นพื้นฐานที่มีประโยชน์สำหรับการเริ่มต้น ในภาษาสแตติกที่สมบูรณ์คุณจะต้องเขียนคลาสพร็อกซีหนึ่งคลาสต่อประเภทที่คุณต้องการใช้พร็อกซีหรือใช้การสร้างรหัสแบบไดนามิก (ซึ่งเป็นที่ยอมรับรวมอยู่ในไลบรารีมาตรฐานของภาษาแบบคงที่หลายแห่ง ปัญหาที่ไม่สามารถทำได้ด้วยสาเหตุนี้)

อีกกรณีการใช้ภาษาแบบไดนามิกนั้นเรียกว่า "การปะของลิง" ในหลาย ๆ ด้านนี่เป็นรูปแบบการต่อต้านมากกว่ารูปแบบ แต่สามารถใช้ในรูปแบบที่มีประโยชน์หากทำอย่างระมัดระวัง และในขณะที่ไม่มีเหตุผลทางทฤษฎีในการปะแก้ลิงไม่สามารถนำมาใช้ในภาษาแบบคงที่ได้ฉันไม่เคยเห็นคนที่มีอยู่จริง


ฉันคิดว่าฉันสามารถเลียนแบบสิ่งนี้ได้ในโก มีชุดของวิธีการที่วัตถุพร็อกซีทั้งหมดจะต้องมี (มิฉะนั้นเป็ดอาจจะไม่ต้มตุ๋นและแยกออกจากกัน) ฉันสามารถสร้างส่วนต่อประสาน Go ด้วยวิธีการเหล่านี้ ฉันจะต้องคิดให้มากกว่านี้ แต่ฉันคิดว่าสิ่งที่ฉันมีในใจจะได้ผล
user7610

คุณสามารถทำอะไรที่คล้ายกันในภาษา. NET ด้วย RealProxy และ generics
LittleEwok

@LittleEwok - RealProxy ใช้การสร้างรหัสรันไทม์ - เช่นที่ฉันพูดภาษาคงที่ที่ทันสมัยจำนวนมากมีวิธีการแก้ปัญหาเช่นนี้ แต่ก็ยังคงง่ายขึ้นในภาษาแบบไดนามิก
จูลส์

วิธีการขยาย C # ค่อนข้างจะเหมือนกับการปะของลิงทำให้ปลอดภัย คุณไม่สามารถเปลี่ยนวิธีการที่มีอยู่ แต่คุณสามารถเพิ่มวิธีการใหม่ได้
แอนดรูพูดว่า Reinstate Monica

3

ใช่มีหลายรูปแบบและเทคนิคที่เป็นไปได้เฉพาะในภาษาที่พิมพ์แบบไดนามิก

Monkey patchingเป็นเทคนิคที่คุณสมบัติหรือวิธีการเพิ่มวัตถุหรือคลาสที่รันไทม์ เทคนิคนี้เป็นไปไม่ได้ในภาษาที่พิมพ์แบบคงที่เนื่องจากวิธีนี้ไม่สามารถตรวจสอบประเภทและการใช้งานได้ในเวลารวบรวม หรือนำไปใช้ในทางอื่นหากภาษารองรับการแก้ไขโดยลิงเป็นคำจำกัดความของภาษาแบบไดนามิก

มันสามารถพิสูจน์ได้ว่าถ้าภาษารองรับการปะของลิง (หรือเทคนิคที่คล้ายกันเพื่อแก้ไขชนิดที่รันไทม์) มันไม่สามารถตรวจสอบประเภทแบบคงที่ ดังนั้นมันไม่ได้เป็นเพียงข้อ จำกัด ในภาษาที่มีอยู่ในปัจจุบันมันเป็นข้อ จำกัด พื้นฐานของการพิมพ์แบบคงที่

ดังนั้นการเสนอราคานั้นถูกต้องแน่นอน - มีความเป็นไปได้มากขึ้นในภาษาไดนามิกมากกว่าภาษาที่พิมพ์แบบคงที่ ในทางกลับกันการวิเคราะห์บางประเภทเป็นไปได้เฉพาะในภาษาที่พิมพ์แบบคงที่ ตัวอย่างเช่นคุณรู้อยู่เสมอว่าการดำเนินงานใดได้รับอนุญาตในประเภทที่กำหนดซึ่งช่วยให้คุณตรวจสอบการดำเนินงานที่ผิดกฎหมายในประเภทคอมไพล์ ไม่มีการตรวจสอบดังกล่าวในภาษาแบบไดนามิกเมื่อสามารถเพิ่มหรือลบการดำเนินการได้ในขณะใช้งานจริง

นี่คือเหตุผลที่ไม่มีความชัดเจน "ดีที่สุด" ในความขัดแย้งระหว่างภาษาแบบคงที่และแบบไดนามิก ภาษาแบบคงที่ให้พลังงานบางอย่างที่รันไทม์ในการแลกเปลี่ยนสำหรับพลังงานที่แตกต่างกันในเวลารวบรวมซึ่งพวกเขาเชื่อว่าลดจำนวนข้อบกพร่องและทำให้การพัฒนาง่ายขึ้น บางคนเชื่อว่าการแลกเปลี่ยนมีความคุ้มค่า

คำตอบอื่น ๆ ที่ถกเถียงกันว่าทัวริงเทียบเท่าหมายความว่าอะไรเป็นไปได้ในหนึ่งภาษาเป็นไปได้ในทุกภาษา แต่สิ่งนี้ไม่ปฏิบัติตาม เพื่อสนับสนุนบางอย่างเช่นการปะของลิงในภาษาแบบคงที่คุณจะต้องใช้ภาษาย่อยแบบไดนามิกภายในภาษาแบบคงที่ เป็นไปได้แน่นอน แต่ฉันจะโต้แย้งว่าคุณกำลังเขียนโปรแกรมในภาษาไดนามิกฝังตัวเนื่องจากคุณสูญเสียการตรวจสอบประเภทคงที่ซึ่งมีอยู่ในภาษาโฮสต์

C # เนื่องจากรุ่น 4 รองรับวัตถุที่พิมพ์แบบไดนามิก เห็นได้ชัดว่านักออกแบบภาษาเห็นประโยชน์ในการพิมพ์ทั้งสองชนิด แต่มันก็แสดงให้เห็นว่าคุณไม่สามารถมีเค้กและกินฉันได้เช่นกัน: เมื่อคุณใช้วัตถุแบบไดนามิกใน C # คุณจะได้รับความสามารถในการทำสิ่งต่าง ๆ เช่นการปะลิง แต่คุณก็สูญเสียการตรวจสอบชนิดคงที่สำหรับการโต้ตอบกับวัตถุเหล่านี้


+1 ของคุณสองย่อหน้าสุดท้ายฉันคิดว่าเป็นข้อโต้แย้งที่สำคัญ ฉันยังคงยืนยันว่ามีความแตกต่างเหมือนกันกับประเภทคงที่คุณสามารถควบคุมได้อย่างเต็มที่ว่าที่ไหนและสิ่งที่คุณสามารถแก้ไขได้
jk

2

ฉันสงสัยว่ามีรูปแบบการออกแบบหรือกลยุทธ์ที่มีประโยชน์โดยใช้สูตรของคำพูด "ไม่ทำงานเป็นประเภท" หรือไม่?

ใช่และไม่.

มีสถานการณ์ที่โปรแกรมเมอร์รู้ชนิดของตัวแปรที่มีความแม่นยำมากกว่าคอมไพเลอร์ คอมไพเลอร์อาจรู้ว่ามีบางอย่างเป็นวัตถุ แต่โปรแกรมเมอร์จะรู้ (เนื่องจากค่าคงที่ของโปรแกรม) ว่าจริง ๆ แล้วเป็นสตริง

ให้ฉันแสดงตัวอย่างของสิ่งนี้:

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

ฉันรู้ว่าsomeMap.get(T.class)จะกลับมาFunction<T, String>เพราะฉันสร้างบางแผนที่ แต่ Java มั่นใจว่าฉันมีฟังก์ชั่นเท่านั้น

ตัวอย่างอื่น:

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

ฉันรู้ว่า data.properties.rowCount จะเป็นการอ้างอิงที่ถูกต้องและจำนวนเต็มเพราะฉันตรวจสอบข้อมูลกับสคีมาแล้ว หากฟิลด์นั้นหายไปจะมีการโยนข้อยกเว้น แต่คอมไพเลอร์จะรู้ว่าเป็นการโยนข้อยกเว้นหรือคืนค่า JSONValue ทั่วไปบางประเภท

ตัวอย่างอื่น:

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

"II6s" กำหนดวิธีการเข้ารหัสข้อมูลตัวแปรสามตัว เนื่องจากฉันระบุรูปแบบฉันจึงทราบว่าจะส่งคืนประเภทใด คอมไพเลอร์จะรู้ว่ามันส่งคืน tuple เท่านั้น

ชุดรูปแบบรวมของตัวอย่างเหล่านี้ทั้งหมดคือโปรแกรมเมอร์รู้ชนิด แต่ระบบชนิดระดับ Java จะไม่สามารถสะท้อนได้ คอมไพเลอร์จะไม่ทราบชนิดและภาษาที่พิมพ์แบบคงที่จะไม่ให้ฉันเรียกพวกเขาในขณะที่ภาษาที่พิมพ์แบบไดนามิกจะ

นั่นคือสิ่งที่ต้นฉบับพูดถึงที่:

สิ่งมหัศจรรย์เกี่ยวกับการพิมพ์แบบไดนามิกคือช่วยให้คุณสามารถแสดงสิ่งที่คำนวณได้ และประเภทของระบบไม่พิมพ์ - ระบบประเภทมักจะตัดสินใจได้และพวกเขา จำกัด ให้คุณเป็นส่วนย่อย

เมื่อใช้การพิมพ์แบบไดนามิกฉันสามารถใช้ประเภทที่ได้รับมากที่สุดที่ฉันรู้ได้ไม่ใช่เพียงแค่ระบบการพิมพ์ภาษาที่ฉันรู้จักมากที่สุด ในทุกกรณีข้างต้นฉันมีรหัสที่ถูกต้องทางความหมาย แต่จะถูกปฏิเสธโดยระบบการพิมพ์แบบคงที่

อย่างไรก็ตามหากต้องการกลับไปที่คำถามของคุณ:

ฉันสงสัยว่ามีรูปแบบการออกแบบหรือกลยุทธ์ที่มีประโยชน์โดยใช้สูตรของคำพูด "ไม่ทำงานเป็นประเภท" หรือไม่?

ตัวอย่างใด ๆ ข้างต้นและแน่นอนตัวอย่างใด ๆ ของการพิมพ์แบบไดนามิกสามารถทำให้ถูกต้องในการพิมพ์แบบคงที่โดยการเพิ่มบรรยากาศที่เหมาะสม ถ้าคุณรู้ว่าคอมไพเลอร์ของคุณไม่มีประเภทให้บอกคอมไพเลอร์ด้วยการส่งค่า ดังนั้นในบางระดับคุณจะไม่ได้รับรูปแบบเพิ่มเติมใด ๆ โดยใช้การพิมพ์แบบไดนามิก คุณอาจต้องเพิ่มมากขึ้นเพื่อให้ได้รหัสที่พิมพ์แบบคงที่

ข้อได้เปรียบของการพิมพ์แบบไดนามิกคือคุณสามารถใช้รูปแบบเหล่านี้ได้โดยไม่ต้องกังวลกับความจริงที่ว่ามันยากที่จะโน้มน้าวให้ระบบการพิมพ์ของคุณใช้งานได้จริง มันไม่ได้เปลี่ยนรูปแบบที่มีอยู่มันอาจจะทำให้ง่ายต่อการนำไปใช้เพราะคุณไม่ต้องคิดหาวิธีที่จะทำให้ระบบพิมพ์ของคุณจดจำรูปแบบหรือเพิ่ม cast cast เพื่อล้มล้างระบบพิมพ์


1
ทำไม java เป็นจุดตัดที่คุณไม่ควรไปที่ 'ระบบพิมพ์ที่ซับซ้อน / ซับซ้อนมากขึ้น'
jk

2
@jk อะไรทำให้คุณคิดว่านั่นคือสิ่งที่ฉันพูด ฉันหลีกเลี่ยงอย่างชัดเจนในการพิจารณาว่าระบบประเภทที่ก้าวหน้า / ซับซ้อนกว่านั้นคุ้มค่าหรือไม่
Winston Ewert

2
สิ่งเหล่านี้บางอย่างเป็นตัวอย่างที่แย่มากและคนอื่น ๆ ดูเหมือนจะเป็นการตัดสินใจทางภาษามากกว่าที่จะพิมพ์หรือไม่พิมพ์ ฉันสับสนเป็นพิเศษว่าทำไมผู้คนถึงคิดว่าการดีซีเรียลไลเซชั่นนั้นซับซ้อนมากในภาษาที่พิมพ์ ผลลัพธ์ที่พิมพ์ออกมาน่าจะเป็นdata = parseJSON<SomeSchema>(someJson); print(data.properties.rowCount); และถ้าหากเราไม่มีคลาสที่จะเลิกเรียนเราสามารถถอยกลับไปได้data = parseJSON(someJson); print(data["properties.rowCount"]);- ซึ่งยังคงพิมพ์และแสดงออกถึงเจตนาเดียวกัน
NPSF3000

2
@ NPSF3000 ฟังก์ชัน parseJSON ทำงานอย่างไร ดูเหมือนว่าจะใช้การสะท้อนหรือมาโคร ข้อมูล ["Properties.rowCount"] สามารถพิมพ์เป็นภาษาแบบคงที่ได้อย่างไร จะทราบได้อย่างไรว่าค่าผลลัพธ์เป็นจำนวนเต็ม?
Winston Ewert

2
@ NPSF3000 คุณวางแผนใช้งานอย่างไรถ้าคุณไม่รู้จำนวนเต็ม? คุณวางแผนที่จะวนลูปมากกว่าองค์ประกอบในรายการใน JSON โดยไม่ทราบว่ามันเป็นอาร์เรย์อย่างไร จุดตัวอย่างของฉันคือการที่ฉันรู้ว่าdata.propertiesเป็นวัตถุและฉันรู้ว่าdata.properties.rowCountเป็นจำนวนเต็มและฉันก็สามารถเขียนรหัสที่ใช้พวกเขา ข้อเสนอของคุณdata["properties.rowCount"]ไม่ได้ให้สิ่งเดียวกัน
Winston Ewert

1

นี่คือตัวอย่างบางส่วนจาก Objective-C (พิมพ์แบบไดนามิก) ซึ่งเป็นไปไม่ได้ใน C ++ (พิมพ์แบบคงที่):

  • การวางวัตถุของคลาสที่แตกต่างกันหลายอย่างลงในคอนเทนเนอร์เดียวกัน
    แน่นอนว่าต้องใช้การตรวจสอบชนิดรันไทม์เพื่อตีความเนื้อหาของคอนเทนเนอร์ในภายหลังและเพื่อนส่วนใหญ่ของการพิมพ์แบบสแตติกจะคัดค้านว่าคุณไม่ควรทำสิ่งนี้ตั้งแต่แรก แต่ฉันได้พบว่านอกเหนือจากการถกเถียงทางศาสนาสิ่งนี้มีประโยชน์

  • การขยายคลาสโดยไม่มีคลาสย่อย
    ใน Objective-C, NSStringคุณสามารถกำหนดฟังก์ชั่นสมาชิกใหม่สำหรับชั้นเรียนที่มีอยู่รวมทั้งคนที่ภาษาที่กำหนดไว้เช่น ตัวอย่างเช่นคุณสามารถเพิ่มวิธีการstripPrefixIfPresent:เพื่อให้คุณสามารถพูดได้[@"foo/bar/baz" stripPrefixIfPresent:@"foo/"](สังเกตการใช้NSSringตัวอักษร@"")

  • การใช้การเรียกกลับเชิงวัตถุ
    ในภาษาที่พิมพ์แบบคงที่เช่น Java และ C ++ คุณจะต้องมีความยาวพอสมควรเพื่อให้ไลบรารี่เรียกสมาชิกของวัตถุที่ผู้ใช้ระบุ ใน Java การแก้ปัญหาคือคู่ของอินเตอร์เฟส / อะแด็ปเตอร์บวกคลาสที่ไม่ระบุชื่อใน C ++ การแก้ปัญหามักจะใช้เทมเพลตซึ่งหมายความว่ารหัสไลบรารีจะต้องเปิดเผยกับรหัสผู้ใช้ ใน Objective-C คุณเพียงแค่ส่งการอ้างอิงออบเจ็กต์พร้อมตัวเลือกสำหรับเมธอดไปยังไลบรารีและไลบรารีสามารถเรียกใช้การเรียกกลับได้โดยตรงและอย่างง่ายดาย


ฉันสามารถทำสิ่งแรกใน C ++ โดยการแคสติ้งเป็นโมฆะ * แต่นั่นคือการหลีกเลี่ยงระบบประเภทดังนั้นมันจึงไม่นับ ฉันสามารถทำสิ่งที่สองใน C # ด้วยวิธีการขยายได้อย่างสมบูรณ์ภายในระบบชนิด สำหรับครั้งที่สามฉันคิดว่า "ตัวเลือกสำหรับวิธีการ" อาจเป็นแลมบ์ดาดังนั้นภาษาที่พิมพ์แบบคงที่ใด ๆ กับแลมบ์ดาสามารถทำได้เช่นเดียวกันหากฉันเข้าใจอย่างถูกต้อง ฉันไม่คุ้นเคยกับ ObjC
user7610

1
@JiriDanek "ฉันสามารถทำสิ่งแรกใน C ++ ได้โดยการแคสติ้งให้เป็นโมฆะ *" ไม่ใช่รหัสที่อ่านอิลิเมนต์ไม่มีทางที่จะดึงประเภทที่แท้จริงออกมาได้เอง คุณต้องพิมพ์แท็ก นอกจากนี้ฉันไม่คิดว่าการพูดว่า "ฉันสามารถทำได้ใน <ภาษา>" เป็นวิธีที่เหมาะสม / มีประสิทธิผลในการดูสิ่งนี้เพราะคุณสามารถเลียนแบบพวกเขาได้ตลอดเวลา สิ่งสำคัญคือการได้รับความชัดเจนและความซับซ้อนของการนำไปใช้ นอกจากนี้คุณดูเหมือนจะคิดว่าถ้าภาษามีทั้งความสามารถคงที่และแบบไดนามิก (Java, C #) มันเป็นของตระกูลภาษา "คงที่" โดยเฉพาะ
coredump

1
@JiriDanek void*เพียงอย่างเดียวไม่ใช่การพิมพ์แบบไดนามิก แต่ขาดการพิมพ์ แต่ใช่ dynamic_cast ตารางเสมือนเป็นต้นทำให้ C ++ ไม่ได้พิมพ์แบบสแตติก มันแย่ใช่ไหม
coredump

1
มันแสดงให้เห็นว่ามีตัวเลือกในการล้มล้างระบบประเภทเมื่อมีความจำเป็นมีประโยชน์ มีช่องหนีเมื่อคุณต้องการ หรือใครบางคนคิดว่ามันมีประโยชน์ ไม่เช่นนั้นพวกเขาจะไม่ใส่มันเข้าไปในภาษา
user7610

2
@JiriDanek ฉันคิดว่าคุณน่ารักมากกับความคิดเห็นล่าสุดของคุณ ช่องหลบหนีเหล่านั้นมีประโยชน์อย่างยิ่งถ้าใช้ด้วยความระมัดระวัง อย่างไรก็ตามด้วยพลังอันยิ่งใหญ่มาพร้อมความรับผิดชอบที่ยอดเยี่ยมและผู้คนมากมายที่ใช้มันในทางที่ผิด ... ดังนั้นจึงรู้สึกดีขึ้นมากที่จะใช้ตัวชี้ไปยังคลาสพื้นฐานทั่วไปที่คลาสอื่นทั้งหมดมาจากคำจำกัดความ (ตามกรณี ทั้งใน Objective-C และ Java) และพึ่งพา RTTI เพื่อแยกเคสออกจากกันแทนที่จะใช้void*กับชนิดอ็อบเจ็กต์เฉพาะบางชนิด อดีตสร้างข้อผิดพลาดรันไทม์ถ้าคุณทำผิดพลาดผลในภายหลังในพฤติกรรมที่ไม่ได้กำหนด
cmaster
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.