การหาคำอธิบายที่ชัดเจนเกี่ยวกับความขัดแย้งที่เห็นได้ชัดเกี่ยวกับภาษาที่พิมพ์อย่างอ่อน


178

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

ตัวอย่างเช่นในบทความนี้ชื่อTyping: Strong vs. Weak, Static vs. Dynamicบอกว่า Python นั้นถูกพิมพ์อย่างมากเพราะคุณได้รับการยกเว้นถ้าคุณพยายามที่จะ:

หลาม

1 + "1"
Traceback (most recent call last):
File "", line 1, in ? 
TypeError: unsupported operand type(s) for +: 'int' and 'str'

อย่างไรก็ตามสิ่งดังกล่าวเป็นไปได้ใน Java และใน C # และเราไม่พิจารณาว่าพวกเขาพิมพ์เพียงเล็กน้อยเพื่อสิ่งนั้น

ชวา

  int a = 10;
  String b = "b";
  String result = a + b;
  System.out.println(result);

ค#

int a = 10;
string b = "b";
string c = a + b;
Console.WriteLine(c);

ในบทความอื่นที่ชื่อว่าWeakly Type Languagesผู้เขียนบอกว่า Perl พิมพ์อย่างอ่อนเพียงเพราะฉันสามารถเชื่อมสตริงกับจำนวนและ viceversa ได้โดยไม่ต้องมีการแปลงที่ชัดเจน

Perl

$a=10;
$b="a";
$c=$a.$b;
print $c; #10a

ตัวอย่างเดียวกันทำให้ Perl พิมพ์อย่างอ่อน แต่ไม่ใช่ Java และ C #?

Gee นี่คือความสับสน ป้อนคำอธิบายรูปภาพที่นี่

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

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

ฉันมีแนวโน้มที่จะเชื่อว่าฉันต้องผิดในการแทรกซึมครั้งนี้ฉันแค่ไม่รู้ว่าทำไมหรืออธิบายได้อย่างไร

ดังนั้นคำถามของฉันคือ:

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

8
ที่แข็งแกร่งเมื่อเทียบกับการพิมพ์ที่อ่อนแอเป็นข้อมูลเกี่ยวกับการแปลงชนิด (อะไรที่มันอาจจะเกี่ยวกับอะไร?) ถ้าคุณต้องการตัวอย่างของ "มาก" ภาษาอ่อนแอชมนี้: destroyallsoftware.com/talks/wat
Wilduck

2
@Wildduck ทุกภาษาให้การแปลงประเภท แต่ไม่ใช่ทุกประเภทที่จะถูกพิมพ์อย่างอ่อน ตัวอย่างของฉันที่แสดงด้านล่างแสดงให้เห็นว่าผู้เขียนโปรแกรมพิจารณาภาษาที่พิมพ์อย่างอ่อน ๆ บนพื้นฐานของตัวอย่างเดียวกันที่เป็นไปได้ในภาษาอื่น ๆ เช่นคำถามของฉันยังคงมีชัย อะไรคือความแตกต่าง?
Edwin Dalorzo

1
ฉันคิดว่าคำตอบสั้น ๆ คือ "Typedness" ไม่ใช่สถานะไบนารี Java และ C # นั้นพิมพ์ได้มากขึ้น แต่ไม่ใช่อย่างแน่นอน
Jodrell

3
ผมเชื่อว่านี่เป็นความเหมาะสมที่ดีกว่าสำหรับวิศวกรรมซอฟต์แวร์
zzzzBov

4
@Brendan สิ่งที่เกี่ยวกับการรวมทศนิยมและจำนวนเต็ม? จำนวนเต็มไม่ถูกรวมเข้ากับการลอยใน Python หรือไม่? คุณจะบอกว่าตอนนี้ว่างูใหญ่ไม่ได้พิมพ์อย่างแน่นอน?
Edwin Dalorzo

คำตอบ:


210

อัปเดต: คำถามนี้เป็นหัวข้อของบล็อกของฉันในวันที่ 15 ตุลาคม 2012ขอบคุณสำหรับคำถามที่ยอดเยี่ยม!


มันหมายความว่าอย่างไรสำหรับภาษาที่จะ "พิมพ์อย่างอ่อนแอ"?

มันหมายถึง "ภาษานี้ใช้ระบบประเภทที่ฉันพบว่าน่ารังเกียจ" ภาษา "ที่พิมพ์ได้ดี" ในทางตรงข้ามเป็นภาษาที่มีระบบการพิมพ์ที่ฉันพอใจ

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

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

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

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

อันไหนจริงเหรอ? มันเป็นไปไม่ได้ที่จะพูด; มันขึ้นอยู่กับมุมมองของผู้พูดและทัศนคติที่มีต่อภาษาต่างๆ


14
@edalorzo: มันขึ้นอยู่กับรสนิยมและความคิดเห็นส่วนตัวเกี่ยวกับ (1) แง่มุมของทฤษฎีประเภทใดที่เกี่ยวข้องและไม่เกี่ยวข้องและ (2) ต้องใช้ภาษาในการบังคับหรือสนับสนุนข้อ จำกัด ประเภท ดังที่ฉันได้ชี้ให้เห็นแล้วใคร ๆ ก็สามารถพูดได้ว่า C # นั้นพิมพ์ได้อย่างแรงเพราะมันช่วยให้และสนับสนุนการพิมพ์แบบคงที่และอย่างที่ใคร ๆ ก็บอกได้ว่ามันพิมพ์อย่างอ่อนแอเพราะมันทำให้เกิดความเสี่ยงต่อความปลอดภัย
Eric Lippert

4
@edalorzo: สำหรับการชุมนุมอีกครั้งมันเป็นเรื่องของความเห็น คอมไพเลอร์ภาษาแอสเซมบลีจะไม่อนุญาตให้คุณย้าย 64 บิตสองเท่าจากสแต็กไปยังการลงทะเบียน 32 บิต มันจะช่วยให้คุณย้ายตัวชี้ 32 บิตไปเป็น 64 บิตสองเท่าจากสแต็กไปยังการลงทะเบียน 32 บิต ในแง่ที่ว่าภาษาคือ "typesafe" - มันกำหนดข้อ จำกัด ในความถูกต้องตามกฎหมายของโปรแกรมขึ้นอยู่กับการจำแนกประเภทของข้อมูล ไม่ว่าจะเป็นข้อ จำกัด "แข็งแกร่ง" หรือ "อ่อนแอ" เป็นเรื่องของความเห็น แต่มันก็เป็นข้อ จำกัด อย่างชัดเจน
Eric Lippert

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

7
@edalorzo: ถูกต้อง ตัวอย่างเช่นแคลคูลัสแลมบ์ดาที่ไม่ได้พิมพ์นั้นจะพิมพ์ได้ไม่ดีเท่าที่คุณจะได้รับ ทุกฟังก์ชั่นเป็นฟังก์ชั่นจากฟังก์ชั่นไปยังฟังก์ชั่น; ข้อมูลใด ๆ ที่สามารถส่งผ่านไปยังฟังก์ชั่นใด ๆ โดยไม่มีข้อ จำกัด เพราะทุกอย่างเป็น "ประเภทเดียวกัน" ความถูกต้องของการแสดงออกในแคลคูลัสแลมบ์ดาที่ไม่ได้พิมพ์ขึ้นอยู่กับรูปแบบวากยสัมพันธ์ของมันไม่ใช่การวิเคราะห์เชิงความหมายที่จำแนกนิพจน์บางอย่างว่ามีบางประเภท
Eric Lippert

3
@ Mark ฉันจะให้ +1 เขาอีกครั้งเพื่อทำนายว่าทุกคนจะให้การตีความที่แตกต่างกันในเรื่องนี้ "การพิมพ์แบบอ่อน ๆ " นี้ดูเหมือนจะเป็น "แนวคิดเกี่ยวกับตำนาน" หรือ "ตำนานเมือง" ทุกคนได้เห็นแล้ว แต่ไม่มีใครสามารถพิสูจน์ได้ว่ามันมีอยู่ :-)
Edwin Dalorzo

64

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

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

$foo = "123" + "456";           # $foo = 579
$bar = substr($foo, 2, 1);      # $bar = 9
$bar .= " lives";               # $bar = "9 lives"
$foo -= $bar;                   # $foo = 579 - 9 = 570

แน่นอนตามที่คุณทราบอย่างถูกต้องทั้งหมดนี้สามารถเห็นได้ว่าเป็นเพียงการข่มขู่ แต่ประเด็นก็คือใน Perl ประเภทต่างๆจะถูกข่มขู่เสมอ ในความเป็นจริงมันค่อนข้างยากสำหรับผู้ใช้ที่จะบอกสิ่งที่ "ประเภท" ภายในของตัวแปรอาจเป็น: ที่บรรทัดที่ 2 ในตัวอย่างของฉันข้างต้นถามว่าค่าของ$barเป็นสตริง"9"หรือจำนวนที่9ไม่มีความหมายสวยมากเนื่องจากเป็น เท่าที่ Perl เป็นห่วงเหล่านั้นเป็นสิ่งเดียวกัน ที่จริงแล้วมันเป็นไปได้ที่สเกลาร์ Perl จะมีทั้งสตริงและค่าตัวเลขในเวลาเดียวกันภายในเช่นเดียวกับเคสของ$fooหลังบรรทัดที่ 2 ด้านบน

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

ตัวอย่างเช่น Perl มีและต้องการทั้งตัวดำเนินการบวกตัวเลข ( +) และตัวดำเนินการเรียงสตริง ( .): ตามที่คุณเห็นด้านบนการเพิ่มสตริง ( "1" + "2" == "3") หรือเพื่อเชื่อมหมายเลข ( 1 . 2 == 12) ในทำนองเดียวกันผู้ประกอบการเปรียบเทียบตัวเลข==, !=, <, >, <=, >=และ<=>เปรียบเทียบค่าตัวเลขของการขัดแย้งของพวกเขาในขณะที่ผู้ประกอบการเปรียบเทียบสตริงeq, ne, lt, gt, le, geและcmpเปรียบเทียบพวกเขา lexicographically เป็นสตริง ดังนั้น2 < 10แต่2 gt 10(แต่"02" lt 10ในขณะที่"02" == 2) (โปรดทราบว่าภาษาอื่น ๆเช่น JavaScript พยายามรองรับการพิมพ์ที่อ่อนแอเหมือนภาษา Perlยังทำผู้ประกอบการมากไป สิ่งนี้มักนำไปสู่ความอัปลักษณ์เช่นการสูญเสียการเชื่อมโยงสำหรับ+)

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

ทั้งหมดที่กล่าวว่าใครจะเถียงว่า Perl จะมีชนิดที่แข็งแกร่ง; พวกเขาไม่ใช่ประเภทที่คุณคาดหวัง โดยเฉพาะนอกเหนือจากประเภท "เซนต์คิตส์และเนวิส" ที่กล่าวถึงข้างต้น Perl ยังมีโครงสร้างที่มีสองประเภท: "อาร์เรย์" และ "แฮช" เหล่านี้จะมีมากแตกต่างจากสเกลาไปยังจุดที่ตัวแปร Perl มีแตกต่างกันsigilsบ่งบอกชนิดของพวกเขา ( $สำหรับสเกลา, @สำหรับอาร์เรย์%สำหรับแฮช) 1 มีมีกฎบังคับระหว่างประเภทเหล่านี้เพื่อให้คุณสามารถเขียนเช่น%foo = @barแต่ส่วนมากของพวกเขาจะค่อนข้างสูญเสีย: ยกตัวอย่างเช่น$foo = @barกำหนดความยาวของอาร์เรย์ @barไป$fooไม่ใช่เนื้อหา (นอกจากนี้ยังมีประเภทแปลก ๆ อีกสองสามอย่างเช่น typeglobs และ I / O handle ซึ่งคุณมักจะไม่ได้เห็น)

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


1จริงเครื่องหมายหมายถึงประเภทของค่าการเข้าถึงเพื่อให้เช่นเกลาแรกในอาร์เรย์จะเขียนแทน@foo $foo[0]ดูperlfaq4สำหรับรายละเอียดเพิ่มเติม

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


19

นอกจากสิ่งที่ Eric ได้กล่าวไว้ให้พิจารณารหัส C ต่อไปนี้:

void f(void* x);

f(42);
f("hello");

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

แต่ถึงกระนั้นรันไทม์จะยังคงตรวจสอบประเภท! หากการส่งไม่ถูกต้องระบบรันไทม์จะตรวจจับและโยนข้อยกเว้น

ด้วยการลบประเภทนี้จะไม่เกิดขึ้น - ข้อมูลประเภทจะถูกโยนทิ้งไป การส่งไปที่void*C ทำอย่างนั้นทั้งหมด ในเรื่องนี้ที่กล่าวมาเป็นพื้นฐานแตกต่างจาก C # void f(Object x)ประกาศวิธีเช่น

(ในทางเทคนิค C # ยังอนุญาตให้ลบประเภทผ่านรหัสที่ไม่ปลอดภัยหรือการจัดระเบียบ)

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


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

1
ตกลงมีชั้นไปที่หัวหอมหรือนรก สิ่งเหล่านี้ดูเหมือนจะเป็นคำจำกัดความที่สำคัญของความอ่อนแอประเภท
Jodrell

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

1
@edalorzo คุณสามารถหลีกเลี่ยงคอมไพเลอร์ด้วยวิธีนี้เท่านั้นไม่ใช่ระบบรันไทม์ สิ่งสำคัญคือต้องตระหนักว่าภาษาเช่น Java และ C # (และในระดับหนึ่งด้วย C ++) มีระบบประเภทที่มั่นใจได้สองครั้ง: ครั้งเดียวในเวลารวบรวมและอีกครั้งที่รันไทม์ void*เจาะผ่านการตรวจสอบทั้งสองประเภท การลบประเภททั่วไปไม่เพียง แต่หลีกเลี่ยงการตรวจสอบเวลาคอมไพล์เท่านั้น มันเหมือนกับการปลดเปลื้องที่ชัดเจน (เอ่ยโดย Eric) ในเรื่องนี้
Konrad Rudolph

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

14

ตัวอย่างที่สมบูรณ์แบบมาจากบทความวิกิพีเดียของการพิมพ์ที่แข็งแกร่ง :

โดยทั่วไปการพิมพ์ที่แข็งแกร่งหมายถึงว่าภาษาการเขียนโปรแกรมวางข้อ จำกัด ที่รุนแรงเกี่ยวกับ intermixing ที่ได้รับอนุญาตให้เกิดขึ้น

การพิมพ์ที่อ่อนแอ

a = 2
b = "2"

concatenate(a, b) # returns "22"
add(a, b) # returns 4

พิมพ์ดีดที่แข็งแกร่ง

a = 2
b = "2"

concatenate(a, b) # Type Error
add(a, b) # Type Error
concatenate(str(a), b) #Returns "22"
add(a, int(b)) # Returns 4

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

ทั้งหมดนี้ขึ้นอยู่กับการตีความการพิมพ์


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

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

4

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

จากมุมมองทางทฤษฎีฉันคิดว่าบทความโดย Luca Cardelli และ Peter Wegner ชื่อOn Understanding Types, Data Abstraction และ Polymorphismเป็นหนึ่งในข้อโต้แย้งที่ดีที่สุดที่ฉันได้อ่าน

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

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

บทความต่อไป ...

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

ดังนั้นการพิมพ์ที่รัดกุมหมายถึงไม่มีข้อผิดพลาดประเภทฉันสามารถสันนิษฐานได้ว่าการพิมพ์ที่อ่อนหมายถึงสิ่งที่ตรงกันข้าม: โอกาสที่จะเกิดข้อผิดพลาดประเภท ณ รันไทม์หรือเวลารวบรวม? ดูเหมือนว่าไม่เกี่ยวข้องที่นี่

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

บนมืออื่น ๆ ที่ผมสามารถพูดได้มากกว่าค่าเผื่อของClassCastExceptionและArrayStoreException(ชวา) และInvalidCastException, ArrayTypeMismatchException(ใน C #) จะบ่งบอกถึงระดับของการพิมพ์อย่างอ่อนอย่างน้อยที่รวบรวมเวลา? คำตอบของ Eric ดูเหมือนจะเห็นด้วยกับสิ่งนี้

ในบทความที่สองชื่อTypeful Programming ที่มีให้ในการอ้างอิงหนึ่งในคำตอบของคำถามนี้ Luca Cardelli นำเสนอแนวคิดของการละเมิดประเภท:

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

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

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

จากนี้ไม่ได้พิมพ์ Python, Perl, Java หรือ C # อย่างอ่อน

Cardelli กล่าวถึงความชั่วร้ายสองประเภทที่ฉันพิจารณากรณีการพิมพ์ที่อ่อนแออย่างแท้จริง:

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

สิ่งต่าง ๆ ที่เป็นไปได้ในภาษาเช่น C (กล่าวถึงโดย Konrad) หรือผ่านรหัสที่ไม่ปลอดภัยใน. Net (กล่าวถึงโดย Eric) จะเป็นการพิมพ์ที่อ่อนแออย่างแท้จริง

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


4

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

การพิมพ์ที่รัดกุมหมายความว่าประเภทจะไม่ถูกข่มขู่หรือถูกบังคับน้อยกว่า

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

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

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

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

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


+1 เพราะฉันชอบคำประกาศเกียรติคุณ "พิมพ์ชัดแจ้ง" ซึ่งจัดหมวดหมู่ภาษาเช่น Java และ C # ที่คุณต้องประกาศประเภทอย่างชัดเจนและแยกแยะความแตกต่างจากภาษาประเภทคงที่อื่น ๆ เช่น Haskell และ Scala ที่การอนุมานประเภทมีบทบาทสำคัญและโดยทั่วไป สร้างความสับสนให้ผู้คนอย่างที่คุณพูดและทำให้พวกเขาเชื่อว่าภาษาเหล่านี้เป็นแบบไดนามิก
Edwin Dalorzo

3

แข็งแรง <=> พิมพ์อ่อนแอไม่เพียงเกี่ยวกับความต่อเนื่องเกี่ยวกับวิธีการมากหรือน้อยของค่าที่มีการข่มขู่โดยอัตโนมัติจากภาษาหนึ่งไปยังอีกประเภทข้อมูล แต่วิธีการขอหรืออ่อนที่เกิดขึ้นจริงค่าพิมพ์ ใน Python และ Java และส่วนใหญ่เป็น C # ค่าจะมีประเภทเป็นหิน ใน Perl ไม่มาก - มีเพียงไม่กี่แบบในการจัดเก็บในตัวแปร

เรามาเปิดเคสกันทีละตัว


หลาม

ในหลามตัวอย่าง1 + "1", +ผู้ประกอบการเรียก__add__สำหรับประเภทintให้มันสตริง"1"เป็นอาร์กิวเมนต์ - แต่ผลใน NotImplemented:

>>> (1).__add__('1')
NotImplemented

ถัดไปล่ามจะพยายาม__radd__ของ str:

>>> '1'.__radd__(1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'str' object has no attribute '__radd__'

ในขณะที่มันล้มเหลวของผู้ประกอบการล้มเหลวกับผลที่ได้+ TypeError: unsupported operand type(s) for +: 'int' and 'str'ดังนั้นข้อยกเว้นไม่ได้พูดอะไรมากเกี่ยวกับการพิมพ์ที่แข็งแกร่ง แต่ความจริงที่ว่าผู้ดำเนินการ+ ไม่ได้บังคับให้ข้อโต้แย้งของมันเป็นประเภทเดียวกันโดยอัตโนมัติเป็นตัวชี้ถึงความจริงที่ว่า Python ไม่ใช่ภาษาที่พิมพ์อย่างอ่อนแอที่สุดในทวีป

ในทางตรงกันข้ามใน Python 'a' * 5 ถูกนำไปใช้งาน:

>>> 'a' * 5
'aaaaa'

นั่นคือ,

>>> 'a'.__mul__(5)
'aaaaa'

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


ชวา

ตัวอย่าง Java ใช้String result = "1" + 1;งานได้เนื่องจากความสะดวกเท่านั้นตัวดำเนินการ+ถูกโหลดมากเกินไปสำหรับสตริง +ผู้ประกอบการJava แทนที่ลำดับด้วยการสร้างStringBuilder(ดูนี้ ):

String result = a + b;
// becomes something like
String result = new StringBuilder().append(a).append(b).toString()

นี่เป็นตัวอย่างของการพิมพ์แบบสแตติกมากโดยไม่มีการข่มขู่จริง - StringBuilderมีวิธีการappend(Object)ที่ใช้เฉพาะที่นี่ เอกสารอธิบายดังต่อไปนี้:

ผนวกการแทนค่าสตริงของObjectอาร์กิวเมนต์

ผลกระทบโดยรวมจะเหมือนกับว่าอาร์กิวเมนต์ถูกแปลงเป็นสตริงโดยวิธีการString.valueOf(Object)และอักขระของสตริงนั้นจะถูกผนวกเข้ากับลำดับอักขระนี้

อยู่ที่ไหนString.valueOfแล้ว

ส่งคืนการแทนค่าสตริงของอาร์กิวเมนต์ Object [Returns] ถ้าอาร์กิวเมนต์เป็นnullแล้วสตริงเท่ากับ"null"; มิฉะนั้นobj.toString()จะส่งคืนค่าของ

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


ค#

ตามคำตอบของJon Skeet ที่นี่ผู้ประกอบการ+ไม่ได้รับภาระมากเกินไปสำหรับstringคลาส - ซึ่งคล้ายกับ Java นี่เป็นเพียงความสะดวกสบายที่สร้างโดยคอมไพเลอร์ด้วยการพิมพ์ทั้งแบบคงที่และแรง


Perl

ตามที่perldataอธิบาย

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

Perl แต่ไม่มีประเภทข้อมูลแยกต่างหากสำหรับตัวเลข, booleans, สตริง, nulls, undefineds, การอ้างอิงไปยังวัตถุอื่น ๆ ฯลฯ - มันมีเพียงประเภทเดียวสำหรับสิ่งเหล่านี้ทั้งหมด, ประเภทสเกลาร์; 0 คือค่าสเกลาร์มากเท่ากับ "0" ตัวแปรสเกลาร์ที่ถูกตั้งค่าเป็นสตริงสามารถเปลี่ยนเป็นตัวเลขได้จริงและจากที่นั่นจะทำงานแตกต่างจาก "เพียงแค่สตริง" หากมีการเข้าถึงในบริบทตัวเลข. เซนต์คิตส์และเนวิสสามารถเก็บอะไรก็ได้ใน Perl มันเป็นวัตถุที่มีอยู่ในระบบ ในขณะที่ใน Python ชื่อเพียงหมายถึงวัตถุใน Perl ค่าสเกลาร์ในชื่อเป็นวัตถุที่เปลี่ยนแปลงได้ นอกจากนี้ระบบ Object Oriented Type ยังติดอยู่ด้านบนของสิ่งนี้: มีเพียง 3 ประเภทข้อมูลใน perl - scalars, รายการและแฮช วัตถุที่ผู้ใช้กำหนดใน Perl เป็นการอ้างอิง (นั่นคือตัวชี้ไปยัง 3 จากก่อนหน้านี้) blessไปยังแพ็คเกจ - คุณสามารถนำค่าดังกล่าวมาใช้และอวยพรให้กับคลาสใด ๆ ในทันทีที่คุณต้องการ

Perl ยังช่วยให้คุณสามารถเปลี่ยนคลาสของค่าที่ราชประสงค์ - เป็นไปไม่ได้ใน Python ที่จะสร้างค่าของบางคลาสคุณต้องสร้างค่าที่เป็นของคลาสนั้นด้วยobject.__new__หรือคล้ายกันอย่างชัดเจน ใน Python คุณไม่สามารถเปลี่ยนแก่นแท้ของวัตถุหลังจากการสร้างใน Perl คุณสามารถทำอะไรได้มาก:

package Foo;
package Bar;

my $val = 42;
# $val is now a scalar value set from double
bless \$val, Foo;
# all references to $val now belong to class Foo
my $obj = \$val;
# now $obj refers to the SV stored in $val
# thus this prints: Foo=SCALAR(0x1c7d8c8)
print \$val, "\n"; 
# all references to $val now belong to class Bar
bless \$val, Bar;
# thus this prints Bar=SCALAR(0x1c7d8c8)
print \$val, "\n";
# we change the value stored in $val from number to a string
$val = 'abc';
# yet still the SV is blessed: Bar=SCALAR(0x1c7d8c8)
print \$val, "\n";
# and on the course, the $obj now refers to a "Bar" even though
# at the time of copying it did refer to a "Foo".
print $obj, "\n";

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

my $another = $val;

\$anotherไม่มีตัวตนของชั้นแม้ว่า\$valจะยังคงให้การอ้างอิงที่มีความสุข


TL; DR

มีมากขึ้นเกี่ยวกับการพิมพ์ที่อ่อนแอไปยัง Perl มากกว่าการข่มขู่โดยอัตโนมัติและมันเป็นมากกว่าเกี่ยวกับประเภทของค่าที่ตัวเองไม่ได้กำหนดไว้ในหินซึ่งแตกต่างจาก Python ซึ่งเป็นภาษาแบบไดนามิกยังพิมพ์อย่างรุนแรงมาก งูหลามที่ให้TypeErrorใน1 + "1"ข้อบ่งชี้ว่าภาษาที่มีการพิมพ์ขอแม้ว่าหนึ่งในทางตรงกันข้ามในการทำสิ่งที่มีประโยชน์เช่นเดียวกับใน Java หรือ C # ไม่ได้ดักคอพวกเขาเป็นภาษาพิมพ์มั่น


นี่คือความสับสนโดยสิ้นเชิง ตัวแปร Perl 5 นั้นไม่มีประเภทไม่มีการแบกค่าใด ๆซึ่งมีประเภทอยู่เสมอ
Jim Balter

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

ค่าไม่เปลี่ยนแปลงประเภท - ที่ไม่ต่อเนื่องกัน ค่าเป็นประเภทเสมอ ค่าที่ตัวแปรมีสามารถเปลี่ยนแปลงได้ การเปลี่ยนจาก 1 เป็น "1" เป็นเพียงการเปลี่ยนแปลงมูลค่ามากเช่นเดียวกับการเปลี่ยนจาก 1 เป็น 2
Jim Balter

ภาษาที่พิมพ์อย่างอ่อนเช่น Perl ช่วยให้การเปลี่ยนแปลงค่าชนิดก่อนหน้าเกิดขึ้นโดยปริยายขึ้นอยู่กับบริบท แต่แม้แต่ C ++ ก็อนุญาตให้มีการแปลงโดยนัยดังกล่าวผ่านข้อกำหนดของโอเปอเรเตอร์ การพิมพ์ที่อ่อนแอนั้นเป็นคุณสมบัติที่ไม่เป็นทางการและจริงๆแล้วมันไม่ใช่วิธีที่มีประโยชน์ในการอธิบายภาษาตามที่ Eric Lippert กล่าว
Jim Balter

ป.ล. สามารถแสดงให้เห็นว่าแม้ใน Perl <digits> และ "<digits>" จะมีค่าต่างกันไม่ใช่แค่ประเภทที่แตกต่างกัน Perl ทำให้ <digits> และ "<digits>" ดูเหมือนจะมีค่าเท่ากันในกรณีส่วนใหญ่ผ่านการแปลงโดยนัยแต่ภาพลวงตาไม่สมบูรณ์ เช่น "12" | "34" คือ 36 โดย 12 | 34 คือ 46 อีกตัวอย่างหนึ่งคือ "00" มีค่าเป็นตัวเลขเท่ากับ 00 ในบริบทส่วนใหญ่ แต่ไม่ใช่ในบริบทบูลีนโดยที่ "00" เป็นจริง แต่ 00 เป็นเท็จ
Jim Balter

1

ดังที่คนอื่น ๆ ได้แสดงออกความคิดทั้งหมดของการพิมพ์ "แข็งแรง" และ "อ่อนแอ" เป็นปัญหา

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

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

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

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

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


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

0

ฉันชอบคำตอบของ @Eric Lippertแต่เพื่อตอบคำถาม - โดยทั่วไปแล้วภาษาที่พิมพ์ออกมาจะมีความรู้ที่ชัดเจนเกี่ยวกับประเภทของตัวแปรในแต่ละจุดของโปรแกรม ภาษาที่พิมพ์ออกมาอ่อนแอไม่ได้ดังนั้นพวกเขาจึงสามารถพยายามดำเนินการที่อาจไม่สามารถใช้ได้กับบางประเภท มันคิดว่าวิธีที่ง่ายที่สุดที่จะเห็นสิ่งนี้คือในฟังก์ชั่น C ++:

void func(string a) {...}

ตัวแปรaนี้รู้จักกันว่าเป็นสตริงประเภทและการทำงานที่เข้ากันไม่ได้ใด ๆ จะถูกตรวจจับในเวลารวบรวม

งูหลาม:

def func(a)
  ...

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


12
ฉันคิดว่าคุณอาจสับสนกับการพิมพ์แบบไดนามิกกับการพิมพ์แบบคงที่ด้วยการพิมพ์ที่แรงและการพิมพ์ที่อ่อนแอ ในโค้ดทั้งสองเวอร์ชันระบบประเภทรันไทม์รู้ดีว่า a คือสตริง ในกรณีแรกคอมไพเลอร์สามารถบอกคุณได้ว่าในกรณีที่สองไม่สามารถทำได้ แต่นี่ไม่ได้ทำให้ภาษาใด ๆ เหล่านี้พิมพ์อ่อนแอ
Edwin Dalorzo
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.