การพิมพ์แบบไดนามิกใช้ฟังก์ชันอะไรได้บ้าง? [ปิด]


91

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

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


5
ในทางทฤษฎีมีอะไรที่คุณไม่สามารถทำทั้งตราบใดที่ภาษาจะเป็นทัวริงสมบูรณ์ คำถามที่น่าสนใจสำหรับฉันก็คือสิ่งที่ง่ายหรือเป็นธรรมชาติในหนึ่งกับคำถามอื่น ๆ มีสิ่งที่ฉันทำเป็นประจำใน Python ที่ฉันจะไม่พิจารณาด้วยใน C ++ ถึงแม้ว่าฉันรู้ว่ามันมีความสามารถ
Mark Ransom

28
อย่างที่ Chris Smith เขียนไว้ในบทความที่ดีเยี่ยมสิ่งที่ควรรู้ก่อนการโต้วาทีระบบพิมพ์ : "ปัญหาในกรณีนี้คือโปรแกรมเมอร์ส่วนใหญ่มีประสบการณ์ จำกัด และยังไม่ได้ลองภาษามากมายสำหรับบริบทที่นี่หกหรือเจ็ด ไม่นับว่าเป็น "มาก" สิ่งที่น่าสนใจสองอย่างคือ: (1) โปรแกรมเมอร์จำนวนมากใช้ภาษาที่มีการพิมพ์ไม่คงที่ (2) โปรแกรมเมอร์จำนวนมากใช้ภาษาที่พิมพ์แบบไดนามิกต่ำมาก "
Daniel Pryden

3
@suslik: หากภาษาดั้งเดิมมีประเภทไร้สาระแน่นอนว่าคุณสามารถทำสิ่งที่ไร้สาระกับประเภท สิ่งนี้ไม่มีส่วนเกี่ยวข้องกับการพิมพ์แบบคงที่และแบบไดนามิก
Jon Purdy

10
@CzarekTomczak: นั่นเป็นคุณสมบัติของภาษาที่พิมพ์แบบไดนามิกบางใช่ แต่เป็นไปได้ที่ภาษาที่พิมพ์แบบคงที่จะสามารถแก้ไขได้ในขณะใช้งานจริง ตัวอย่างเช่น Visual Studio ช่วยให้คุณสามารถเขียนรหัส C # ในขณะที่คุณอยู่ที่จุดพักในดีบักเกอร์และแม้แต่ย้อนตัวชี้คำสั่งเพื่อเรียกใช้รหัสของคุณอีกครั้งด้วยการเปลี่ยนแปลงใหม่ ในขณะที่ฉันอ้างถึง Chris Smith ในความคิดเห็นอื่นของฉัน: "โปรแกรมเมอร์หลายคนใช้ภาษาที่พิมพ์แบบสแตติกไม่ดีมาก" - อย่าตัดสินภาษาที่พิมพ์แบบคงที่ทั้งหมดโดยคนที่คุณรู้จัก
Daniel Pryden

11
@WarrenP: คุณยืนยันว่า "ระบบประเภทไดนามิกลดจำนวนของ cruft พิเศษที่ฉันต้องพิมพ์" - แต่คุณเปรียบเทียบ Python กับ C ++ นั่นไม่ใช่การเปรียบเทียบที่เป็นธรรม: แน่นอน C ++ เป็น verbose มากกว่า Python แต่นั่นไม่ใช่เพราะความแตกต่างของระบบการพิมพ์ของพวกเขา แต่เป็นเพราะความแตกต่างในไวยากรณ์ของพวกเขา หากคุณเพียงต้องการลดจำนวนตัวอักษรในแหล่งโปรแกรมของคุณเรียนรู้ J หรือ APL: ฉันรับประกันว่าพวกเขาจะสั้นลง การเปรียบเทียบที่ยุติธรรมมากขึ้นคือการเปรียบเทียบ Python กับ Haskell (สำหรับเร็กคอร์ด: ฉันชอบ Python และชอบมากกว่า C ++ แต่ฉันชอบ Haskell มากยิ่งขึ้น)
Daniel Pryden

คำตอบ:


50

เมื่อคุณขอตัวอย่างที่เฉพาะเจาะจงฉันจะให้คุณ

Massive ORM ของ Rob Conery คือรหัส 400 บรรทัด มีขนาดเล็กเนื่องจาก Rob สามารถแมปตาราง SQL และให้ผลลัพธ์วัตถุโดยไม่ต้องใช้สแตติกจำนวนมากเพื่อทำมิเรอร์ตาราง SQL สามารถทำได้โดยใช้dynamicชนิดข้อมูลใน C # หน้าเว็บของ Rob อธิบายรายละเอียดของกระบวนการนี้ แต่เห็นได้ชัดว่าในกรณีการใช้งานเฉพาะนี้การพิมพ์แบบไดนามิกนั้นส่วนใหญ่มีส่วนรับผิดชอบต่อความกะทัดรัดของรหัส

เปรียบเทียบกับDapperของ Sam Saffron ซึ่งใช้ประเภทคงที่ SQLMapperระดับเพียงอย่างเดียวคือ 3000 สายรหัส

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


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

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

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

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


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


22
หากฉันเพิ่มสตริงตัวเลขสองตัวเข้าด้วยกันฉันก็ยังคงไม่คาดหวังผลลัพธ์ที่เป็นตัวเลข
pdr

22
@ Robert ฉันเห็นด้วยกับคำตอบของคุณส่วนใหญ่ อย่างไรก็ตามโปรดทราบว่ามีภาษาที่พิมพ์แบบคงที่ด้วยลูป read-eval-print แบบโต้ตอบเช่น Scala และ Haskell อาจเป็นได้ว่า C # ไม่ใช่ภาษาเชิงโต้ตอบโดยเฉพาะ
Andres F.

14
ต้องเรียนรู้ฉัน Haskell
Robert Harvey

7
@RobertHarvey: คุณอาจประหลาดใจ / ประทับใจกับ F # ถ้าคุณยังไม่ได้ลอง คุณได้รับความปลอดภัยทุกประเภท (การคอมไพล์เวลา) ที่ปกติคุณได้รับในภาษา. NET ยกเว้นว่าคุณแทบจะไม่ต้องประกาศชนิดใด ๆ เลย การอนุมานประเภทใน F # เป็นมากกว่าสิ่งที่มีอยู่ / ทำงานใน C # นอกจากนี้: คล้ายกับสิ่งที่แอนเดรสและแดเนียลจะชี้ออก F # โต้ตอบเป็นส่วนหนึ่งของ Visual Studio ...
สตีเว่น Evers

8
"คุณจะไม่เพิ่มสองสายเข้าด้วยกันและคาดว่าจะมีคำตอบที่เป็นตัวเลขเว้นแต่สตริงประกอบด้วยข้อมูลที่เป็นตัวเลขและถ้าพวกเขาทำไม่คุณกำลังจะได้รับผลที่ไม่คาดคิด"ขอโทษนี้มีอะไรจะทำอย่างไรกับแบบไดนามิก VS คงพิมพ์ นี่คือการพิมพ์ที่แข็งแกร่งและอ่อนแอ
vartec

26

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

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

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

คอมไพเลอร์อาจปฏิเสธโปรแกรมที่มีข้อผิดพลาดประเภทคงที่

นี่เป็นข้อ จำกัด เนื่องจากโปรแกรมที่ปลอดภัยหลายประเภทจำเป็นต้องมีข้อผิดพลาดประเภทคงที่

ตัวอย่างเช่นฉันมีสคริปต์ Python ที่ต้องการเรียกใช้เป็นทั้ง Python 2 และ Python 3 บางฟังก์ชันเปลี่ยนประเภทพารามิเตอร์ระหว่าง Python 2 และ 3 ดังนั้นฉันจึงมีรหัสดังนี้:

if sys.version_info[0] == 2:
    wfile.write(txt)
else:
    wfile.write(bytes(txt, 'utf-8'))

ตัวตรวจสอบชนิดคงที่ Python 2 จะปฏิเสธรหัส Python 3 (และในทางกลับกัน) แม้ว่ามันจะไม่ถูกดำเนินการ โปรแกรมประเภทปลอดภัยของฉันมีข้อผิดพลาดประเภทคงที่

อีกตัวอย่างพิจารณาโปรแกรม Mac ที่ต้องการทำงานบน OS X 10.6 แต่ใช้ประโยชน์จากฟีเจอร์ใหม่ใน 10.7 วิธี 10.7 นั้นอาจจะมีหรือไม่มีอยู่ตอนรันไทม์และสำหรับฉันมันเป็นโปรแกรมเมอร์ที่จะตรวจจับพวกมัน ตัวตรวจสอบชนิดแบบคงที่ถูกบังคับให้ปฏิเสธโปรแกรมของฉันเพื่อรับรองความปลอดภัยประเภทหรือยอมรับโปรแกรมพร้อมกับความเป็นไปได้ในการสร้างข้อผิดพลาดประเภท (ฟังก์ชันขาดหายไป) ที่รันไทม์

การตรวจสอบชนิดสแตติกสันนิษฐานว่าสภาพแวดล้อมรันไทม์มีการอธิบายอย่างเพียงพอโดยข้อมูลเวลารวบรวม แต่การทำนายอนาคตนั้นเต็มไปด้วยอันตราย!

นี่คือข้อ จำกัด อีกหนึ่งข้อ:

คอมไพเลอร์อาจสร้างรหัสที่ถือว่าเป็นประเภทรันไทม์เป็นประเภทคงที่

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

ตัวอย่างของการใช้งานในระยะไกลเช่นNSXPCConnectionหรือObjic TransparentProxyของ ObjC (ซึ่งการติดตั้งจำเป็นต้องมีการเปลี่ยนแปลงเล็กน้อยในรันไทม์ - ดูที่นี่สำหรับการอภิปราย)

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

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


2
"ตัวตรวจสอบชนิดคงที่ Python 2 จะปฏิเสธรหัส Python 3 (และในทางกลับกัน) แม้ว่ามันจะไม่ถูกเรียกใช้งานโปรแกรมประเภทปลอดภัยของฉันมีข้อผิดพลาดประเภทคงที่" ฟังดูเหมือนสิ่งที่คุณต้องการจริงๆมี "static if" บางชนิดที่คอมไพเลอร์ / ล่ามไม่เห็นแม้แต่รหัสถ้าเงื่อนไขเป็นเท็จ
David Stone

@davidstone มีอยู่ใน c ++
Milind R

A Python 2 static type checker would reject the Python 3 code (and vice versa), even though it would never be executed. My type safe program contains a static type error. ในภาษาแบบคงที่ใด ๆ ที่เหมาะสมคุณสามารถทำได้ด้วยIFDEFคำสั่ง preprocessor ประเภทในขณะที่รักษาความปลอดภัยประเภทในทั้งสองกรณี
Mason Wheeler

1
@MasonWheeler, davidstone No, เทคนิค preprocessor และ static_if นั้นทั้งสองคงที่เกินไป ในตัวอย่างของฉันฉันใช้ Python2 และ Python3 แต่มันอาจเป็นได้อย่างง่ายดายเหมือน AmazingModule2.0 และ AmazingModule3.0 ซึ่งอินเตอร์เฟสบางส่วนเปลี่ยนไประหว่างเวอร์ชัน สิ่งแรกสุดที่คุณสามารถรู้ได้คืออินเทอร์เฟซคือเวลาการนำเข้าโมดูลซึ่งจำเป็นในการรันไทม์ (อย่างน้อยถ้าคุณต้องการสนับสนุนการเชื่อมโยงแบบไดนามิก)
ridiculous_fish

18

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

แต่การพิมพ์เป็ดในคอลเล็กชันที่สร้างขึ้นแบบไดนามิกนั้นทำได้ยากในทางอื่น:

>>> d = JSON.parse(foo)
>>> d['bar'][3]
12
>>> d['baz']['qux']
'quux'

แล้วJSON.parseผลตอบแทนประเภทใด? พจนานุกรมของอาร์เรย์ของจำนวนเต็มหรือพจนานุกรมของสตริง? ไม่แม้ว่ามันจะไม่กว้างพอ

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

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

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


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

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

4
@MattFenwick ประเภทข้อมูลพีชคณิตค่อนข้าง จำกัด เฉพาะภาษาที่ใช้งานได้จริง (ในทางปฏิบัติ) แล้วภาษาอย่าง Java และ c # ล่ะ
spirc

4
ADT มีอยู่ใน C / C ++ เป็นสหภาพที่ติดแท็ก นี่ไม่ใช่ภาษาที่ใช้งานได้จริง
Clark Gaebel

2
@spirc คุณสามารถเลียนแบบ ADT ในภาษา OO แบบคลาสสิกโดยใช้คลาสหลายคลาสที่ได้รับมาจากส่วนต่อประสานทั่วไปการเรียกใช้เวลาทำงานไปยัง getClass () หรือ GetType () และการตรวจสอบความเท่าเทียมกัน หรือคุณสามารถใช้การส่งแบบคู่ แต่ฉันคิดว่ามันจ่ายมากกว่าใน C ++ ดังนั้นคุณอาจมีอินเตอร์เฟส JSObject และ JSString, JSNumber, JSHash และ JSArray จากนั้นคุณจะต้องใช้รหัสเพื่อเปลี่ยนโครงสร้างข้อมูล "untyped" นี้ให้เป็นโครงสร้างข้อมูล "application typed" แต่คุณอาจต้องการทำเช่นนี้ในภาษาที่พิมพ์แบบไดนามิกเช่นกัน
Daniel Yankowsky

12

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

ฉันจะทำตัวอย่าง สมมติว่าคุณใช้โมเดลข้อมูลอย่างง่ายเพื่ออธิบายวัตถุข้อมูลคอลเลกชันของพวกเขา ฯลฯ ซึ่งถูกพิมพ์แบบคงที่ในแง่ที่ว่าถ้าแบบจำลองบอกคุณลักษณะxของวัตถุประเภท Foo เก็บจำนวนเต็มมันจะต้องถือจำนวนเต็มเสมอ เนื่องจากนี่เป็นโครงสร้างแบบรันไทม์คุณจึงไม่สามารถพิมพ์แบบคงที่ได้ สมมติว่าคุณเก็บข้อมูลที่อธิบายไว้ในไฟล์ YAML คุณสร้างแฮชแมป (จะถูกส่งไปยังไลบรารี YAML ในภายหลัง) รับแอxททริบิวเก็บไว้ในแผนที่รับแอททริบิวอื่น ๆ ที่เกิดขึ้นเป็นสตริง ... ค้างไว้สักวินาที? the_map[some_key]ตอนนี้เป็นประเภทอะไร ทีนี้เรารู้ว่านั่นsome_keyคือ'x'และผลลัพธ์จะต้องเป็นจำนวนเต็ม แต่ระบบพิมพ์ก็ไม่สามารถให้เหตุผลเกี่ยวกับเรื่องนี้ได้

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

แน่นอนว่าทางออกของทุกวันนี้คือการชกมวยทุกอย่างแล้วทำการคัดเลือก (หรือมีวิธีการ overriden ซึ่งส่วนใหญ่จะยกระดับข้อยกเว้น แต่นี่ไม่ใช่การพิมพ์แบบสแตติกมันเป็นแฮ็กรอบระบบพิมพ์เพื่อทำการตรวจสอบชนิดที่รันไทม์


ประเภททั่วไปไม่ได้มีความต้องการมวย
Robert Harvey

@RobertHarvey ใช่ ผมไม่ได้พูดคุยกับมวยใน Java C # ผมได้พูดคุยเกี่ยวกับ "ห่อมันขึ้นมาในระดับห่อหุ้มบางส่วนที่มีวัตถุประสงค์เป็นตัวแทนค่าของ T ในชนิดย่อยของ U เป็น" ตัวแปรหลากหลาย (สิ่งที่คุณเรียกว่าการพิมพ์ทั่วไป) แต่ไม่ได้ใช้กับตัวอย่างของฉัน มันเป็นนามธรรมเวลารวบรวมมากกว่าประเภทที่เป็นรูปธรรม แต่เราต้องการกลไกการพิมพ์รันไทม์

มันอาจจะคุ้มค่าที่จะชี้ให้เห็นว่าระบบประเภทของ Scala นั้นกำลังทำการทัวริงสมบูรณ์ ดังนั้นระบบพิมพ์อาจมีความสำคัญน้อยกว่ารูปภาพ
Andrea

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

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

7

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

ตัวอย่างสั้น ๆ ใน Haskell:

data Data = DString String | DInt Int | DDouble Double

-- defining a '+' operator here, with explicit promotion behavior
DString a + DString b = DString (a ++ b)
DString a + DInt b = DString (a ++ show b)
DString a + DDouble b = DString (a ++ show b)
DInt a + DString b = DString (show a ++ b)
DInt a + DInt b = DInt (a + b)
DInt a + DDouble b = DDouble (fromIntegral a + b)
DDouble a + DString b = DString (show a ++ b)
DDouble a + DInt b = DDouble (a + fromIntegral b)
DDouble a + DDouble b = DDouble (a + b)

มีกรณีเพียงพอคุณสามารถใช้ระบบชนิดไดนามิกที่กำหนด

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

แก้ไข: ฉันต้องการทำให้ง่ายนี้ แต่นี่คือรายละเอียดเพิ่มเติมเกี่ยวกับแบบจำลองวัตถุ

ฟังก์ชั่นใช้รายการของข้อมูลเป็นอาร์กิวเมนต์และทำการคำนวณด้วยผลข้างเคียงใน ImplMonad และส่งคืนข้อมูล

type Function = [Data] -> ImplMonad Data

DMember เป็นค่าสมาชิกหรือฟังก์ชัน

data DMember = DMemValue Data | DMemFunction Function

ขยายDataเพื่อรวมวัตถุและฟังก์ชั่น วัตถุคือรายการของสมาชิกที่มีชื่อ

data Data = .... | DObject [(String, DMember)] | DFunction Function

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


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

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

2
@Jed เมื่อคุณใช้โมเดลวัตถุชนิดพื้นฐานและการดำเนินการดั้งเดิมไม่จำเป็นต้องมีพื้นฐานอื่นใด คุณสามารถแปลโปรแกรมโดยอัตโนมัติในภาษาไดนามิกดั้งเดิมเป็นภาษาถิ่นนี้
NovaDenizen

2
@ hcalves เนื่องจากคุณอ้างถึงการใช้งานมากเกินไปในรหัส Haskell ของฉันฉันสงสัยว่าคุณไม่มีความคิดที่ถูกต้องเกี่ยวกับความหมายของมัน มีฉันได้กำหนดใหม่+ผู้ประกอบการซึ่งรวมสองDataค่าลงอีกDataค่า Dataแทนค่ามาตรฐานในระบบชนิดไดนามิก
NovaDenizen

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

3

เมมเบรน :

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

ป้อนคำอธิบายรูปภาพที่นี่

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


4
ใช่ Haskell สามารถใช้สิ่งนี้ได้จริง หากคุณมีคลาสประเภท Foo คุณสามารถสร้าง wrapper รอบ ๆ อินสแตนซ์ของอินเทอร์เฟซนั้น class Foo a where ... data Wrapper = forall a. Foo a => Wrapper a
Jake McArthur

@ JakeMcArthur ขอบคุณสำหรับการอธิบาย นั่นเป็นอีกเหตุผลที่ทำให้ฉันนั่งลงและเรียนรู้ Haskell
Mike Samuel

2
เมมเบรนของคุณคือ 'อินเทอร์เฟซ' และประเภทของวัตถุนั้น "พิมพ์แบบมีอยู่จริง" นั่นคือเรารู้ว่ามันมีอยู่ภายใต้อินเตอร์เฟส แต่นั่นคือทั้งหมดที่เรารู้ ประเภทที่มีอยู่สำหรับข้อมูลนามธรรมได้รับการรู้จักมาตั้งแต่ยุค 80 การอ้างอิงที่ดีคือcs.cmu.edu/~rwh/plbook/book.pdf ตอนที่ 21.1
Don Stewart

@DonStewart คลาสพร็อกซี Java เป็นกลไกชนิดที่มีอยู่จริงหรือไม่? สถานที่แห่งหนึ่งที่เมมเบรนกลายเป็นเรื่องยากคือในภาษาที่มีระบบประเภทเล็กน้อยที่มีชื่อสำหรับประเภทคอนกรีตที่มองเห็นได้นอกความหมายของประเภทนั้น ตัวอย่างเช่นสิ่งหนึ่งไม่สามารถห่อหุ้มได้Stringเนื่องจากเป็นชนิดที่เป็นรูปธรรมใน Java #doesNotUnderstandสมอลล์ทอล์คไม่ได้มีปัญหานี้เพราะมันไม่ได้พยายามที่จะพิมพ์
Mike Samuel

1

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

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

ก่อนอื่นให้คำจำกัดความ

เอนทิตี - ฉันต้องการความคิดทั่วไปของเอนทิตีบางอย่างในรหัส สามารถเป็นอะไรก็ได้ตั้งแต่หมายเลขดั้งเดิมไปจนถึงข้อมูลที่ซับซ้อน

พฤติกรรม - สมมติว่าเอนทิตีของเรามีสถานะบางอย่างและชุดของวิธีการที่อนุญาตให้โลกภายนอกสั่งให้เอนทิตี้ของปฏิกิริยาบางอย่าง ให้เรียกสถานะ + อินเตอร์เฟสของเอนทิตีนี้พฤติกรรมของมัน เอนทิตีหนึ่งสามารถมีพฤติกรรมมากกว่าหนึ่งอย่างรวมกันในวิธีที่แน่นอนโดยภาษาของเครื่องมือ

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

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

พิมพ์แบบคงที่ - พฤติกรรมของเอนทิตีทั้งหมดในโปรแกรมของคุณจะถูกตรวจสอบในเวลารวบรวมก่อนที่รหัสจะเริ่มทำงาน ซึ่งหมายความว่าหากคุณต้องการตัวอย่างเอนทิตีของประเภทบุคคลที่มีพฤติกรรม (ทำตัวเหมือน) นักมายากลคุณจะต้องกำหนดเอนทิตี MagicianPerson และให้พฤติกรรมของนักมายากลเช่น ThrowMagic () หากคุณอยู่ในรหัสของคุณผิดพลาดบอกกับสามัญ Person.throwMagic () คอมไพเลอร์จะบอกคุณ"Error >>> hell, this Person has no this behavior, dunno throwing magics, no run!".

การพิมพ์แบบไดนามิก - ในสภาพแวดล้อมการพิมพ์แบบไดนามิกพฤติกรรมของเอนทิตีที่ใช้ได้จะไม่ถูกตรวจสอบจนกว่าคุณจะลองทำบางสิ่งกับเอนทิตีบางอย่าง การรันโค้ด Ruby ที่ถาม Person.throwMagic () จะไม่ถูกตรวจจับจนกว่าโค้ดของคุณจะมาถึงจริงๆ ฟังดูน่าผิดหวังใช่มั้ย แต่ฟังดูเป็นการเปิดเผยเช่นกัน จากคุณสมบัตินี้คุณสามารถทำสิ่งที่น่าสนใจ เช่นสมมติว่าคุณออกแบบเกมที่ทุกสิ่งสามารถเปลี่ยนเป็นนักมายากลและคุณไม่รู้ว่าใครจะเป็นใครจนกว่าคุณจะมาถึงจุดที่แน่นอนในรหัส จากนั้นกบก็มาแล้วคุณก็บอกว่าHeyYouConcreteInstanceOfFrog.include Magicและหลังจากนั้นกบตัวนี้กลายเป็นกบตัวหนึ่งที่มีพลังเวทย์มนตร์ กบอื่น ๆ ยังไม่ คุณจะเห็นว่าในภาษาการพิมพ์แบบคงที่คุณจะต้องกำหนดความสัมพันธ์นี้ด้วยค่าเฉลี่ยมาตรฐานของการรวมพฤติกรรม (เช่นการใช้อินเตอร์เฟส) ในภาษาการพิมพ์แบบไดนามิกคุณสามารถทำได้ในขณะใช้งานจริงและไม่มีใครสนใจ

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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.