ทำไมไม่ใส่คำอธิบายประกอบพารามิเตอร์ฟังก์ชั่น?


28

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

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

let foo x y = x + y

ตอนนี้การตรวจสอบอย่างรวดเร็วของคำแนะนำเครื่องมือจะแสดงให้คุณเห็นว่า F # ได้พิจารณาแล้วว่าคุณต้องการให้ x และ y เป็น ints ถ้านั่นคือสิ่งที่คุณตั้งใจไว้ทุกอย่างก็ดี แต่ฉันไม่รู้ว่านั่นคือสิ่งที่คุณต้องการหรือไม่ จะเกิดอะไรขึ้นถ้าคุณสร้างรหัสนี้เพื่อเชื่อมสองสายเข้าด้วยกัน หรือถ้าฉันคิดว่าคุณอาจหมายถึงการเพิ่มคู่? หรือถ้าฉันไม่ต้องการเลื่อนเมาส์ไปเหนือพารามิเตอร์ฟังก์ชันทุกตัวเพื่อกำหนดประเภทของมัน

ตอนนี้ใช้ตัวอย่างนี้:

let foo x y = "result: " + x + y

ตอนนี้ F # สมมติว่าคุณตั้งใจจะเชื่อมสตริงดังนั้น x และ y จึงถูกนิยามเป็นสตริง อย่างไรก็ตามในฐานะที่เป็น schmuck ที่ไม่ดีที่รักษารหัสของคุณฉันอาจดูที่นี่และสงสัยว่าบางทีคุณอาจต้องการเพิ่ม x และ y (ints) ด้วยกันแล้วผนวกผลลัพธ์ไปยังสตริงเพื่อจุดประสงค์ของ UI

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

let foo (x:string) (y:string) = "result: " + x + y

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

นี่เป็นคำถามที่จริงจัง ... ฉันยังใหม่กับ F # และกำลังเป็นที่ประจักษ์แก่ บริษัท ของฉัน มาตรฐานที่ฉันนำมาใช้น่าจะเป็นพื้นฐานสำหรับการเข้ารหัส F # ในอนาคตทั้งหมดซึ่งฝังอยู่ในการคัดลอกที่ไม่รู้จบที่ฉันมั่นใจว่าจะแทรกซึมวัฒนธรรมสำหรับปีต่อ ๆ ไป

ดังนั้น ... มีอะไรพิเศษเกี่ยวกับการอนุมานของ F # ที่ทำให้มันมีคุณสมบัติที่มีค่าที่จะเก็บไว้ทำหมายเหตุประกอบเมื่อจำเป็นเท่านั้น? หรือผู้ชำนาญการ # # สร้างนิสัยในการเพิ่มความคิดเห็นพารามิเตอร์สำหรับแอปพลิเคชันที่ไม่สำคัญหรือไม่?


รุ่นที่มีคำอธิบายประกอบมีความทั่วไปน้อยกว่า ด้วยเหตุผลเดียวกับที่คุณควรใช้ IEnumerable <> ในลายเซ็นและไม่ใช่ SortedSet <>
Patrick

2
@Patrick - ไม่ไม่ใช่เพราะ F # อนุมานชนิดของสตริง ฉันไม่ได้เปลี่ยนลายเซ็นของฟังก์ชันทำให้ชัดเจนเท่านั้น
JDB

1
@ แพทริค - และแม้ว่ามันจะเป็นจริงคุณสามารถอธิบายได้ว่าทำไมมันถึงเป็นสิ่งที่ไม่ดี? บางทีลายเซ็นควรจะน้อยกว่าถ้าเป็นสิ่งที่โปรแกรมเมอร์ตั้งใจไว้ บางทีลายเซ็นทั่วไปที่มากกว่านั้นก่อให้เกิดปัญหาจริง ๆ แต่ฉันไม่แน่ใจว่าโปรแกรมเมอร์ที่เจาะจงตั้งใจที่จะไม่มีการวิจัยจำนวนมาก
JDB

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

คำตอบ:


30

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

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

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

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

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


3
ฟังดูเหมือนเป็นการตอบสนองที่สมเหตุสมผลและมีความสมดุล
JDB

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

4
ใน Haskell นั้นถือว่าถูกต้องเพื่อใส่คำอธิบายประกอบฟังก์ชั่นของคุณ แต่ฉันเชื่อว่าสำนวน F # และ OCaml มีแนวโน้มที่จะละเว้นคำอธิบายประกอบเว้นแต่จำเป็นต้องลบความคลุมเครือ ไม่ได้หมายความว่าจะเป็นอันตราย (แม้ว่าไวยากรณ์สำหรับการเพิ่มความคิดเห็นใน F # นั้นน่าเกลียดกว่าใน Haskell)
KChaloux

4
@KChaloux ใน OCaml มีการพิมพ์คำอธิบายประกอบในไฟล์ interface ( .mli) (ถ้าคุณเขียนชื่อสิ่งที่คุณได้รับการสนับสนุนอย่างมาก) คำอธิบายประกอบประเภทมักจะถูกละเว้นจากคำจำกัดความเนื่องจากพวกเขาจะซ้ำซ้อนกับส่วนติดต่อ
Gilles ' ดังนั้นหยุดความชั่วร้าย

1
@Gilles @KChaloux แน่นอนเหมือนกันใน F # ลายเซ็น ( .fsi) ไฟล์กับหนึ่งข้อแม้: F # ไม่ได้ใช้แฟ้มลายเซ็นสำหรับประเภทการอนุมานดังนั้นหากสิ่งที่ในการดำเนินการไม่ชัดเจนคุณจะยังคงต้องใส่คำอธิบายประกอบอีกครั้ง books.google.ca/…
Yawar Amin

1

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

เมื่อเร็ว ๆ นี้ฉันได้ทำงานกับการวิเคราะห์คำโดยใช้ตัวแยกวิเคราะห์ parser หากคุณรู้จักการแยกวิเคราะห์คุณจะรู้ว่าโดยปกติแล้วคุณใช้ lexer ในระยะแรกและใช้ parser ในระยะที่สอง lexer แปลงข้อความเป็นโทเค็นและ parser แปลงโทเค็นให้เป็น AST ขณะนี้ด้วย F # เป็นภาษาที่ใช้งานได้และ combinators ที่ออกแบบมาเพื่อรวมกันตัวแยกวิเคราะห์ได้รับการออกแบบมาเพื่อใช้ประโยชน์จากฟังก์ชั่นเดียวกันทั้งใน lexer และ parser แต่ถ้าคุณตั้งประเภทสำหรับฟังก์ชันตัวแยกส่วน combinator คุณสามารถใช้พวกมันได้เท่านั้น lex หรือแจงและไม่ใช่ทั้งคู่

ตัวอย่างเช่น:

/// Parser that requires a specific item.

// a (tok : 'a) : ('a list -> 'a * 'a list)                     // generic
// a (tok : string) : (string list -> string * string list)     // string
// a (tok : token)  : (token list  -> token  * token list)      // token

หรือ

/// Parses iterated left-associated binary operator.


// leftbin (prs : 'a -> 'b * 'c) (sep : 'c -> 'd * 'a) (cons : 'd -> 'b -> 'b -> 'b) (err : string) : ('a -> 'b * 'c)                                                                                    // generic
// leftbin (prs : string list -> string * string list) (sep : string list -> string * string list) (cons : string -> string -> string -> string) (err : string) : (string list -> string * string list)  // string
// leftbin (prs : token list  -> token  * token list)  (sep : token list  -> token  * token list)  (cons : token  -> token  -> token  -> token)  (err : string) : (token list  -> token  * token list)   // token

ตั้งแต่รหัสเป็นลิขสิทธิ์ฉันจะไม่รวมไว้ที่นี่ แต่มันสามารถใช้ได้ที่Github อย่าคัดลอกที่นี่

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


1
ทำไมคุณไม่เขียนรุ่นทั่วไปลงในฟังก์ชั่น? มันจะไม่ทำงานเหรอ
svick

@svick ฉันไม่เข้าใจคำถามของคุณ คุณหมายถึงว่าฉันทิ้งประเภทของคำจำกัดความของฟังก์ชั่น แต่อาจเพิ่มประเภททั่วไปลงในคำจำกัดความของฟังก์ชั่นเนื่องจากประเภททั่วไปจะไม่เปลี่ยนความหมายของฟังก์ชั่น? ถ้าเป็นเช่นนั้นเหตุผลก็คือความชอบส่วนตัวมากกว่า เมื่อฉันแรกเริ่มด้วย F # ฉันเพิ่มพวกเขา เมื่อฉันเริ่มทำงานกับผู้ใช้ F # ขั้นสูงพวกเขาต้องการให้พวกเขาเป็นความคิดเห็นเพราะง่ายต่อการแก้ไขรหัสโดยไม่มีลายเซ็นที่นั่น ...
Guy Coder

ในระยะยาววิธีที่ฉันแสดงให้พวกเขาเป็นความคิดเห็นที่ทำงานให้กับทุกคนเมื่อรหัสทำงาน เมื่อฉันเริ่มเขียนฟังก์ชั่นฉันใส่ในประเภท เมื่อฉันมีฟังก์ชั่นการทำงานฉันย้ายลายเซ็นประเภทไปที่ความคิดเห็นและลบประเภทมากที่สุดเท่าที่จะทำได้ บางครั้งด้วย F # คุณต้องออกจากประเภทเพื่อช่วยในการอนุมาน ในขณะที่ฉันกำลังทดสอบกับการสร้างความคิดเห็นด้วยการให้และ = เพื่อให้คุณสามารถยกเลิกการใส่เครื่องหมายบรรทัดสำหรับการทดสอบแล้วแสดงความคิดเห็นอีกครั้งเมื่อเสร็จแล้ว แต่พวกเขาดูโง่และเพิ่มการให้และ = ไม่ยาก
Guy Coder

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

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

-8

จำนวนข้อบกพร่องเป็นสัดส่วนโดยตรงกับจำนวนตัวละครในโปรแกรม!

โดยปกติจะระบุเป็นจำนวนข้อบกพร่องที่เป็นสัดส่วนกับบรรทัดของรหัส แต่การมีบรรทัดน้อยลงโดยมีจำนวนรหัสเท่ากันจะทำให้มีจำนวนบั๊กเท่ากัน

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


5
“ จำนวนข้อบกพร่องเป็นสัดส่วนโดยตรงกับจำนวนตัวอักษรในโปรแกรม!” ดังนั้นเราทุกคนควรเปลี่ยนเป็นAPL ? การกระชับให้สั้นเกินไปเป็นปัญหาเช่นเดียวกับการใช้คำฟุ่มเฟือยเกินไป
svick

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