การพิมพ์แบบค่อยเป็นค่อยไป:“ เกือบทุกภาษาที่มีระบบพิมพ์แบบสแตติกยังมีระบบพิมพ์แบบไดนามิก”


20

การอ้างสิทธิ์โดยAleks Bromfieldระบุว่า:

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

นี่คือการอ้างสิทธิ์ที่ถูกต้องหรือไม่ ฉันเข้าใจว่าด้วยคลาส Reflection หรือ Loading ที่รันไทม์ Java จะมีลักษณะเช่นนี้ - แต่สามารถขยายความคิดของ 'การพิมพ์ทีละส่วน' เป็นภาษาจำนวนมากได้หรือไม่?


ฉันไม่ใช่ผู้เชี่ยวชาญด้านภาษา แต่มีบางคนที่นึกขึ้นมาได้ทันทีว่าฉันไม่เชื่อว่ามีประเภทแบบไดนามิก (โดยพื้นฐานแล้วไม่ได้บอกว่าคุณไม่สามารถปูอะไรบางอย่างร่วมกันได้) - Fortran, Ada, Pascal, Cobol
mattnz

4
ฉันเป็นผู้เชี่ยวชาญด้านภาษาและฉันไม่แน่ใจว่าสิ่งที่ผู้ชายคนนี้อ้างสิทธิ์ "static" และ "dynamic" เป็น misnomers และมักจะระบุเวลาของการตรวจสอบ / วิเคราะห์ประเภท (ไม่ว่าในเวลารวบรวมหรือที่รันไทม์) บางภาษาประนีประนอมทั้งสองอย่าง (เช่นโดยการไม่อนุญาตให้ดำเนินการกับประเภทที่ไม่ได้รับการสนับสนุนในระหว่างการรวบรวมและยกระดับข้อยกเว้นเช่น ClassCastException ระหว่างรันไทม์) และนั่นอาจเป็นสิ่งที่เขาหมายถึง ถ้าเป็นเช่นนั้นมันเป็นความจริงที่ C มักจะไม่ทำการตรวจสอบชนิดระหว่างรันไทม์ การพูดภาษาหรั่ง ไม่มี "ระบบประเภทไดนามิก" ไม่สมเหตุสมผล
Thiago Silva

คำตอบ:


37

ทวีตเตอร์ดั้งเดิมที่นี่ :)

ก่อนอื่นฉันค่อนข้างขบขัน / ตกใจที่ทวีตของฉันจริงจังมาก! ถ้าฉันรู้ว่ามันกำลังจะถูกเผยแพร่อย่างกว้างขวางฉันจะใช้เวลามากกว่า 30 วินาทีในการเขียนมัน!

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

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

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

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

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


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

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

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

ทุกภาษามีระบบพิมพ์แบบไดนามิกซึ่งเป็นชุดของกฎที่ห้ามการดำเนินการบางอย่าง
Elazar

7

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

นอกจากนี้คุณสามารถทำเช่นนั้นได้อย่างง่ายดายใน C เช่นกัน

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

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

ตัวอย่างเช่นดูที่Cythonคอมไพเลอร์ Python-to-C มันสร้างรหัส C แบบคงที่ แต่ระบบประเภทยังคงรักษาธรรมชาติของมันแบบไดนามิก C เป็นภาษาที่พิมพ์แบบคงที่ แต่ก็สามารถรองรับการพิมพ์แบบไดนามิกจาก Python


ในทางเทคนิคคุณสามารถทำได้ใน C # ตั้งแต่เวอร์ชัน 4 ด้วยExpandoObjectแม้ว่าจะเป็นกระบวนการที่เลือกใช้อย่างมากซึ่งแตกต่างจาก JavaScript หรือ Ruby ถึงกระนั้นคุณได้สร้างจุดสำคัญมากซึ่งก็คือการพิมพ์เป็ด (99% ของนักพัฒนาจริงหมายถึงเมื่อพวกเขาพูดว่า "พิมพ์แบบไดนามิก") และการสะท้อนกลับไม่ใช่สิ่งเดียวกันเลย
Aaronaught

ฉันอาจเพิ่มว่า Python ไม่มีการพิมพ์เป็ดจริง มันมีตะขอบางอย่างสำหรับคุณที่จะ "ใช้ของคุณเอง" (มีวิธีเวทมนต์ที่สามารถกลับมาTrueเพื่อพูดว่า "วัตถุที่บ้านี้เป็นตัวอย่างของคลาสที่ฉันกำหนด") OCaml มีคุณสมบัตินี้เท่าที่ฉันเข้าใจ แต่ฉันไม่รู้จริงๆ
vlad-ardelean

6

ภาษาแบบไดนามิกที่มีภาษาแบบคงที่ สิ่งที่เรียกกันโดยทั่วไปว่า "การพิมพ์แบบไดนามิก" เป็นกรณีพิเศษของการพิมพ์แบบคงที่ - กรณีที่คุณ จำกัด ตัวเองให้มีเพียงหนึ่งประเภทเท่านั้น เป็นการทดลองทางความคิดลองนึกภาพการเขียนโปรแกรมใน Java หรือ C # โดยใช้เฉพาะObjectตัวแปร / ฟิลด์ / พารามิเตอร์และการส่งสัญญาณทันทีก่อนเรียกวิธีการใด ๆ การเรียกภาษาอย่างแม่นยำมากขึ้นเช่น Python หรือ Javascript "unityped" (การอ้างสิทธิ์นี้มีแนวโน้มที่จะสร้างความสับสนให้กับคนจำนวนมากเนื่องจากโปรแกรม Java หรือ C # จะใช้ประเภทย่อยจำนวนมาก แต่นั่นเป็นเพราะภาษา OOP โดยเฉลี่ยทำให้เกิดประเภทและชั้นเรียนที่ซับซ้อนอ่านโพสต์บล็อกสำหรับรายละเอียดเพิ่มเติม)

โปรดทราบว่าแม้ C มีการพิมพ์ "แบบไดนามิก" - คุณสามารถส่งตัวชี้ไปยังvoid(และหากหน่วยความจำทำหน้าที่ฉันchar) ตัวชี้และกลับมาอีกครั้ง และโปรดทราบว่าไม่มีการตรวจสอบรันไทม์ที่นั่น ถ้าคุณเข้าใจผิดสนุกกับพฤติกรรมที่ไม่ได้กำหนด!


แต่นักแสดงจะทำแบบคงที่ ฉันไม่เห็นว่านี่เป็นตัวอย่างของการพิมพ์แบบไดนามิก
osa

@osa เพียงเพราะรหัสบอกว่าString foo = (String) barมันไม่ได้หมายความว่าในความเป็นจริงbar Stringคุณสามารถรู้ได้อย่างแน่นอนในเวลาทำงานดังนั้นฉันจึงไม่เห็นวิธีการโยน "แบบคงที่"
Doval

ฉันไม่เห็นด้วยกับสิ่งนี้ อย่างน้อยใน Javascript คุณสามารถเพิ่มวิธีการและคุณสมบัติใหม่ให้กับวัตถุในรันไทม์ ใน C # คุณต้องใช้dynamicวัตถุเพื่อทำสิ่งนี้อย่างชัดเจน หากคุณพยายามที่จะเพิ่มคุณสมบัติในobject... ดีคุณไม่สามารถ
Arturo Torres Sánchez

3

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

void horribleJava(List untypedList) {
  for (Object o : untypedList)
    ((SpecificType) o).frobnicate();
}

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

void goodJava(List<SpecificType> typedList) {
  for (SpecificType o : typedList) {
    o.forbnicate();
  }
}

ใน C ค่า (และโดยเฉพาะคำแนะนำ) จะไม่เก็บประเภทของพวกเขาในช่วง Runtime - void *ทุกตัวชี้เทียบเท่ากับ แต่ตัวแปรและนิพจน์มีชนิดแทน เพื่อให้บรรลุการพิมพ์แบบไดนามิกคุณต้องดำเนินการกับข้อมูลประเภทของตัวเอง (เช่นเขตข้อมูลใน struct)


1
ฉันไม่คิดว่านักแสดงจะนับเป็นการพิมพ์แบบไดนามิกในแง่ที่เป็นประโยชน์จริงๆ มันยังคงต้องการความรู้เกี่ยวกับชนิดของเวลาคอมไพล์มันเพียง defers การตรวจสอบที่แท้จริงจนกว่ารันไทม์ คุณไม่สามารถเรียกใช้frobnicateวิธีการที่นี่โดยไม่ทราบSpecificTypeก่อน
Aaronaught

2
@Aaraught แต่นี่เป็นการพิมพ์แบบไดนามิกเมื่อเรารอการตรวจสอบประเภทเวลาทำงาน โปรดทราบว่าทั้งสองตัวอย่างของฉันใช้การพิมพ์แบบระบุชื่อซึ่งต้องใช้ชื่อประเภทเฉพาะ คุณกำลังคิดถึงโครงสร้างการพิมพ์เช่นการพิมพ์เป็ดซึ่งต้องใช้วิธีการบางอย่าง แกนที่มีโครงสร้างเทียบกับแบบระบุชื่อและแบบคงที่เทียบกับการพิมพ์แบบไดนามิกเป็นแบบตั้งฉากกัน (Java เป็นแบบเสนอชื่อและส่วนใหญ่เป็นแบบคงที่; ML และไปเป็นโครงสร้างและแบบคงที่; Perl, Python และทับทิมเป็นโครงสร้างและไดนามิก)
amon

เช่นเดียวกับที่ฉันพูดในความคิดเห็นล่าสุดของฉันมันเป็นความแตกต่างทางทฤษฎีที่ไม่มีโปรแกรมเมอร์คนไหนที่ฉันเคยใส่ใจ คุณสามารถโต้แย้งอย่างถ่องแท้ว่าผู้คนกำลังใช้คำศัพท์ที่ผิด (และในความเป็นจริงแล้วปรากฏว่าทวีตเตอร์ดั้งเดิมกำลังดำเนินการอย่างบ้าคลั่งอย่างนั้น) แต่สำหรับคนที่อยู่ในสนามเพลาะการพิมพ์แบบไดนามิก = การพิมพ์เป็ด ในความเป็นจริงนั้นคำจำกัดความเป็นเรื่องธรรมดามากที่ langues เช่น C # ได้ประมวลผลจริงแล้ว (เช่นด้วยdynamicคำหลัก) การเปรียบเทียบเวลาแบบคงที่กับการรวบรวมและแบบไดนามิกไปยังรันไทม์ส่วนใหญ่เพียงแค่โคลนน้ำ
Aaronaught

@Aaraught: หากมีใครโยนไปยังส่วนต่อประสานและถ้าชั้นเรียนที่ใช้วิธีการที่มีความหมายที่ตั้งใจจะใช้ส่วนต่อประสานที่เหมือนกันในการพูดเช่นนั้นก็จะเป็นหนึ่งในนั้น ในขณะที่บางคนอาจโต้แย้งว่า "การพิมพ์เป็ด" ควรใช้ชื่อเมธอดฉันคิดว่ามันจะมีประโยชน์มากขึ้นในการพิจารณาชื่อเมธอด "ของจริง" ว่าเป็นชื่ออินเตอร์เฟสพร้อมกับชื่อเมธอด มิฉะนั้นควร[1,2].Add([3,4])ผลผลิต[1,2,3,4], [1,2,[3,4]]หรือ[4,6]?
supercat

@supercat: ไม่มีข้อใดข้อหนึ่งข้างต้นก็ควรจะล้มเหลวเพราะไม่มีAddวิธีใดในอาเรย์ที่ยอมรับอาเรย์เพราะเมธอดดังกล่าวจะคลุมเครือ การพิมพ์เป็ดไม่ได้ยกโทษให้คุณจากการเขียนประเภทและฟังก์ชั่นที่เข้าใจได้
Aaronaught

2

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

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


จากนั้นมันไม่ถูกต้อง ใน C ++, Pascal, Fortran --- ในภาษาใด ๆ ที่คอมไพล์ --- ชนิดจะถูกตรวจสอบแบบสแตติกเท่านั้นและไม่ใช่แบบไดนามิก (ยกเว้นว่าคุณกำลังใช้ dynamic_cast) คุณสามารถใช้พอยน์เตอร์เพื่อเขียนข้อมูลลงในหน่วยความจำและไม่มีใครรู้ประเภท ดังนั้นการพิมพ์แบบสแตติกหมายถึงการตรวจสอบอย่างเดียวเท่านั้นในขณะที่การพิมพ์แบบไดนามิกหมายถึงการตรวจสอบเพียงครั้งเดียวในทุกเวลา
osa

-1

การคัดออกผิด: C ยังมีระบบประเภทไดนามิกที่สำคัญ มันไม่ได้ตรวจสอบ ("พิมพ์ C อย่างยิ่งตรวจสอบอย่างอ่อนแอ") ตัวอย่างเช่นการรักษาโครงสร้างเป็นdouble( reinternpret_cast- สไตล์) ให้ผลการทำงานที่ไม่ได้กำหนด - ข้อผิดพลาดประเภทแบบไดนามิก


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