เหตุใดโปรแกรมการทำงานจึงมีความสัมพันธ์ระหว่างความสำเร็จในการรวบรวมและความถูกต้อง


12

ฉันมีแนวโน้มที่จะเขียนโปรแกรมการทำงานเป็นเวลา 4 ปีแล้วตั้งแต่ฉันเริ่มทำงานกับ LINQ เป็นครั้งแรก เมื่อเร็ว ๆ นี้ฉันเขียนโค้ด C # ที่ใช้งานได้จริงและฉันสังเกตเห็นสิ่งแรกที่ฉันได้อ่านเกี่ยวกับโปรแกรมที่ใช้งานได้ซึ่งเมื่อพวกเขารวบรวมพวกเขามักจะถูกต้อง

ฉันพยายามใส่นิ้วว่าทำไมกรณีนี้ แต่ฉันยังไม่ประสบความสำเร็จ

สิ่งหนึ่งที่เดาได้คือในการใช้หลักการ OO คุณมี "abstraction layer" ที่ไม่ได้อยู่ในโปรแกรมการทำงานและ layer abstraction นี้ทำให้สัญญาระหว่างวัตถุถูกต้องในขณะที่การนำไปใช้นั้นไม่ถูกต้อง

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


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

1
@delnan ฉันจะไม่พูดว่า Lisp เป็นภาษาการเขียนโปรแกรมที่ใช้งานได้แม้ว่าจะสามารถใช้ในการเขียนรหัสการทำงานของโปรแกรมได้ Clojure ซึ่งเป็นภาษา Lisp เป็นภาษาโปรแกรมที่ใช้งานได้
sakisk

2
ฉันเห็นด้วยกับ @delnan คำสั่งนี้มีความเกี่ยวข้องกับภาษาการเขียนโปรแกรมฟังก์ชั่นแบบคงที่โดยเฉพาะอย่างยิ่ง Haskell ซึ่งใช้ระบบ Hindley-Milner ฉันคิดว่าความคิดหลักคือถ้าคุณได้รับประเภทที่ถูกต้องความมั่นใจว่าโปรแกรมของคุณถูกต้องจะเพิ่มขึ้น
sakisk

1
โค้ดที่ใช้งานได้อาจมีบทคัดย่อและแนวอ้อมได้มากเท่ากับรหัส OOP ทั่วไปที่เป็นกระแสหลักของคุณ (ถ้าไม่มากกว่านั้น) ปีศาจอยู่ในรายละเอียด - ผลข้างเคียงน้อยลงและไม่มีโมฆะหมายถึงสถานะที่มองไม่เห็นน้อยลงในการติดตามและโอกาสที่จะพลาดน้อยลง โปรดทราบว่าคุณสามารถนำหลักการเดียวกันนี้ไปใช้กับภาษาที่จำเป็นต้องใช้กระแสหลักมันเป็นงานที่มากขึ้นและมักจะให้รายละเอียดมากขึ้น (เช่นต้องตบfinalทุกอย่าง)
Doval

1
ไม่ใช่คำตอบเต็มรูปแบบ แต่การพิมพ์โปรแกรมเป็นรูปแบบหยาบของการตรวจสอบอย่างเป็นทางการของโปรแกรม โดยทั่วไปโปรแกรม Object Oriented นั้นมีทั้งระบบที่ซับซ้อนหรือง่ายมากเนื่องจากการทดแทนต้องคำนึงถึง - ในกรณีส่วนใหญ่พวกเขาจะไม่ปลอดภัยเพื่อความสะดวก OTOH ระบบที่คล้าย ML สามารถใช้ CH ได้อย่างเต็มที่เพื่อให้คุณสามารถเข้ารหัสการพิสูจน์ในรูปแบบและใช้คอมไพเลอร์เป็นเครื่องมือตรวจสอบหลักฐาน
Maciej Piechotka

คำตอบ:


12

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

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

อย่างไรก็ตามเนื่องจากเราได้จ่ายค่าไพเพอร์ล่วงหน้าเราจึงมีข้อตกลงน้อยลงและเรามีทางเลือกน้อยลงสำหรับสิ่งต่าง ๆ ที่อาจผิดพลาดได้ หากคุณเป็นแฟน 1984 คุณมีอิสระที่จะเป็นทาส! ด้วยการใช้กลวิธีที่ประณีต 101 แบบที่แตกต่างกันสำหรับโปรแกรมเราจำเป็นต้องให้เหตุผลเกี่ยวกับสิ่งต่างๆ มันยากมากที่จะทำตามที่ปรากฏออกมา :)

หากคุณเริ่มต้นด้วยกรรไกรความปลอดภัยแทนที่จะเป็นดาบการวิ่งนั้นอันตรายน้อยกว่าปานกลาง

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

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


1
ฉันชอบคำตอบของคุณ แต่ฉันไม่เห็นว่ามันตอบคำถามของ OP
sakisk

1
@faif ขยายคำตอบของฉัน TLDR: ทุกคนเป็นนักคณิตศาสตร์
Daniel Gratzer

"ด้วยการใช้กลวิธีที่ประณีต 101 แบบที่แตกต่างกันสำหรับโปรแกรมเราต้องให้เหตุผลเกี่ยวกับสิ่งต่าง ๆ ราวกับว่าสิ่งเหล่านี้เกิดขึ้นได้ 101 ข้อ!": ฉันอ่านบางแห่งที่คุณต้องเป็นอัจฉริยะในการเขียนโปรแกรมด้วยการกลายพันธุ์เพราะคุณต้อง ข้อมูลมากมายในหัวของคุณ
จอร์โจ

12

เหตุผลพื้นฐานสำหรับความสัมพันธ์ระหว่างความสำเร็จในการรวบรวมและความถูกต้องของโปรแกรมในการเขียนโปรแกรมการทำงาน?

รัฐไม่แน่นอน

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

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

หากคุณทำโปรแกรมการทำงานได้ดีไม่มีสถานะที่เปลี่ยนแปลงได้ (หรือน้อยมาก)

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

เป็นไปได้ว่าทั้งสองอย่างผสมผสานกันในประสบการณ์ของฉัน


2
"น่าจะเป็นการผสมผสานทั้งสองอย่างในประสบการณ์ของฉัน": ฉันมีประสบการณ์แบบเดียวกัน การพิมพ์แบบคงที่จะจับข้อผิดพลาดในเวลารวบรวมเช่นกันเมื่อใช้ภาษาที่จำเป็น (เช่น Pascal) ใน FP การหลีกเลี่ยงความไม่แน่นอนและฉันจะเพิ่มการใช้รูปแบบการเขียนโปรแกรมเพิ่มเติมที่ประกาศทำให้ง่ายต่อการใช้เหตุผลเกี่ยวกับรหัส หากภาษามีทั้งสองอย่างคุณจะได้รับทั้งข้อดี
Giorgio

7

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

for (Iterator<String> iterator = list.iterator(); iterator.hasNext();) {
    String string = iterator.next();
    if (string.isEmpty()) {
        iterator.remove();
    }
}

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

ไม่ใช่เรื่องยากมากที่จะสร้างยาชื่อสามัญนี้ดังนั้นมันจะทำงานได้มากกว่าคอลเลกชันStringsแต่หากไม่มีฟังก์ชั่นชั้นหนึ่งคุณไม่สามารถแทนที่ภาคแสดง (เงื่อนไขภายในif) ดังนั้นรหัสนี้จึงมีแนวโน้มที่จะคัดลอกและวาง และแก้ไขเล็กน้อย

รวมฟังก์ชั่นชั้นหนึ่งที่ให้ความสามารถในการส่งผ่านเพรดิเคตเป็นพารามิเตอร์ด้วยข้อ จำกัด ของการเปลี่ยนไม่ได้ที่ทำให้มันน่ารำคาญมากถ้าคุณไม่ทำและคุณมาพร้อมโครงสร้างแบบง่ายๆเช่นfilterในรหัส Scala นี้ นั่นทำสิ่งเดียวกัน:

list filter (!_.isEmpty)

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

  • listต้องเป็นประเภทบางประเภทที่รองรับfilterเมธอดคือคอลเล็กชัน
  • องค์ประกอบของlistต้องมีisEmptyวิธีการที่ส่งกลับแบบบูล
  • ผลลัพธ์จะเป็นคอลเล็กชั่นที่มีขนาดเล็กลง (อาจเป็นไปได้) ที่มีองค์ประกอบประเภทเดียวกัน

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

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

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


"เมื่อมีการตรวจสอบสิ่งเหล่านี้แล้วจะมีวิธีอื่นใดอีกที่โปรแกรมเมอร์จะพลาด?": นี่เป็นการยืนยันประสบการณ์ของฉันว่า (1) การพิมพ์แบบสแตติก + (2) สไตล์การใช้งาน เป็นผลให้ฉันมักจะได้รับโปรแกรมที่ถูกต้องเร็วขึ้นและต้องเขียนหน่วยทดสอบน้อยลงเมื่อใช้ FP
Giorgio

2

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

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

อย่างไรก็ตามการเขียนโปรแกรมการทำงานต่อ se อาจช่วยในวิธีอื่นเช่นหลีกเลี่ยงข้อมูลที่ใช้ร่วมกันและสถานะที่ไม่แน่นอน

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


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

2

คำอธิบายสำหรับผู้จัดการ:

โปรแกรมการใช้งานนั้นเปรียบเสมือนเครื่องจักรขนาดใหญ่ที่เชื่อมต่อทุกอย่างท่อสายเคเบิล [รถ]

โปรแกรมขั้นตอนเป็นเหมือนอาคารที่มีห้องที่มีเครื่องจักรขนาดเล็กเก็บผลิตภัณฑ์บางส่วนไว้ในถังขยะรับผลิตภัณฑ์บางส่วนจากที่อื่น [โรงงาน]

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


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

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


1
Anology ที่ดีกว่า: การเขียนโปรแกรมฟังก์ชั่นเปรียบเสมือนโต๊ะช่วยเหลือที่ไม่มีลูกค้า - ทุกอย่างยอดเยี่ยม (ตราบใดที่คุณไม่ต้องถามวัตถุประสงค์หรือประสิทธิภาพ)
เบรนแดน

@Brendan car & factory ไม่ได้ทำการเปรียบเทียบที่ไม่ดี มันพยายามอธิบายว่าทำไม (โปรแกรมขนาดเล็ก) ในภาษาที่ใช้งานได้มีแนวโน้มที่จะทำงานและมีข้อผิดพลาดน้อยกว่า "โรงงาน" แต่เพื่อช่วยในการกล่าวว่า OOP มาจากโรงงานที่สามารถผลิตหลายสิ่งและมีขนาดใหญ่กว่า การเปรียบเทียบของคุณเหมาะสม ความถี่ในการได้ยินของ FP สามารถเปรียบเทียบและเพิ่มประสิทธิภาพได้อย่างมหาศาล แต่มีผล (ไม่เล่นสำนวน) ให้ผลลัพธ์ที่ช้า ฉันยังถือ FP อยู่
Joop Eggen

โปรแกรมการทำงานที่งานขนาดค่อนข้างดีสำหรับen.wikipedia.org/wiki/Spherical_cow ให้มันท้องถิ่น
Den

@Den ฉันเองจะไม่กลัวปัญหาความเป็นไปได้ในการทำงานในโครงการ FP ขนาดใหญ่ แม้จะรักมัน การวางมาตรฐานมีข้อ จำกัด แต่เป็นคำสั่งสุดท้ายที่เป็นลักษณะทั่วไปเกินไป ... (ขอบคุณสำหรับวัวทรงกลม)
Joop Eggen
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.