“ หลักฐานคือโปรแกรม สูตรที่พิสูจน์เป็นประเภทของโปรแกรม”


37

นี่อาจเป็นคำถามเชิงปรัชญา แต่ฉันเชื่อว่ามีคำตอบที่ตรงตามวัตถุประสงค์

หากคุณอ่านบทความวิกิพีเดียเกี่ยวกับ Haskell คุณสามารถค้นหาสิ่งต่อไปนี้:

ภาษามีรากฐานมาจากการสังเกตของ Haskell Curry และทายาททางปัญญาของเขาว่า "การพิสูจน์คือโปรแกรม; สูตรที่พิสูจน์เป็นสูตรสำหรับโปรแกรม"

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


4
ทุกคนสนใจที่จะอธิบายว่าทำไมคะแนน "ปิด" กรุณา?

1
@Grigory Javadyan: ฉันไม่ได้ลงคะแนนให้ปิด แต่อาจเป็นเพราะคำถามนี้เป็นคำถามที่อยู่นอกขอบเขตของ SO - คำถามเชิงปรัชญาที่ตอบถูกหรือไม่เหมาะสม ในกรณีนี้ฉันคิดว่ามันสมเหตุสมผล แต่เพราะคำตอบนั้นมีความหมายเชิงลึกสำหรับวิธีการใช้ Haskell

2
@Grigory: หากคำถามนี้เป็นปัญหาจริงกับทางออกที่แท้จริง (ในรหัส) จากนั้นก็สามารถอยู่ใน SO โหวตให้ปิดและย้ายไปยังโปรแกรมเมอร์

9
เพียงแค่เพิ่มไปยังที่เพราะฉันบิตนึ่ง - คำตอบสำหรับคำถามนี้อุดมไปด้วยการอ้างอิงถึงการวิจัย CS อย่างหนักและในความหมายนั้น "วัตถุประสงค์" มากกว่า 90% ของ SO นอกจากนี้หลักเกณฑ์ของ sixlettervariable (ที่โซลูชันต้องการรหัส) นั้นแคบสำหรับคำถามการเขียนโปรแกรมของแท้ที่หลากหลายซึ่งไม่เป็นแบบอัตนัยและไม่เป็นหัวข้อ ผมจะเกลียดที่จะเห็นการฟื้นตัว inclusionist / deletionist การอภิปรายเกี่ยวกับ SO แต่ถ้าอย่างชัดเจนการเขียนโปรแกรมหัวข้อเช่นได้รับนี้ชนแล้วฉันต้องกังวล ...
sclv

2
ฉันสับสนเกี่ยวกับสิ่งที่เกิดขึ้นส่วนใหญ่เป็นเพราะฉันไม่ชัดเจนจริง ๆ ว่าเนื้อหาประเภทใดควรจะอยู่ใน Programmers.SE vs. SO แต่ผมจะบอกว่าโปรแกรมเมอร์อธิบายไว้ในหลายสถานที่ในฐานะที่เป็นสำหรับ "คำถามอัตนัย" ซึ่งคำถามนี้อย่างเด่นชัดไม่ได้ คำตอบของฉันเป็นเรื่องเกี่ยวกับที่ไม่เป็นทางการและเป็นคลื่นมือเท่าที่จะเป็นไปได้และฉันยังสามารถสำรองข้อมูลส่วนใหญ่ได้อย่างง่ายดายด้วยการอ้างอิงแม้บรรณาธิการของ Wikipedia จะยอมรับ
CA McCann

คำตอบ:


38

แนวคิดที่สำคัญนำไปใช้อย่างกว้างขวางในบางแฟชั่นใช่ แต่ไม่ค่อยมีประโยชน์

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

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

Haskell นั้นผิดปกติในจำนวนข้อมูลที่คาดว่าจะได้รับโดยเฉพาะอย่างยิ่งฟังก์ชั่นไม่สามารถขึ้นอยู่กับค่าอื่นใดนอกเหนือจากค่าที่ระบุไว้ในอาร์กิวเมนต์ ในภาษาที่มีตัวแปรโกลบอลที่ไม่แน่นอนในทางกลับกันฟังก์ชันใด ๆ สามารถ (อาจ) ตรวจสอบค่าเหล่านั้นและเปลี่ยนพฤติกรรมตามนั้น ดังนั้นฟังก์ชั่น Haskell ชนิดที่A -> Bสามารถถือได้ว่าเป็นโปรแกรมขนาดเล็กที่พิสูจน์ว่าAหมายถึงB; ฟังก์ชั่นเทียบเท่าในภาษาอื่น ๆ อีกมากมายเท่านั้นที่จะบอกเราว่าและสิ่งที่รัฐทั่วโลกอยู่ในขอบเขตรวมกันบ่งบอกถึงAB

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

มีมีวิธีการที่จะทำลายสิ่งที่อยู่ใน Haskell เช่นกันเช่นโดยการอนุญาตให้ยกเว้นรันไทม์หรือใช้การดำเนินการแบบดั้งเดิมที่ไม่ได้มาตรฐานให้โดยคอมไพเลอร์ แต่ผู้ที่มาพร้อมกับความคาดหวังที่แข็งแกร่งที่พวกเขาจะนำมาใช้กับความเข้าใจในวิธีการที่ได้รับรางวัล' ไม่ทำลายความหมายของรหัสภายนอก ในทางทฤษฎีอาจกล่าวได้ว่าเป็นภาษาอื่น แต่ในทางปฏิบัติกับภาษาอื่นส่วนใหญ่จะเป็นการยากที่จะทำสิ่งต่าง ๆ ให้สำเร็จโดยไม่ต้อง "โกง" และขมวดคิ้วน้อยลงเพื่อ "โกง" และแน่นอนในภาษา "แบบไดนามิก" ที่แท้จริงสิ่งทั้งหมดยังคงไม่เกี่ยวข้อง

แนวคิดนี้สามารถนำไปไกลกว่าที่คิดไว้ใน Haskell เช่นกัน


โปรดทราบว่าข้อยกเว้นสามารถรวมเข้ากับระบบชนิดได้อย่างสมบูรณ์
Gardenhead

18

คุณถูกต้องแล้วว่าการติดต่อกับ Curry-Howard นั้นเป็นเรื่องทั่วไปมาก ควรทำความคุ้นเคยกับประวัติของมันสักหน่อย: http://en.wikipedia.org/wiki/Curry-Howard_correspondence

คุณจะทราบว่าตามสูตรดั้งเดิมการติดต่อนี้ใช้โดยเฉพาะกับสัญชาตญาณลางสังหรณ์ในด้านหนึ่งและแลมบ์ดาแคลคูลัส (STLC) ที่พิมพ์เพียงด้านเดียว

Classic Haskell - '98 หรือแม้แต่รุ่นก่อนหน้านี้ได้ทำการสกัดอย่างใกล้ชิดกับ STLC และส่วนใหญ่มีการแปลที่ง่ายตรงไปตรงมาระหว่างการแสดงออกใด ๆ ใน Haskell และคำที่เกี่ยวข้องใน STLC (ขยายด้วยการเรียกซ้ำและ ประเภทดั้งเดิมไม่กี่) ดังนั้นสิ่งนี้ทำให้ Curry-Howard ชัดเจนมาก วันนี้ต้องขอบคุณส่วนขยายการแปลดังกล่าวเป็นธุรกิจที่ค่อนข้างยุ่งยากกว่า

ดังนั้นในแง่หนึ่งคำถามคือทำไม Haskell "desugars" เข้าสู่ STLC ตรงไปตรงมา นึกถึงสองสิ่ง:

  • ประเภท ซึ่งแตกต่างจาก Scheme ซึ่งเป็น lambda แคลคูลัสที่มีความแปลกใหม่ ซึ่งหมายความว่าไม่มีคำศัพท์ใน Haskell แบบคลาสสิกซึ่งคำจำกัดความไม่สามารถพิมพ์คำศัพท์ได้ดีใน STLC
  • ความบริสุทธิ์ อีกครั้งซึ่งแตกต่างจาก Scheme แต่เหมือน STLC, Haskell เป็นภาษาที่บริสุทธิ์และมีความโปร่งใสในการอ้างอิง สิ่งนี้ค่อนข้างสำคัญ ภาษาที่มีผลข้างเคียงสามารถฝังลงในภาษาที่ไม่มีผลข้างเคียง อย่างไรก็ตามการทำเช่นนั้นเป็นการเปลี่ยนแปลงทั้งโปรแกรมไม่ใช่เพียงการแยกแยะในท้องถิ่น ดังนั้นเพื่อให้มีการโต้ตอบโดยตรงคุณจำเป็นต้องเริ่มต้นด้วยภาษาที่ใช้งานได้จริง

นอกจากนี้ยังมีวิธีการสำคัญที่ Haskell เช่นเดียวกับภาษาส่วนใหญ่ล้มเหลวเนื่องจากการใช้งานโดยตรงของการติดต่อกับ Curry-Howard Haskell ซึ่งเป็นภาษาที่สมบูรณ์แบบมีความเป็นไปได้ที่จะเรียกซ้ำไม่ จำกัด และด้วยเหตุนี้จึงไม่มีการยกเลิก STLC ไม่มีตัวดำเนินการ fixpoint ไม่ได้ทำให้เสร็จสมบูรณ์และกำลังnormalizing อย่างยิ่ง - ซึ่งจะบอกว่าไม่มีการลดคำใน STLC จะล้มเหลวในการยกเลิก ความเป็นไปได้ของการเรียกซ้ำหมายความว่าเราสามารถ "โกง" Curry-Howard ตัวอย่างเช่นlet x = x in xมีประเภทforall a. a- กล่าวคือเนื่องจากมันไม่กลับมาเลยฉันสามารถทำเป็นแบบนั้นได้เลย! เนื่องจากเราสามารถทำสิ่งนี้ได้ตลอดเวลาใน Haskell นั่นหมายความว่าเราไม่สามารถ "เชื่อ" ได้อย่างเต็มที่ในการพิสูจน์ใด ๆ ที่เกี่ยวข้องกับโปรแกรม Haskell เว้นแต่ว่าเราจะมีหลักฐานแยกต่างหากว่าโปรแกรมกำลังจะสิ้นสุดลง

สายเลือดของการเขียนโปรแกรมการทำงานก่อน Haskell (โดยเฉพาะตระกูล ML) เป็นผลมาจากการวิจัย CS มุ่งเน้นไปที่การสร้างภาษาที่คุณสามารถพิสูจน์ได้อย่างง่ายดายเกี่ยวกับ (เหนือสิ่งอื่นใด) การวิจัยตระหนักถึงและเกิดจาก CH เพื่อเริ่มต้น ในทางกลับกัน Haskell ได้ทำหน้าที่เป็นทั้งโฮสต์ภาษาและเป็นแรงบันดาลใจให้กับผู้ช่วยพิสูจน์จำนวนมากภายใต้การพัฒนาเช่น Agda และ Epigram ซึ่งมีรากฐานมาจากการพัฒนาในทฤษฎีประเภทที่เกี่ยวข้องกับเชื้อสายของ CH


1
มันอาจเป็นการดีที่จะเน้นว่าการทำลายล้างทำลายการพิสูจน์ในวิธีการบางอย่างที่ในขณะที่ความหายนะอย่างเห็นได้ชัดจากมุมมองเชิงตรรกะรักษาคุณสมบัติอื่น ๆ โดยเฉพาะอย่างยิ่งฟังก์ชั่นที่A -> Bได้รับAอาจจะสร้างBหรือไม่มีอะไรเลย มันจะไม่ผลิต a Cและมูลค่าของประเภทที่Bให้หรือถ้ามันแตกต่างก็ยังคงขึ้นอยู่กับการAให้

@camccann - nitpicky เล็กน้อย แต่ฉันจะแยกความแตกต่างระหว่างด้านล่างและ "ไม่มีอะไรเลย" ซึ่งเป็นเหมือนVoidไม่? ความเกียจคร้านทำให้มันซับซ้อนมากขึ้นและน้อยลง ผมบอกว่าเป็นหน้าที่ของA -> B มักจะก่อให้เกิดค่าของชนิดBแต่ค่าที่อาจมีข้อมูลน้อยกว่าใครจะคาดคิด
sclv

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

12

สำหรับการประมาณลำดับที่หนึ่งภาษาอื่น ๆ ส่วนใหญ่ (อ่อนและ / หรือพิมพ์ไม่ได้) ไม่สนับสนุนการแยกระดับภาษาที่เข้มงวดระหว่าง

  • ข้อเสนอ (เช่นประเภท)
  • หลักฐาน (เช่นโปรแกรมที่แสดงให้เห็นถึงวิธีการที่เราสามารถสร้างข้อเสนอจากชุดของวิทยาการและ / หรืออื่น ๆที่สูงขึ้นเพื่อสร้าง)

และความสัมพันธ์ที่เข้มงวดระหว่างคนทั้งสอง หากมีสิ่งใดสิ่งที่ดีที่สุดรับประกันได้ว่าภาษาอื่น ๆ

  • เนื่องจากข้อ จำกัด ที่ จำกัด ของอินพุตรวมถึงสิ่งที่เกิดขึ้นในสภาพแวดล้อมในเวลานั้นเราสามารถสร้างมูลค่าด้วยข้อ จำกัด ที่ จำกัด (ประเภทสแตติกดั้งเดิม cf C / Java)
  • ทุกโครงสร้างมีประเภทเดียวกัน (ประเภทไดนามิก, cf ruby ​​/ python)

โปรดทราบว่าตามประเภทเราหมายถึงข้อเสนอและด้วยเหตุนี้สิ่งที่อธิบายข้อมูลเพิ่มเติมแล้วก็เป็นเพียงintหรือบูล ใน Haskell จะมีการเปลี่ยนแปลงของฟังก์ชันที่ได้รับผลกระทบจากอาร์กิวเมนต์เท่านั้น - ไม่มีข้อยกเว้น *

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

  • ข้อเสนอที่เข้มงวดสำหรับภาษาดั้งเดิมทั้งหมด
  • ชุดกลไกที่ จำกัด ซึ่งอาจรวมเข้าด้วยกัน

สิ่งปลูกสร้าง Haskell มีแนวโน้มที่จะปล่อยกู้ตัวเองได้ดีมากเพื่อให้เหตุผลเกี่ยวกับพฤติกรรมของพวกเขา หากเราสามารถสร้างหลักฐาน (read: function) เพื่อพิสูจน์ว่าAมีความหมายBก็จะมีคุณสมบัติที่มีประโยชน์มาก :

  • มันถืออยู่เสมอ (ตราบใดที่เรามีAเราสามารถสร้างได้B)
  • ความหมายนี้อาศัยเพียงบนAและไม่มีอะไรอื่น

ทำให้เราสามารถให้เหตุผลเกี่ยวกับค่าคงที่ท้องถิ่น / สากลได้อย่างมีประสิทธิภาพ เพื่อกลับไปที่คำถามเดิม คุณสมบัติทางภาษาของ Haskell ที่น่าสนใจที่สุดก็คือ:

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

ไม่มีสิ่งใดที่เป็นเอกลักษณ์ของ Haskell (ความคิดเหล่านี้ส่วนใหญ่แก่แล้วอย่างไม่น่าเชื่อ ) อย่างไรก็ตามเมื่อรวมเข้ากับชุดของ abstractions ในไลบรารีมาตรฐาน (มักพบในคลาสชนิด), sugaring ระดับไวยากรณ์ต่าง ๆ และความมุ่งมั่นอย่างเข้มงวดเพื่อความบริสุทธิ์ในการออกแบบโปรแกรมเราท้ายด้วยภาษาที่จัดการอย่างใดอย่างหนึ่งเพื่อทั้งสองอย่าง มีประโยชน์เพียงพอสำหรับการใช้งานในโลกแห่งความเป็นจริงแต่ในขณะเดียวกันก็พิสูจน์ได้ง่ายกว่าในเรื่องภาษาดั้งเดิมส่วนใหญ่

คำถามนี้ได้คำตอบที่ลึกพอสมควรและฉันไม่สามารถทำมันได้อย่างยุติธรรมในบริบทนี้ ฉันขอแนะนำให้อ่านเพิ่มเติมเกี่ยวกับวิกิพีเดีย / ในวรรณคดี:

* หมายเหตุ: ฉันกำลังคัดค้าน / เพิกเฉยต่อแง่มุมที่ซับซ้อนของสิ่งสกปรกของ Haskell (ข้อยกเว้นการไม่เลิก ฯลฯ ) ซึ่งจะทำให้การโต้แย้งซับซ้อนเท่านั้น


4

คุณสมบัติอะไร ระบบประเภท (เป็นแบบคงที่บริสุทธิ์ polymorphic) จุดเริ่มต้นที่ดีคือ "Theorems for Free" ของ Wadler ผลกระทบที่เห็นได้ชัดเจนในการออกแบบของภาษา? ประเภท IO, เรียนพิมพ์


0

Kleene ลำดับชั้นแสดงให้เราเห็นว่าพิสูจน์ไม่ได้โปรแกรม

ความสัมพันธ์แบบเรียกซ้ำครั้งแรกคือ:

R1( Program , Iteration )  Program halts at Iteration.
R2( Theorem , Proof ) Proof proves a Theorem.

ความสัมพันธ์ที่นับซ้ำครั้งแรกคือ:

(exists x) R1( Program , x )  Program Halts.
(exists x) R2( Theorem , x)   Theorem is provable.

เพื่อให้โปรแกรมเป็นทฤษฎีบทและการวนซ้ำที่มีอยู่ในโปรแกรมหยุดเหมือนเป็นหลักฐานที่มีอยู่ที่พิสูจน์ทฤษฎีบท

Program = Theorem
Iteration = Proof

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

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


2
คำถามนี้ตอบคำถามนี้อย่างไร "อะไรคือสิ่งที่เห็นได้ชัดเจนว่าข้อความนี้ส่งผลกระทบต่อการออกแบบภาษาอย่างไร"
gnat

1
@gnat - คำตอบนี้กล่าวถึงข้อสันนิษฐานที่แฝงอยู่ในคำถามเดิมกล่าวคือ: " doesn't this really apply to pretty much all the programming languages?" คำตอบนี้อ้าง / แสดงว่าการสันนิษฐานนั้นไม่ถูกต้องดังนั้นจึงไม่มีเหตุผลที่จะตอบคำถามที่เหลือซึ่งอยู่บนพื้นฐานของข้อบกพร่อง .
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.