การเขียนโปรแกรมแบบอะซิงโครนัสในภาษาเชิงหน้าที่


31

ฉันส่วนใหญ่เป็นโปรแกรมเมอร์ C / C ++ ซึ่งหมายความว่าประสบการณ์ส่วนใหญ่ของฉันอยู่กับกระบวนทัศน์เชิงกระบวนทัศน์และเชิงวัตถุ อย่างไรก็ตามในขณะที่โปรแกรมเมอร์ C ++ หลายคนตระหนักดี C ++ ได้เปลี่ยนการเน้นในช่วงหลายปีที่ผ่านมาเป็นรูปแบบการใช้งานแบบเอสคิว

ไม่ว่าในขณะที่ฉันมีประสบการณ์การเขียนโค้ดในรูปแบบการทำงานโดยใช้ C ++ ฉันมีประสบการณ์น้อยมากกับภาษาที่ใช้งานได้จริงเช่น Lisp, Haskell เป็นต้น

ฉันเพิ่งเริ่มศึกษาภาษาเหล่านี้เนื่องจากความคิดที่ว่า "ไม่มีผลข้างเคียง" ในภาษาที่ใช้งานได้ล้วนทำให้ฉันทึ่งเสมอโดยเฉพาะอย่างยิ่งเกี่ยวกับแอพพลิเคชั่นของมันในการทำงานพร้อมกันและการคำนวณแบบกระจาย

อย่างไรก็ตามมาจากพื้นหลัง C ++ ฉันสับสนว่า philsophy "ไม่มีผลข้างเคียง" นี้ทำงานร่วมกับการเขียนโปรแกรมแบบอะซิงโครนัสได้อย่างไร โดยการเขียนโปรแกรมแบบอะซิงโครนัสฉันหมายถึงเฟรมเวิร์ก / API / รูปแบบการเข้ารหัสที่ให้ผู้ใช้จัดการเหตุการณ์เพื่อจัดการเหตุการณ์ที่เกิดขึ้นแบบอะซิงโครนัส (นอกกระแสของโปรแกรม) ซึ่งรวมถึงไลบรารีแบบอะซิงโคร ตัวจัดการสัญญาณหรือตัวจัดการเหตุการณ์ Java GUI

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

ดังนั้นดูเหมือนว่าการเขียนโปรแกรมแบบอะซิงโครนัสนั้นเกี่ยวกับการสร้างผลข้างเคียงแบบอะซิงโครนัส ดูเหมือนว่าจะขัดแย้งกับเป้าหมายของการเขียนโปรแกรมการทำงาน กระบวนทัศน์ทั้งสองนี้ได้รับการคืนดี (ในทางปฏิบัติ) ในภาษาเชิงหน้าที่อย่างไร


3
ว้าวฉันเพิ่งจะเขียนคำถามแบบนี้และไม่รู้ว่าจะนำไปใช้ได้อย่างไรและจากนั้นก็เห็นสิ่งนี้ในคำแนะนำ!
Amogh Talpallikar

คำตอบ:


11

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

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

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

สำหรับข้อมูลเพิ่มเติมเกี่ยวกับการเขียนโปรแกรมแบบอะซิงโครนัสในภาษาที่ใช้งานได้ผมขอแนะนำให้คุณศึกษารูปแบบการเขียนโปรแกรมแบบอะซิงโครนัสเวิร์กโฟลว์ของ F # อนุญาตให้คุณเขียนโปรแกรมที่ใช้งานได้ในขณะที่ซ่อนรายละเอียดยุ่งเหยิงทั้งหมดของการเปลี่ยนเธรดภายในไลบรารี (ในลักษณะคล้ายกับ Monads สไตล์ Haskell)

หาก 'ร่างกาย' ของเธรดคำนวณค่าเพียงจากนั้นวางไข่หลายเธรดและให้พวกเขาคำนวณค่าในแบบคู่ขนานยังคงอยู่ในกระบวนทัศน์การทำงาน


5
นอกจากนี้: การดู Erlang ช่วย ภาษานั้นง่ายมากบริสุทธิ์ (ข้อมูลทั้งหมดไม่เปลี่ยนรูป) และทั้งหมดเกี่ยวกับการประมวลผลแบบอะซิงโครนัส
9000

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

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

8

นี่เป็นคำถามที่น่าสนใจ สิ่งที่น่าสนใจที่สุดคือในมุมมองของฉันแนวทางที่นำมาใช้ใน Clojure และอธิบายในวิดีโอนี้:

http://www.infoq.com/presentations/Value-Identity-State-Rich-Hickey

โดยพื้นฐานแล้ว "การแก้ปัญหา" ที่เสนอมีดังนี้:

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

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


6

One note: ภาษาที่ใช้งานได้นั้นบริสุทธิ์ แต่รันไทม์ไม่ได้

ตัวอย่างเช่น Haskell runtimes เกี่ยวข้องกับการรอคิวการมัลติเพล็กซ์การรวบรวมขยะ ฯลฯ ซึ่งทั้งหมดนั้นไม่บริสุทธิ์

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


2

ฉันสับสนว่า philsophy "ไม่มีผลข้างเคียง" นี้ทำงานร่วมกับการเขียนโปรแกรมแบบอะซิงโครนัสได้อย่างไร โดยการเขียนโปรแกรมแบบอะซิงโครนัสฉันหมายถึง ...

นั่นจะเป็นประเด็นแล้ว

เสียงไม่มีลักษณะข้างเคียงไม่เข้ากันกับกรอบที่ขึ้นอยู่กับสถานะ ค้นหาเฟรมเวิร์กใหม่

ยกตัวอย่างเช่นมาตรฐาน WSGI ของ Python ช่วยให้เราสามารถสร้างแอปพลิเคชันที่ไม่มีผลข้างเคียง

แนวคิดก็คือว่า "การเปลี่ยนแปลงสถานะ" ต่าง ๆ นั้นสะท้อนออกมาจากสภาพแวดล้อมของค่านิยมที่สามารถสร้างขึ้นแบบค่อยเป็นค่อยไป แต่ละคำขอเป็นขั้นตอนของการเปลี่ยนแปลง


"อนุญาตให้สร้างแอปพลิเคชันที่ไม่มีผลข้างเคียง" ฉันคิดว่ามีคำใดคำหนึ่งหายไป
Christopher Mahan

1

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

การยึดมั่นในกรอบความคิดเฉพาะทำให้เกิดความไม่สะดวกในการจัดการกับ abstractions รั่วสำหรับเช่น runtimes เชิงพาณิชย์เช่น JRE, DirectX, .net วัตถุเป้าหมายที่มุ่งเน้นผู้สนับสนุนที่สำคัญที่สุด เพื่อจำกัดความไม่สะดวกภาษาเลือกใช้ monads ที่มีความซับซ้อนทางวิชาการอย่าง Haskell หรือการสนับสนุนกระบวนทัศน์หลากหลายที่ยืดหยุ่นอย่าง F # ในที่สุดก็มี เว้นแต่ encapsulation จะมีประโยชน์จากบางกรณีการใช้การสืบทอดหลายวิธีการหลายกระบวนทัศน์อาจเป็นทางเลือกที่เหนือกว่าสำหรับบางรูปแบบการเขียนโปรแกรมที่เฉพาะเจาะจงกระบวนทัศน์ที่ซับซ้อน

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