วัตถุประสงค์ของลูกศรคืออะไร?


62

ฉันกำลังเรียนรู้การเขียนโปรแกรม functionnal กับ Haskell และฉันพยายามคว้าแนวคิดโดยการทำความเข้าใจก่อนว่าทำไมฉันถึงต้องการมัน

ฉันต้องการทราบเป้าหมายของลูกศรในภาษาโปรแกรมที่ใช้งานได้ พวกเขาแก้ปัญหาอะไร ฉันจะตรวจสอบhttp://en.wikibooks.org/wiki/Haskell/Understanding_arrowsและhttp://www.cse.chalmers.se/~rjmh/afp-arrows.pdf ทั้งหมดที่ฉันเข้าใจคือพวกเขาใช้เพื่ออธิบายกราฟสำหรับการคำนวณและพวกเขาอนุญาตให้เขียนโค้ดในรูปแบบจุดฟรีได้ง่ายขึ้น

บทความสมมติว่าสไตล์ฟรีพอยต์นั้นโดยทั่วไปง่ายต่อการเข้าใจและเขียน ดูเหมือนว่าฉันจะคิดแบบนี้ ในบทความอื่น ( http://en.wikibooks.org/wiki/Haskell/StephensArrowTutorial#Hangman:_Main_program ) มีการนำเกมแฮงก์แมนมาใช้ แต่ฉันไม่เห็นว่าลูกศรทำให้การติดตั้งนี้เป็นธรรมชาติอย่างไร

ฉันสามารถหาเอกสารจำนวนมากที่อธิบายแนวคิด แต่ไม่มีอะไรเกี่ยวกับแรงจูงใจ

ฉันกำลังคิดถึงอะไร

คำตอบ:


42

ฉันรู้ว่าฉันมางานปาร์ตี้ช้า แต่คุณมีคำตอบทางทฤษฎีสองข้อที่นี่และฉันต้องการให้ทางเลือกที่เป็นประโยชน์แก่การเคี้ยวมากกว่า ฉันมาที่นี่ในฐานะผู้เยาว์ Haskell ที่ยังไม่ได้รับการเดินขบวนในหัวข้อ Arrows สำหรับโครงการที่ฉันกำลังทำอยู่

ก่อนอื่นคุณสามารถแก้ไขปัญหาส่วนใหญ่ใน Haskell ได้อย่างมีประสิทธิภาพโดยไม่ต้องใช้ Arrows บาง Haskellers ที่โดดเด่นอย่างแท้จริงไม่ชอบและไม่ใช้พวกเขา (ดูที่นี่ , ที่นี่และที่นี่สำหรับข้อมูลเพิ่มเติมเกี่ยวกับเรื่องนี้) ดังนั้นหากคุณกำลังพูดกับตัวเอง "เฮ้ฉันไม่ต้องการสิ่งเหล่านี้" เข้าใจว่าคุณอาจถูกต้องอย่างแท้จริง

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

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

สิ่งที่ลูกศรอนุญาตให้ใช้ในระบบ FRP คือองค์ประกอบของฟังก์ชั่นเข้าสู่เครือข่ายในขณะเดียวกันก็สามารถแยกการอ้างอิงใด ๆ ไปยังค่าพื้นฐานที่ถูกส่งผ่านโดยฟังก์ชันเหล่านั้น หากคุณยังใหม่กับ FP สิ่งนี้อาจสร้างความสับสนในตอนแรกและจากนั้นก็เหลือเชื่อเมื่อคุณดูดซับความหมายของมัน คุณได้ดูดซึมได้เพียงไม่นานความคิดที่ว่าฟังก์ชั่นจะใจลอยและวิธีการที่จะเข้าใจรายการเช่นเป็นประเภท[(*), (+), (-)] [(a -> a -> a)]ด้วย Arrows คุณสามารถเพิ่ม Abstraction หนึ่งเลเยอร์ต่อไป

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

นอกจากนี้ยังมีตัวอย่างจำนวนหนึ่งของสิ่งที่ฉันเรียกว่า "Stupid Arrow Stunts" ที่ตัวเข้ารหัสสำหรับ Arrow Arrow combinator เพียงเพราะเขาหรือเธอต้องการที่จะแสดงกลอุบายกับ tuples (นี่คือสิ่งที่ฉันทำกับความบ้าคลั่งเล็กน้อย) รู้สึกอิสระที่จะเพิกเฉยต่อฮ็อตด็อกดังกล่าวเมื่อคุณเจอมันในป่า

หมายเหตุ: ตามที่ฉันได้กล่าวไว้ข้างต้น หากฉันประกาศใช้ความเข้าใจผิดใด ๆ ข้างต้นโปรดแก้ไขให้ถูกต้อง


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

ในขณะที่ลูกศรที่แน่นอนเครื่องมือที่ไม่ถูกต้องสำหรับการแก้ปัญหาการเชื่อมโยงของคุณฉันรู้สึกเหมือนฉันต้องพูดถึงว่าสามารถรัดกุมมากขึ้นอย่างชัดเจนและเขียนเป็นremoveAt' n = arr(\ xs -> (xs,xs)) >>> arr (take (n-1)) *** arr (drop n) >>> arr (uncurry (++)) >>> returnA removeAt' n = (arr (take $ n-1) &&& arr (drop n)) >>> (arr $ uncurry (++))
cemper93

29

นี่เป็นคำตอบที่ "นุ่มนวล" และฉันไม่แน่ใจว่าการอ้างอิงใด ๆ ระบุไว้ในลักษณะนี้จริงหรือไม่ แต่นี่คือวิธีที่ฉันได้คิดถึงลูกศร:

ประเภทลูกศรA b cนั้นเป็นฟังก์ชั่นb -> cแต่มีโครงสร้างอื่น ๆ ในทางเดียวกันว่าค่าเอกมีโครงสร้างมากขึ้นกว่าเก่าธรรมดาM aa

ตอนนี้โครงสร้างพิเศษที่เกิดขึ้นนั้นขึ้นอยู่กับอินสแตนซ์ลูกศรที่คุณกำลังพูดถึง เช่นเดียวกับ monads IO aและMaybe aแต่ละคนมีโครงสร้างเพิ่มเติมที่แตกต่างกัน

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

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

ตัวอย่างเช่นฟังก์ชั่นสัญญาณใน Yampa นั้นมีความคร่าวๆ(Time -> a) -> (Time -> b)แต่นอกจากนี้พวกเขายังต้องปฏิบัติตามข้อ จำกัด ที่เป็นเหตุเป็นผล : เอาต์พุตในเวลานั้นtถูกกำหนดโดยค่าที่ผ่านมาของสัญญาณอินพุต: คุณไม่สามารถมองเข้าไปในอนาคตได้ ดังนั้นสิ่งที่พวกเขาทำคือแทนที่จะเขียนโปรแกรมด้วย(Time -> a) -> (Time -> b)คุณตั้งโปรแกรมด้วยSF a bและคุณสร้างฟังก์ชั่นสัญญาณของคุณโดยไม่ใช้พื้นฐาน มันเกิดขึ้นเพราะSF a bมีพฤติกรรมเหมือนฟังก์ชั่นมากมายดังนั้นโครงสร้างทั่วไปจึงเป็นสิ่งที่เรียกว่า "ลูกศร"


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

14

ฉันชอบคิดถึง Arrows เช่น Monads และ Functors เพื่อให้โปรแกรมเมอร์ทำองค์ประกอบต่าง ๆ ที่แปลกใหม่

หากไม่มี Monads หรือ Arrows (และ Functors) การจัดองค์ประกอบของฟังก์ชั่นในภาษาที่ใช้งานจะถูก จำกัด ให้ใช้ฟังก์ชันหนึ่งกับผลลัพธ์ของฟังก์ชันอื่น ด้วย monads และ functors คุณสามารถกำหนดฟังก์ชั่นได้สองฟังก์ชั่นจากนั้นเขียนโค้ดที่นำมาใช้ซ้ำได้ซึ่งระบุวิธีที่ฟังก์ชั่นเหล่านั้นในบริบทของ Monad นั้นโต้ตอบกับแต่ละอื่น ๆ และกับข้อมูลที่ส่งผ่านเข้าไป รหัสนี้อยู่ในรหัสผูกของ Monad ดังนั้น monad เป็นหนึ่งมุมมองเพียงคอนเทนเนอร์สำหรับผูกรหัส reusuable ฟังก์ชั่นเขียนแตกต่างกันในบริบทของหนึ่ง monad จาก monad อื่น

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

Monads ยังหมายความว่าโปรแกรมเมอร์ไม่จำเป็นต้องพิมพ์พารามิเตอร์ที่แต่ละฟังก์ชันต้องใช้ในซอร์สโค้ดอย่างชัดเจน - ฟังก์ชัน bind จัดการกับการส่งพารามิเตอร์ ดังนั้นการใช้ monads ซอร์สโค้ดจึงเริ่มดูเหมือนชื่อของฟังก์ชันคงที่มากกว่าที่จะดูราวกับฟังก์ชั่น "เรียก" ฟังก์ชั่น B พร้อมพารามิเตอร์ C และ D - รหัสเริ่มรู้สึกเหมือนวงจรอิเล็กทรอนิกส์มากกว่า เครื่องย้าย - ทำงานได้มากกว่าความจำเป็น

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

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

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


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

3

นอกเหนือจากคำตอบอื่น ๆ : โดยส่วนตัวแล้วมันช่วยฉันได้มากที่จะเข้าใจว่าแนวคิดดังกล่าวคืออะไร (ทางคณิตศาสตร์) และเกี่ยวข้องกับแนวคิดอื่น ๆ ที่ฉันรู้จัก

ในกรณีที่ลูกศรที่ผมพบว่ากระดาษนี้เป็นประโยชน์ - มันเปรียบเทียบ monads, functors applicative (สำนวน) และลูกศร: สำนวนลบเลือน, ลูกศรมีความพิถีพิถัน monads มีสำส่อนโดยแซมลินด์, ฟิลิป Wadler และเจเร Yallop

นอกจากนี้ฉันเชื่อว่าไม่มีใครพูดถึงลิงก์นี้ซึ่งอาจให้ความคิดและวรรณกรรมเกี่ยวกับเรื่องนี้แก่คุณ

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