ภาษาที่พิมพ์แบบไดนามิกนั้นเป็นแบบ uni-typed
เมื่อเปรียบเทียบกับระบบประเภทแล้วไม่มีข้อได้เปรียบในการพิมพ์แบบไดนามิก การพิมพ์แบบไดนามิกเป็นกรณีพิเศษของการพิมพ์แบบคงที่ - เป็นภาษาที่พิมพ์แบบคงที่ซึ่งทุกตัวแปรมีชนิดเดียวกัน คุณสามารถบรรลุสิ่งเดียวกันใน Java (ลบความกระชับ) โดยทำให้ทุกตัวแปรเป็นประเภทObject
และมีค่า "วัตถุ" เป็นประเภทMap<String, Object>
:
void makeItBark(Object dog) {
Map<String, Object> dogMap = (Map<String, Object>) dog;
Runnable bark = (Runnable) dogMap.get("bark");
bark.run();
}
ดังนั้นแม้จะไม่มีการสะท้อนก็ตามคุณสามารถได้รับผลเช่นเดียวกันในภาษาที่พิมพ์แบบคงที่ใดก็ได้โดยไม่ต้องคำนึงถึงความสะดวกสบายทางไวยากรณ์ คุณไม่ได้รับพลังเพิ่มเติมใด ๆ ในทางตรงกันข้ามคุณมีพลังแสดงออกน้อยกว่าเพราะในภาษาที่พิมพ์แบบไดนามิกคุณจะปฏิเสธความสามารถในการ จำกัด ตัวแปรเฉพาะบางประเภท
ทำเปลือกเป็ดในภาษาที่พิมพ์แบบคงที่
ยิ่งไปกว่านั้นภาษาที่พิมพ์แบบสแตติกที่ดีจะช่วยให้คุณสามารถเขียนโค้ดที่ทำงานกับประเภทใดก็ได้ที่มีการbark
ดำเนินการ ใน Haskell นี่คือคลาสประเภท:
class Barkable a where
bark :: a -> unit
สิ่งนี้เป็นการแสดงออกถึงข้อ จำกัด ที่บางประเภทa
จะพิจารณาว่าเป็น Barkable จะต้องมีbark
ฟังก์ชันที่รับค่าของประเภทนั้นและไม่ส่งคืนอะไรเลย
จากนั้นคุณสามารถเขียนฟังก์ชันทั่วไปในแง่ของBarkable
ข้อ จำกัด :
makeItBark :: Barkable a => a -> unit
makeItBark barker = bark (barker)
นี้กล่าวว่าmakeItBark
จะทำงานสำหรับประเภทใดที่น่าพอใจBarkable
's ความต้องการ สิ่งนี้อาจดูเหมือนคล้ายกับinterface
ใน Java หรือ C # แต่มีข้อได้เปรียบใหญ่อย่างหนึ่ง - ประเภทไม่จำเป็นต้องระบุล่วงหน้าว่าคลาสชนิดใดที่พวกเขาพึงพอใจ ฉันสามารถพูดประเภทDuck
นั้นBarkable
ได้ตลอดเวลาแม้ว่าDuck
จะเป็นประเภทบุคคลที่สามที่ฉันไม่ได้เขียน ในความเป็นจริงมันไม่สำคัญว่าผู้เขียนDuck
ไม่ได้เขียนbark
ฟังก์ชั่น - ฉันสามารถให้มันตามความเป็นจริงเมื่อฉันบอกภาษาที่Duck
ตรงกับBarkable
:
instance Barkable Duck where
bark d = quack (punch (d))
makeItBark (aDuck)
สิ่งนี้บอกว่าDuck
s สามารถเห่าและฟังก์ชั่นเปลือกของพวกเขาจะดำเนินการโดยการไล่เป็ดก่อนที่จะต้มตุ๋น เมื่อพ้นทางเราสามารถเรียกmakeItBark
เป็ดได้
Standard ML
และOCaml
มีความยืดหยุ่นมากขึ้นในการที่คุณสามารถตอบสนองประเภทคลาสเดียวกันได้มากกว่าหนึ่งวิธี ในภาษาเหล่านี้ฉันสามารถพูดได้ว่าจำนวนเต็มสามารถสั่งซื้อโดยใช้การสั่งซื้อแบบเดิมแล้วหมุนไปรอบ ๆ และบอกว่าพวกเขายังสามารถเรียงลำดับได้ด้วยการหาร (เช่น10 > 5
เพราะ 10 หารด้วย 5) ใน Haskell คุณสามารถสร้างคลาสประเภทได้ครั้งเดียวเท่านั้น (นี้จะช่วยให้ Haskell จะรู้โดยอัตโนมัติว่ามันโอเคที่จะเรียกbark
ในเป็ด; ใน SML หรือ OCaml คุณจะต้องมีความชัดเจนเกี่ยวกับที่ bark
ทำงานคุณต้องการเพราะอาจจะมีมากกว่าหนึ่ง.)
ความรัดกุม
แน่นอนว่ามันมีความแตกต่างทางไวยากรณ์ รหัส Python ที่คุณนำเสนอมีความกระชับมากกว่า Java ที่ฉันเขียน ในทางปฏิบัติความรัดกุมนั้นเป็นส่วนสำคัญของเสน่ห์ของภาษาที่พิมพ์แบบไดนามิก แต่การอนุมานประเภทช่วยให้คุณสามารถเขียนโค้ดที่มีความกระชับในภาษาที่พิมพ์แบบคงที่โดยช่วยให้คุณไม่ต้องเขียนชนิดของตัวแปรทุกตัวอย่างชัดเจน ภาษาที่พิมพ์แบบสแตติกยังสามารถให้การสนับสนุนแบบดั้งเดิมสำหรับการพิมพ์แบบไดนามิกการลบความฟุ่มเฟื่อยของการชี้ขาดและการปรับแต่งแผนที่ทั้งหมด (เช่น C # dynamic
)
โปรแกรมที่ถูกต้อง แต่พิมพ์ผิด
เพื่อความเป็นธรรมการพิมพ์สแตติกจำเป็นต้องออกกฎบางโปรแกรมที่ถูกต้องทางเทคนิคแม้ว่าตัวตรวจสอบประเภทจะไม่สามารถตรวจสอบได้ ตัวอย่างเช่น:
if this_variable_is_always_true:
return "some string"
else:
return 6
ภาษาที่พิมพ์แบบสแตติกส่วนใหญ่จะปฏิเสธif
ข้อความนี้แม้ว่าสาขาอื่นจะไม่เกิดขึ้น ในทางปฏิบัติดูเหมือนว่าไม่มีใครใช้รหัสประเภทนี้ - สิ่งใดที่ฉลาดเกินกว่าสำหรับตัวตรวจสอบชนิดอาจทำให้ผู้ดูแลรหัสของคุณในอนาคตสาปแช่งคุณและญาติคนต่อไปของคุณ ในกรณีนี้บางคนประสบความสำเร็จในการแปลโครงการโอเพนซอร์ซ 4 โครงการเป็น Haskellซึ่งหมายความว่าพวกเขาไม่ได้ทำสิ่งใด ๆ ที่ภาษาที่พิมพ์แบบคงที่ที่ดีไม่สามารถรวบรวมได้ ยิ่งไปกว่านั้นคอมไพเลอร์ยังพบข้อบกพร่องเกี่ยวกับประเภทที่หน่วยทดสอบไม่ได้จับ
อาร์กิวเมนต์ที่แข็งแกร่งที่สุดที่ฉันเคยเห็นสำหรับการพิมพ์แบบไดนามิกคือมาโครของ LISP เนื่องจากพวกเขาอนุญาตให้คุณขยายไวยากรณ์ของภาษาโดยพลการ อย่างไรก็ตามTyped Racketเป็นภาษาที่พิมพ์แบบสแตติกของ Lisp ที่มีมาโครดังนั้นจึงดูเหมือนว่าการพิมพ์แบบสแตติกและมาโครไม่ได้มีความสัมพันธ์เฉพาะกัน
แอปเปิ้ลและส้ม
สุดท้ายอย่าลืมว่าภาษามีความแตกต่างมากกว่าระบบประเภทของพวกเขา ก่อนที่จะมี Java 8 ทำใด ๆชนิดของโปรแกรมการทำงานในชวาเป็นไปไม่ได้ในทางปฏิบัติ; แลมบ์ดาง่าย ๆ จะต้องใช้รหัสคลาส 4 บรรทัดที่ไม่ระบุชื่อสำเร็จรูป Java ยังไม่สนับสนุนตัวอักษรสำหรับการรวบรวม (เช่น[1, 2, 3]
) นอกจากนี้ยังอาจมีความแตกต่างในด้านคุณภาพและความพร้อมของเครื่องมือ (IDE, debuggers), ไลบรารีและการสนับสนุนชุมชน เมื่อมีคนอ้างว่ามีประสิทธิผลมากขึ้นใน Python หรือ Ruby มากกว่า Java ต้องคำนึงถึงความแตกต่างของคุณลักษณะนั้น มีความแตกต่างระหว่างการเปรียบเทียบเป็นภาษาที่มีแบตเตอรี่ทั้งหมดรวม , แกนภาษาและระบบการพิมพ์
makeItBark(collections.namedtuple("Dog", "bark")(lambda x: "woof woof"))
. อาร์กิวเมนต์นั้นไม่ได้เป็นคลาสแต่เป็น tuple ที่ไม่ระบุชื่อ การพิมพ์เป็ด ("ถ้ามันเหมือน quacks ... ") ให้คุณทำส่วนต่อประสานเฉพาะกิจโดยไม่มีข้อ จำกัด เป็นศูนย์และไม่มีค่าใช้จ่ายเกี่ยวกับวากยสัมพันธ์ คุณสามารถทำสิ่งนี้ในภาษาเช่น Java แต่คุณก็มีภาพสะท้อนที่ยุ่งเหยิงมากมาย ถ้าฟังก์ชั่นใน Java ต้องการ ArrayList และคุณต้องการให้คอลเลกชันประเภทอื่นแสดงว่าคุณเป็น SOL ในงูหลามที่ไม่สามารถเกิดขึ้นได้