โอ้มนุษย์ฉันตื่นเต้นที่จะพยายามตอบคำถามนี้ให้ดีที่สุดเท่าที่จะทำได้ ฉันหวังว่าฉันจะได้รับความคิดของฉันตามลำดับ
ดังที่ @Doval พูดถึงและผู้ถามชี้ให้เห็น (ถึงแม้จะหยาบคาย) คุณไม่มีระบบการพิมพ์จริงๆ คุณมีระบบการตรวจสอบแบบไดนามิกโดยใช้แท็กซึ่งโดยทั่วไปอ่อนแอกว่าและน่าสนใจน้อยกว่ามาก
คำถามที่ว่า "ระบบการพิมพ์แบบใด" นั้นเป็นปรัชญาค่อนข้างมากและเราสามารถเติมหนังสือที่มีมุมมองที่แตกต่างกันในเรื่องนี้ อย่างไรก็ตามเนื่องจากเป็นเว็บไซต์สำหรับโปรแกรมเมอร์ฉันจะพยายามทำให้คำตอบของฉันเป็นจริงที่สุดเท่าที่จะเป็นไปได้ (และจริงๆแล้วประเภทต่างๆนั้นเป็นประโยชน์อย่างมากในการเขียนโปรแกรม
ภาพรวม
เรามาเริ่มกันที่ที่นั่งของกางเกงเข้าใจว่าระบบแบบไหนดีก่อนที่จะดำดิ่งลงสู่ใต้กางเกงที่เป็นทางการ ระบบประเภทกำหนดโครงสร้างในโปรแกรมของเรา พวกเขาบอกเราว่าเราจะเสียบฟังก์ชั่นและการแสดงออกต่างๆเข้าด้วยกันได้อย่างไร หากไม่มีโครงสร้างโปรแกรมจะไม่สามารถป้องกันได้และซับซ้อนอย่างมากพร้อมที่จะก่อให้เกิดอันตรายในความผิดพลาดเพียงเล็กน้อยของโปรแกรมเมอร์
การเขียนโปรแกรมด้วยระบบพิมพ์ก็เหมือนกับการดูแลเอาใจใส่ในสภาพที่ไม่ดี - เบรกทำงานประตูปิดอย่างปลอดภัยเครื่องยนต์ถูกทาน้ำมัน ฯลฯ โปรแกรมการเขียนที่ไม่มีระบบพิมพ์ก็เหมือนกับการปั่นจักรยานโดยไม่มีหมวกและมีล้อทำ สปาเก็ตตี้ออกมา คุณไม่สามารถควบคุมได้อย่างแน่นอน
สมมติว่าเรามีภาษาที่มีการแสดงออกตามตัวอักษรnum[n]
และstr[s]
นั่นแทนตัวเลข n และสตริง s ตามลำดับและฟังก์ชันดั้งเดิมplus
และconcat
ด้วยความหมายที่ตั้งใจไว้ เห็นได้ชัดว่าคุณไม่ต้องการที่จะสามารถที่จะเขียนสิ่งที่ต้องการหรือplus "hello" "world"
concat 2 4
แต่เราจะป้องกันได้อย่างไร เบื้องต้นไม่มีวิธีแยกแยะตัวเลข 2 จาก "โลก" ของสตริงตามตัวอักษร สิ่งที่เราอยากจะพูดคือการแสดงออกเหล่านี้ควรใช้ในบริบทที่แตกต่างกัน พวกเขามีประเภทที่แตกต่างกัน
ภาษาและประเภท
ลองย้อนกลับไปหน่อย: ภาษาโปรแกรมคืออะไร? โดยทั่วไปเราสามารถแบ่งภาษาโปรแกรมออกเป็นสองเลเยอร์: ไวยากรณ์และซีแมนทิกส์ สิ่งเหล่านี้เรียกว่าสถิตยศาสตร์และพลศาสตร์ตามลำดับ ปรากฎว่าระบบประเภทที่จำเป็นในการเป็นสื่อกลางในการปฏิสัมพันธ์ระหว่างสองส่วนนี้
วากยสัมพันธ์
โปรแกรมเป็นต้นไม้ อย่าหลงกลตามบรรทัดของข้อความที่คุณเขียนบนคอมพิวเตอร์ นี่เป็นเพียงการนำเสนอของโปรแกรมที่มนุษย์อ่านได้ ตัวโปรแกรมเองเป็นนามธรรมต้นไม้ไวยากรณ์ ตัวอย่างเช่นใน C เราอาจเขียน:
int square(int x) {
return x * x;
}
นั่นคือไวยากรณ์ที่เป็นรูปธรรมสำหรับโปรแกรม (แฟรกเมนต์) ตัวแทนต้นไม้คือ:
function square
/ | \
int int x return
|
times
/ \
x x
ภาษาการเขียนโปรแกรมให้ไวยากรณ์การกำหนดต้นไม้ที่ถูกต้องของภาษานั้น (ทั้งไวยากรณ์คอนกรีตหรือนามธรรมอาจจะใช้) โดยปกติจะทำสิ่งนี้โดยใช้สัญลักษณ์ BNF ฉันจะสมมติว่าคุณทำสิ่งนี้กับภาษาที่คุณสร้างขึ้น
อรรถศาสตร์
ตกลงเรารู้ว่าโปรแกรมคืออะไร แต่มันเป็นเพียงโครงสร้างต้นไม้แบบคงที่ สมมุติว่าเราต้องการให้โปรแกรมของเราคำนวณบางสิ่งบางอย่าง เราต้องการความหมาย
ความหมายของภาษาโปรแกรมเป็นเขตการศึกษาที่หลากหลาย พูดกว้างมีสองวิธี: ความหมาย denotationalและความหมายในการดำเนินงาน ความหมายเชิง Denotational อธิบายโปรแกรมโดยการจับคู่มันเข้าไปในโครงสร้างทางคณิตศาสตร์พื้นฐานบางอย่าง (เช่นจำนวนธรรมชาติ, ฟังก์ชันต่อเนื่องเป็นต้น) ที่ให้ความหมายกับโปรแกรมของเรา ในทางตรงกันข้ามความหมายในการปฏิบัติงานจะกำหนดโปรแกรมโดยรายละเอียดวิธีการใช้งาน ในความคิดของฉันความหมายในการปฏิบัติงานนั้นง่ายต่อการเขียนโปรแกรม
ฉันจะไม่อธิบายวิธีการกำหนด semantics อย่างเป็นทางการ (รายละเอียดเกี่ยวข้องเล็กน้อย) แต่โดยพื้นฐานแล้วเราต้องการกฎดังนี้:
num[n]
เป็นค่า
str[s]
เป็นค่า
- ถ้า
num[n1]
และnum[n2]
ประเมินค่าเป็นจำนวนเต็มn_1$ and $n_2$, then
บวก (num [n1], NUM [n2]) `ประเมินเป็นจำนวนเต็ม $ n_1 + n_2 $
- ถ้า
str[s1]
และstr[s2]
ประเมินสตริง s1 และ s2 ให้concat(str[s1], str[s2])
ประเมินสตริง s1s2
ฯลฯ กฎในทางปฏิบัติมากขึ้นอย่างเป็นทางการ แต่คุณได้รับส่วนสำคัญ อย่างไรก็ตามเราพบปัญหาในไม่ช้า จะเกิดอะไรขึ้นเมื่อเราเขียนสิ่งต่อไปนี้:
concat(num[5], str[hello])
ฮึ่ม นี่เป็นปริศนา เรายังไม่ได้กำหนดกฎใด ๆ สำหรับวิธีการเชื่อมต่อตัวเลขกับสตริง เราสามารถพยายามสร้างกฎดังกล่าว แต่เรารู้โดยสัญชาตญาณว่าการดำเนินการนี้ไม่มีความหมาย เราไม่ต้องการให้โปรแกรมนี้ใช้งานได้ และดังนั้นเราจึงนำไปสู่การไม่ยอมแพ้ประเภท
ประเภท
โปรแกรมเป็นต้นไม้ตามที่กำหนดโดยไวยากรณ์ของภาษา โปรแกรมได้รับความหมายตามกฎการดำเนินการ แต่บางโปรแกรมไม่สามารถดำเนินการได้ ว่ามีบางโปรแกรมมีความหมาย โปรแกรมเหล่านี้ไม่ถูกต้อง ดังนั้นการพิมพ์ลักษณะของโปรแกรมที่มีความหมายในภาษา หากโปรแกรมถูกพิมพ์อย่างดีเราสามารถเรียกใช้งานได้
ลองยกตัวอย่างบางส่วน อีกครั้งเช่นเดียวกับกฎการประเมินผลฉันจะแสดงกฎการพิมพ์อย่างไม่เป็นทางการ แต่สามารถทำได้อย่างเข้มงวด นี่คือกฎบางอย่าง:
- โทเค็นของแบบฟอร์มมีประเภท
num[n]
nat
- โทเค็นของแบบฟอร์มมีประเภท
str[s]
str
- หากการแสดงออก
e1
มีพิมพ์nat
และการแสดงออกe2
มีประเภทnat
แล้วแสดงออกได้พิมพ์plus(e1, e2)
nat
- หากการแสดงออก
e1
มีพิมพ์str
และการแสดงออกe2
มีประเภทstr
แล้วแสดงออกได้พิมพ์concat(e1, e2)
str
ดังนั้นตามกฎเหล่านี้มีplus(num[5], num[2])
อยู่ในประเภทมีแต่เราไม่สามารถกำหนดประเภทให้nat
plus(num[5], str["hello"])
เราบอกว่าโปรแกรม (หรือนิพจน์) นั้นถูกพิมพ์อย่างดีถ้าเราสามารถกำหนดมันให้เป็นประเภทใดก็ได้และมันก็พิมพ์ผิดไป ระบบประเภทจะมีเสียงถ้าโปรแกรมที่พิมพ์ได้ดีทั้งหมดสามารถดำเนินการได้ Haskell เป็นเสียง C ไม่ได้
ข้อสรุป
มีมุมมองประเภทอื่น ๆ ประเภทในความหมายบางอย่างสอดคล้องกับตรรกะปรีชาและพวกเขายังสามารถดูเป็นวัตถุในทฤษฎีหมวดหมู่ การทำความเข้าใจการเชื่อมต่อเหล่านี้เป็นสิ่งที่น่าสนใจ แต่ก็ไม่จำเป็นถ้าเราเพียงต้องการเขียนหรือออกแบบภาษาโปรแกรม อย่างไรก็ตามความเข้าใจประเภทเป็นเครื่องมือในการควบคุมการก่อตัวของโปรแกรมมีความสำคัญต่อการออกแบบภาษาโปรแกรมและการพัฒนา ฉันมีรอยขีดข่วนเพียงพื้นผิวของประเภทที่สามารถแสดง ฉันหวังว่าคุณคิดว่าพวกเขามีค่าพอที่จะรวมเป็นภาษาของคุณ