เพราะเหตุใดประเภทจึงตามหลังชื่อตัวแปรในภาษาการเขียนโปรแกรมสมัยใหม่


57

ทำไมในภาษาการเขียนโปรแกรมสมัยใหม่เกือบทุกภาษา (Go, Rust, Kotlin, Swift, Scala, Nim หรือ Python เวอร์ชันล่าสุด) มักเกิดขึ้นหลังชื่อตัวแปรในการประกาศตัวแปรและไม่ใช่มาก่อน?

ทำไมx: int = 42ไม่int x = 42?
สิ่งหลังไม่สามารถอ่านได้มากกว่าในอดีตหรือไม่
มันเป็นเพียงเทรนด์หรือมีเหตุผลที่มีความหมายจริงๆที่อยู่เบื้องหลังโซลูชันนี้หรือไม่?



3
ใช่นั่นเป็นล่อคู่คิด แม้ว่ามันจะหมายถึงภาษา Kotlin โดยเฉพาะคำถาม (และคำตอบ) จะใช้กับทุกภาษาที่มีการฝึกฝนนี้
Robert Harvey


2
ขออภัยที่ฉันเห็นคำถามเกี่ยวกับ Kotlin เมื่อ googling แต่ฉันถามอีกครั้งว่าจะไม่ได้รับคำตอบเช่น "เพราะทีม Kotlin (Go, Rust, ... ) dev ตัดสินใจแบบนี้" ฉันสนใจคำตอบทั่วไป: ทำไมมันถึงเป็นโรคระบาด?
Andre Polykanine

2
Hihi ดังนั้น Delphi ซึ่งเป็น (Object) Pascal และมีประเภทหลังจากชื่อตัวแปรนับตั้งแต่เริ่มก่อตั้งเมื่อย้อนกลับไปในยุคมืดของศตวรรษที่แล้วก็มีความทันสมัยอีกครั้ง;) Btw การอ่านจะได้รับผลกระทบมากจากสิ่งที่คุณเป็น เคย. มาจาก Delphi, C # กับประเภทของมันก่อนชื่อ var มีฉันกระหน่ำหัวของฉันค่อนข้างบางครั้ง
Marjan Venema

คำตอบ:


64

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

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

เรื่องนี้ได้รับโดยเฉพาะอย่างยิ่งเมื่อคุณพิจารณาทุกตัวเลือกการปรับเปลี่ยนชนิดที่เป็นภาษา "ไม่ทันสมัย" เช่น C ++ มีเช่น*สำหรับคำแนะนำ&สำหรับการอ้างอิงconst, volatileและอื่น ๆ เมื่อคุณใส่เครื่องหมายจุลภาคสำหรับการประกาศหลายรายการคุณจะเริ่มได้รับความคลุมเครือแปลก ๆ บางอย่างเช่นint* a, b;ไม่ทำตัวbชี้

แม้ c ++ ขณะนี้สนับสนุน "ประเภททางด้านขวา" ประกาศในรูปแบบของauto x = int{ 4 };และมันจะมีข้อได้เปรียบบางอย่าง


2
+1 สำหรับการยอมรับการมีอยู่ของคำหลักที่บังคับเช่นvarนั้นจะทำให้การแยกวิเคราะห์ส่วนใหญ่ง่ายขึ้น
8bittree

2
การมีอยู่ของvarคำหลักนั้นไม่ได้ทำลายจุดประสงค์ของสัญกรณ์แรกหรือไม่ ฉันไม่เห็นประโยชน์จากการเขียนมากกว่าvar userInput: String = getUserInput() String userInput = getUserInput()ข้อได้เปรียบจะมีความเกี่ยวข้องหากuserInput = getUserInput()ได้รับอนุญาตด้วย แต่นั่นก็เป็นการบอกนัยยะของการกำหนดตัวแปรที่มีขอบเขตซึ่งฉันคิดว่าน่ารังเกียจ
kdb

2
@kdb ในภาษาเช่น C # และ C ++ มันจะเป็นvar userInput = getUserInput()หรือauto userInput = getUserInput()คอมไพเลอร์รู้getUserInput()ผลตอบแทนStringดังนั้นคุณไม่จำเป็นต้องระบุด้วยคีย์เวิร์ดการลดชนิด userInput = getUserInput()เป็นของใช้แน่นอนก่อนประกาศ
Wes Toleman

32

ทำไมในภาษาการเขียนโปรแกรมสมัยใหม่เกือบทุกภาษา (Go, Rust, Kotlin, Swift, Scala, Nim, Python เวอร์ชันล่าสุด) มักจะเกิดขึ้นหลังจากการประกาศตัวแปรเสมอและไม่ใช่มาก่อน?

หลักฐานของคุณมีข้อบกพร่องสองมุมมอง:

  • มีภาษาโปรแกรมใหม่ ish ที่มีชนิดอยู่ข้างหน้าตัวระบุ ยกตัวอย่างเช่นC♯, D หรือ Ceylon
  • การมีชนิดหลังจากตัวระบุไม่ใช่ปรากฏการณ์ใหม่มันกลับไปอย่างน้อย Pascal (ออกแบบในปี 1968–1969 ที่วางจำหน่ายในปี 1970) แต่จริงๆแล้วถูกใช้ในทฤษฎีประเภทคณิตศาสตร์ซึ่งเริ่มต้น ~ 1902 มันยังใช้ใน ML (1973), CLU (1974), Hope (1970s), Modula-2 (2520-2528), Ada (1980), Miranda (1985), Mirl (1985), Caml (1985), Eiffel (1985), Oberon (1986), Modula-3 (1986–1988) และ Haskell (1989)

Pascal, ML, CLU, Modula-2 และ Miranda ทั้งหมดเป็นภาษาที่มีอิทธิพลมากดังนั้นจึงไม่น่าแปลกใจที่รูปแบบการประกาศประเภทนี้ยังคงเป็นที่นิยม

ทำไมx: int = 42ไม่int x = 42? สิ่งหลังไม่สามารถอ่านได้มากกว่าในอดีตหรือไม่

การอ่านเป็นเรื่องของความคุ้นเคย โดยส่วนตัวแล้วฉันคิดว่าคนจีนไม่สามารถอ่านได้ แต่เห็นได้ชัดว่าคนจีนไม่ได้ ต้องเรียนปาสคาลในโรงเรียนขลุกอยู่กับไอเฟลF♯แฮสเค็ลล์และสกาล่าและเมื่อดูที่ TypeScript, Fortress, Go, Rust, Kotlin, Idris, Frege, Agda, ML, Ocaml, ... มันดูเป็นธรรมชาติอย่างสมบูรณ์สำหรับฉัน .

มันเป็นเพียงเทรนด์หรือมีเหตุผลที่มีความหมายจริงๆที่อยู่เบื้องหลังโซลูชันดังกล่าวหรือไม่?

ถ้ามันเป็นเทรนด์มันคงไม่เปลี่ยนแปลง: อย่างที่ฉันพูดไปมันย้อนกลับไปเป็นร้อยปีในวิชาคณิตศาสตร์

หนึ่งในข้อดีหลักของการมีประเภทหลังจากตัวระบุคือมันเป็นเรื่องง่ายที่จะออกจากประเภทถ้าคุณต้องการที่จะอนุมาน หากการประกาศของคุณมีลักษณะเช่นนี้:

val i: Int = 10

จากนั้นมันก็ไม่สำคัญที่จะละทิ้งประเภทและอนุมานได้ดังนี้:

val i = 10

โดยที่ถ้าประเภทมาก่อนตัวระบุเช่นนี้:

Int i = 10

จากนั้นจะเริ่มยากสำหรับ parser เพื่อแยกความแตกต่างนิพจน์จากการประกาศ:

i = 10

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

var  i = 10; // C♯
auto i = 10; // C++

แต่สิ่งนี้ไม่สมเหตุสมผลนัก: โดยทั่วไปคุณต้องเขียนประเภทที่ระบุว่าคุณไม่ได้เขียนประเภท ฮะ? มันจะง่ายกว่าและมีเหตุผลมากกว่าที่จะทิ้งมันไว้ แต่นั่นทำให้ไวยากรณ์ซับซ้อนขึ้นมาก

(และอย่าพูดถึงประเภทของฟังก์ชั่นตัวชี้ในซีด้วย)

นักออกแบบของหลายภาษาดังกล่าวได้ชั่งน้ำหนักในเรื่องนี้:

  • ไปที่คำถามที่พบบ่อย (ดูเพิ่มเติมที่: ไวยากรณ์การประกาศของ Go ):

    เหตุใดจึงมีการประกาศย้อนหลัง

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

    int* a, b;
    

    ประกาศaที่จะเป็นตัวชี้ แต่ไม่b; ในไป

    var a, b *int
    

    ประกาศว่าเป็นพอยน์เตอร์ นี่คือชัดเจนและปกติมากขึ้น นอกจากนี้:=แบบฟอร์มการประกาศสั้น ๆ ระบุว่าการประกาศตัวแปรแบบเต็มควรแสดงลำดับเดียวกันเช่น:=นั้น

    var a uint64 = 1
    

    มีผลเช่นเดียวกับ

    a := uint64(1)
    

    การแยกวิเคราะห์ยังทำให้ง่ายขึ้นด้วยการมีไวยากรณ์ที่แตกต่างสำหรับประเภทที่ไม่ได้เป็นเพียงไวยากรณ์การแสดงออก; คำหลักเช่นfuncและchanเก็บสิ่งที่ชัดเจน

  • Kotlin คำถามที่พบบ่อย :

    เหตุใดจึงมีการประกาศชนิดทางด้านขวา

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

  • การเขียนโปรแกรมใน Scala :

    การเบี่ยงเบนที่สำคัญจาก Java เกี่ยวข้องกับไวยากรณ์ของคำอธิบายประกอบชนิด - " variable: Type" แทนที่จะเป็น " Type variable" ใน Java ไวยากรณ์ประเภท postfix ของ Scala คล้ายกับ Pascal, Modula-2 หรือ Eiffel เหตุผลหลักสำหรับการเบี่ยงเบนนี้จะทำอย่างไรกับการอนุมานประเภทซึ่งมักจะช่วยให้คุณละเว้นประเภทของตัวแปรหรือประเภทผลตอบแทนของวิธีการ การใช้variable: Typeไวยากรณ์ "" นี้เป็นเรื่องง่ายเพียงแค่ละทิ้งเครื่องหมายโคลอนและประเภท แต่ในType variableไวยากรณ์รูปแบบ C " " คุณไม่สามารถออกจากประเภทนี้ได้ - จะไม่มีเครื่องหมายเพื่อเริ่มต้นนิยามอีกต่อไป คุณต้องการคำหลักทางเลือกเพื่อเป็นตัวยึดตำแหน่งสำหรับประเภทที่ขาดหายไป (C♯ 3.0 ซึ่งใช้การอนุมานบางประเภทvarเพื่อวัตถุประสงค์นี้) คำหลักทางเลือกดังกล่าวให้ความรู้สึกต่อการโฆษณามากกว่าปกติและน้อยกว่าแนวทางของสกาล่า

หมายเหตุ: นักออกแบบของ Ceylon บันทึกไว้ด้วยว่าทำไมพวกเขาจึงใช้ไวยากรณ์ประเภทคำนำหน้า :

คำนำหน้าแทนที่จะเป็นคำอธิบายประกอบประเภท postfix

ทำไมคุณถึงทำตาม C และ Java ในการใส่คำอธิบายประกอบประเภทก่อนแทนที่จะ Pascal และ ML ในการวางพวกเขาหลังจากชื่อประกาศ?

เพราะเราคิดแบบนี้:

shared Float e = ....
shared Float log(Float b, Float x) { ... }

อ่านง่ายกว่านี้มาก:

shared value e: Float = .... 
shared function log(b: Float, x: Float): Float { ... }

และเราก็ไม่เข้าใจว่าใครจะคิดอย่างอื่นได้!

โดยส่วนตัวแล้วฉันพบว่า "การโต้เถียง" ของพวกเขามีความน่าเชื่อถือน้อยกว่าคนอื่นมาก


4
ดูเหมือนว่าความสามารถในการออกจากชนิดและยังคงมีการแยกง่ายได้รับจากการเพิ่มของvar, auto, letหรือชอบไม่ได้โดยที่ด้านข้างของตัวแปรการประกาศประเภทที่อยู่บน var Int x = 5หรือแม้กระทั่งอาจจะทั้งสองลงไปInt var x = 5 var x = 5
8bittree

5
ในขณะที่ฉันชอบที่จะอ่านได้อย่างเท่าเทียมกันเมื่อคุณคุ้นเคยกับมันและอาร์กิวเมนต์ประเภทที่เป็นตัวเลือกนั้นมีความแข็งแกร่งฉันต้องการชี้ให้เห็นว่า IDE ไม่สามารถเสนอชื่อตัวแปรโดยอัตโนมัติตามประเภท ฉันคิดถึงสิ่งนั้นเมื่อฉันเขียน TypeScript
ปิแอร์เฮนรี่

"Your premise is flawed on two fronts" ทั้งหมดเหล่านี้ใหม่กว่า C # หรือ D
เดช

3
"แต่มันไม่สมเหตุสมผลนัก: โดยทั่วไปคุณต้องเขียนประเภทที่ระบุว่าคุณไม่ได้เขียนประเภท" ทีนี้, รุ่นที่พิมพ์ทางด้านขวานั้นเหมือนกันในตัวอย่างของคุณ ... และฉันก็ไม่เห็นด้วยที่มันไม่สมเหตุสมผล มันสมเหตุสมผลอย่างมากเหมือนfuncใน Go
Sopel

4

ภาษาปาสกาลก็ทำเช่นกันและไม่ใช่ภาษาใหม่ มันเป็นภาษาเชิงวิชาการที่ออกแบบมาตั้งแต่เริ่มต้น

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

C # และ java เกิดจาก C ดังนั้นพวกเขาจึงต้องเคารพ "งานศิลปะก่อนหน้า" ดังนั้นอย่าสับสนกับโปรแกรมเมอร์ที่มีประสบการณ์


3
Ada ก็ทำเช่นนั้น แต่ Ada นั้นไม่ใช่ "ภาษาเชิงวิชาการ" อย่างแน่นอน
John R. Strohm

Ada ทำอย่างนี้เพราะมันเปลื้องผ้าอย่างหนักจาก Pascal และ Modula 2
Gort the Robot

2
@StevenBurnap การค้นหาอย่างรวดเร็วเผยให้เห็นว่าหนังสือเล่มแรกของ Wirth ใน Modula-2 ออกมากลางปี ​​1982 Ada อยู่ภายใต้การพัฒนาหลายปีก่อนหน้านั้น (DoD1 อยู่ในปี 1977 หรือมากกว่านั้นตามที่ฉันจำได้) และเป็นมาตรฐานทั้ง ANSI มาตรฐานและ MIL-STD ในปี 1983 ทั้งสี่ทีมที่ส่งผู้สมัครรับ Ada ใช้ PASCAL เป็นจุดเริ่มต้นของพวกเขา (เบลล์แล็บส์ได้รับเชิญจากกระทรวงฯ ให้ส่งภาษาผู้สมัครโดยอ้างอิงจากซีพวกเขาเข้าใจผิดโดยบอกว่าซีไม่เป็นเช่นนั้นและจะไม่แข็งแกร่งพอสำหรับซอฟต์แวร์ที่มีความสำคัญต่อภารกิจของกระทรวง)
John R. Strohm

ฉันจะต้องเข้าใจผิด ฉันคิดว่าเวิร์ ธ เข้าร่วมใน Ada แล้ว
Gort the Robot

ปาสคาลเริ่มออกเป็นภาษาวิชาการ แต่ในตอนท้ายของทศวรรษที่ 90 มันก็อยู่บนปากเหวของการเป็นภาษาในการเขียนของ Windows ปพลิเคชันผ่าน Delphi ที่เป็นจนกว่า Borland กระโดดปลาฉลามด้วยการกำหนดราคาและเปิดประตูทิ้งไว้สำหรับ Java และ C # เพื่อ มาขโมยรางวัล คุณจะยังคงเห็นเดลฟี่อยู่มากมายในโลกของแอพ Windows รุ่นเก่า
Shayne

1

ทำไมx: int = 42ไม่int x = 42? สิ่งหลังไม่สามารถอ่านได้มากกว่าในอดีตหรือไม่

ในตัวอย่างง่ายๆนั้นมีความแตกต่างไม่มากนัก แต่ขอทำให้มันซับซ้อนขึ้นอีกหน่อย: int* a, b;

นี่คือการประกาศจริงที่ถูกต้องใน C แต่มันไม่ได้ทำในสิ่งที่ดูเหมือนว่าควรจะทำ ดูเหมือนว่าเรากำลังประกาศตัวแปรทั้งสองประเภทint*แต่ที่จริงเรากำลังประกาศอย่างใดอย่างหนึ่งและเป็นหนึ่งในint*int

หากภาษาของคุณใส่ชนิดหลังชื่อตัวแปรในการประกาศปัญหานั้นจะไม่สามารถเกิดขึ้นได้


4
ฉันไม่แน่ใจว่าทำไมย้ายประกาศชนิดหลังจากนั้นจะส่งผลกระทบต่อ เป็นx, y *int;สอง*intหรือหนึ่ง*intและเป็นหนึ่งint? การสร้างint*หรือ*intโทเค็นหนึ่งแทนสองจะแก้ปัญหาได้หรือใช้องค์ประกอบวากยสัมพันธ์เพิ่มเติมเช่น:ซึ่งสามารถทำงานได้ทั้งสองทิศทาง
8bittree

@ 8bittree ทุกภาษาที่ฉันคุ้นเคยกับที่ใช้การประกาศชื่อแรกใช้โทเค็นเพิ่มเติมเช่น:หรือasระหว่างชื่อและประเภท
Mason Wheeler


2
และใน Go x, y *intหมายถึง "ประกาศตัวแปรสองตัวคือ x และ y พร้อมกับตัวชี้ชนิดไปยัง int"
Vatine

10
C # ใช้ไวยากรณ์คำนำหน้า แต่ก็ถือว่าint[] x, yเป็นสองอาร์เรย์ มันเป็นเพียงซินแท็กซ์ C โง่ ๆ ซึ่งถือว่าตัวดัดแปลงเหล่านั้นเป็นโอเปอเรเตอร์ unary ในตัวแปร
CodesInChaos
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.