การเขียนโปรแกรมปฏิกิริยา (ทำงาน) คืออะไร?


1148

ผมเคยอ่านบทความวิกิพีเดียในการเขียนโปรแกรมปฏิกิริยา ฉันยังอ่านบทความเล็ก ๆ เกี่ยวกับการเขียนโปรแกรมปฏิกิริยาการทำงาน คำอธิบายค่อนข้างเป็นนามธรรม

  1. functional reactive programming (FRP) หมายถึงอะไรในทางปฏิบัติ?
  2. การเขียนโปรแกรมเชิงรับ (reactive programming) คืออะไร?

ภูมิหลังของฉันอยู่ในความจำเป็น / ภาษา OO ดังนั้นคำอธิบายที่เกี่ยวข้องกับกระบวนทัศน์นี้จะได้รับการชื่นชม


159
นี่คือผู้ชายที่มีจินตนาการที่กระตือรือร้นและมีทักษะการเล่าเรื่องที่ดี paulstovell.com/reactive-programming
melaos

39
ใครบางคนต้องเขียน "ฟังก์ชั่นการเขียนโปรแกรมปฏิกิริยาสำหรับ Dummies" สำหรับพวกเราทุกคน autodidacts ออกที่นี่ ทรัพยากรทุกอย่างที่ฉันพบแม้แต่เอล์มดูเหมือนว่าคุณได้รับปริญญาโทใน CS ในช่วงห้าปีที่ผ่านมา คนที่มีความรู้เกี่ยวกับ FRP ดูเหมือนจะสูญเสียความสามารถในการมองเห็นปัญหาจากมุมมองที่ไร้เดียงสาอย่างสิ้นเชิงสิ่งที่สำคัญสำหรับการสอนการฝึกอบรมและการประกาศข่าวประเสริฐ
TechZen

26
อีกหนึ่งคำแนะนำ FRP ที่ยอดเยี่ยม: คำแนะนำเกี่ยวกับการเขียนโปรแกรมปฏิกิริยาที่คุณพลาดโดยเพื่อนร่วมงานของฉันAndré
Jonik

5
หนึ่งในสิ่งที่ดีที่สุดที่ฉันเคยเห็นตัวอย่างจาก: gist.github.com/staltz/868e7e9bc2a7b8c1f754
Razmig

2
ฉันพบว่าสเปรดชีตคล้ายคลึงกันมีประโยชน์มากสำหรับการแสดงผลคร่าวๆครั้งแรก (ดูคำตอบของ Bob: stackoverflow.com/a/1033066/1593924 ) เซลล์สเปรดชีตตอบสนองต่อการเปลี่ยนแปลงในเซลล์อื่น (ดึง) แต่ไม่สามารถเข้าถึงและเปลี่ยนแปลงอื่น ๆ (ไม่ดัน) ผลลัพธ์ที่ได้คือคุณสามารถเปลี่ยนหนึ่งเซลล์และอัปเดตจอแสดงผลของตัวเองอีกหลายพันล้านเซลล์
Jon Coombs

คำตอบ:


931

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

โดยส่วนตัวฉันชอบคิดว่า FRP หมายถึงอะไรก่อนที่จะพูดถึงวิธีการนำไปใช้ (รหัสที่ไม่มีสเปคคือคำตอบที่ไม่มีคำถามดังนั้นจึง "ไม่ผิด" ดังนั้นฉันจึงไม่อธิบาย FRP ในแง่ของการเป็นตัวแทน / การนำไปใช้งานอย่างที่ Thomas K ทำในคำตอบอื่น (กราฟ, โหนด, ขอบ, การยิง, การประหารชีวิต) ฯลฯ ) มีสไตล์การใช้งานที่เป็นไปได้มากมาย แต่ไม่มีการนำไปใช้บอกว่า FRP คืออะไร

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

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

FRP คืออะไร คุณสามารถคิดค้นมันเองได้ เริ่มต้นด้วยแนวคิดเหล่านี้:

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

  • พฤติกรรมถูกสร้างขึ้นจากสิ่งดั้งเดิมสองสามอย่างเช่นพฤติกรรมคงที่ (คงที่) และเวลา (เช่นนาฬิกา) จากนั้นมีการรวมกันตามลำดับและขนาน พฤติกรรมnจะรวมกันโดยใช้ฟังก์ชั่น n-ary (ในค่าคงที่), "จุดฉลาด" คืออย่างต่อเนื่องเมื่อเวลาผ่านไป

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

  • หากต้องการหาคำศัพท์ประกอบที่สามารถสร้างพฤติกรรมและเหตุการณ์ทั้งหมดให้เล่นด้วยตัวอย่าง แยกชิ้นส่วนออกเป็นชิ้น ๆ ที่ดูธรรมดา / เรียบง่าย

  • เพื่อให้คุณรู้ว่าคุณอยู่บนพื้นแข็งให้ทั้งโมเดลวางรากฐานองค์ประกอบโดยใช้เทคนิคของความหมายเชิง Denotational ซึ่งหมายความว่า (a) แต่ละประเภทมีประเภทของคณิตศาสตร์ที่ง่ายและแม่นยำของ "ความหมาย" และ ( b) ดั้งเดิมและผู้ปฏิบัติงานแต่ละคนมีความหมายที่เรียบง่ายและแม่นยำเป็นฟังก์ชั่นของความหมายขององค์ประกอบ ไม่เคยผสมผสานข้อควรพิจารณาในการใช้งานกับกระบวนการสำรวจ หากคำอธิบายนี้มีความหมายกับคุณให้ปรึกษา (a) การออกแบบเชิง Denotational ที่มี morphisms คลาสประเภท (b) การเขียนโปรแกรมปฏิกิริยาแบบ Push-pull (โดยไม่สนใจบิตของการใช้งาน) และ (c) หน้าDenotational Semantics Haskell. ระวังว่าความหมายเชิง Denotational มีสองส่วนจากผู้ก่อตั้งสองคน Christopher Strachey และ Dana Scott: ส่วน Strachey ที่ง่ายและมีประโยชน์มากขึ้นและส่วนที่ยากขึ้นและมีประโยชน์น้อยลง (สำหรับการออกแบบซอฟต์แวร์) ส่วน Scott

หากคุณยึดติดกับหลักการเหล่านี้ฉันคาดหวังว่าคุณจะได้รับอะไรมากกว่าหรือน้อยกว่าในจิตวิญญาณของ FRP

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

มันค่อนข้างท้าทายที่จะใช้โมเดลนี้อย่างถูกต้องและมีประสิทธิภาพ แต่นั่นเป็นอีกเรื่องหนึ่ง


78
ฉันได้รับรู้ถึงการเขียนโปรแกรมปฏิกิริยาการทำงาน ดูเหมือนว่าเกี่ยวข้องกับการวิจัยของฉันเอง (ในกราฟิกเชิงสถิติเชิงโต้ตอบ) และฉันมั่นใจว่าความคิดหลายอย่างจะเป็นประโยชน์สำหรับงานของฉัน อย่างไรก็ตามฉันพบว่ามันยากมากที่จะผ่านภาษา - ฉันต้องเรียนรู้เกี่ยวกับ "ความหมายเชิงลบ" และ "morphisms คลาส" เพื่อเข้าใจว่าเกิดอะไรขึ้น? การแนะนำผู้ชมทั่วไปในหัวข้อจะมีประโยชน์มาก
hadley

212
@ Conal: คุณรู้อย่างชัดเจนว่าคุณกำลังพูดถึงเรื่องอะไร แต่ภาษาของคุณทึกทักว่าฉันมีปริญญาเอกในวิชาคณิตศาสตร์การคำนวณซึ่งฉันไม่ได้ ฉันมีพื้นฐานด้านวิศวกรรมระบบและมีประสบการณ์มากกว่า 20 ปีกับคอมพิวเตอร์และภาษาการเขียนโปรแกรม แต่ฉันก็ยังรู้สึกว่าคำตอบของคุณทำให้ฉันงุนงง ฉันขอท้าให้คุณโพสต์คำตอบเป็นภาษาอังกฤษ ;-)
mindplay.dk

50
@ minplay.dk: คำพูดของคุณไม่ได้ให้อะไรฉันมากนักเกี่ยวกับสิ่งที่คุณไม่เข้าใจและฉันก็ไม่อยากจะเดาอะไรเกี่ยวกับสิ่งที่คุณกำลังมองหา อย่างไรก็ตามฉันขอเชิญคุณที่จะพูดโดยเฉพาะในแง่มุมต่าง ๆ ของคำอธิบายข้างต้นที่คุณกำลังทำสะดุดเพื่อที่ฉันและคนอื่น ๆ จะสามารถช่วยเหลือคุณได้ ตัวอย่างเช่นมีคำเฉพาะที่คุณต้องการกำหนดหรือแนวคิดที่คุณต้องการเพิ่มการอ้างอิงหรือไม่ ฉันชอบที่จะปรับปรุงความชัดเจนและการเข้าถึงการเขียนของฉัน - โดยไม่ทำให้แย่ลง
Conal

27
"ความมุ่งมั่น" / "กำหนด" หมายถึงมีค่าที่ถูกต้องที่กำหนดชัดเจน ในทางกลับกันรูปแบบการทำงานพร้อมกันที่จำเป็นเกือบทั้งหมดสามารถให้คำตอบที่แตกต่างกันขึ้นอยู่กับตัวกำหนดตารางเวลาหรือไม่ว่าคุณจะมองหรือไม่และพวกเขาสามารถหยุดชะงักได้ "ความหมาย" (และเฉพาะเจาะจงมากขึ้น "denotational") หมายถึงค่า ("denotation") ของการแสดงออกหรือการเป็นตัวแทนในทางตรงกันข้ามกับ "การดำเนินงาน" (วิธีคำนวณคำตอบหรือจำนวนพื้นที่และ / หรือเวลาที่ใช้ไป ชนิดของเครื่อง)
Conal

18
ฉันเห็นด้วยกับ @ mindplay.dk แม้ว่าฉันจะไม่สามารถโม้อยู่ในสนามได้นานนัก แม้ว่าดูเหมือนคุณจะรู้ว่าสิ่งที่คุณกำลังพูดถึงมันไม่ได้ให้ความเข้าใจอย่างรวดเร็วสั้นและเรียบง่ายของสิ่งนี้คือสิ่งที่ฉันเสียมากพอที่จะคาดหวังใน SO คำตอบนี้ทำให้ฉันมีคำถามใหม่มากมายโดยไม่ตอบคำถามแรกของฉัน ฉันหวังว่าการแบ่งปันประสบการณ์การยังคงไม่รู้ค่อนข้างในสนามสามารถให้คุณเข้าใจว่าคุณจำเป็นต้องเรียบง่ายและสั้น ฉันมาจากพื้นหลังที่คล้ายกันเป็น OP, btw
Aske B.

739

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

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

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

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

x = <mouse-x>;
y = <mouse-y>;

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

ถ้าเราทำการคำนวณตามค่านี้ผลลัพธ์ที่ได้ก็จะเป็นค่าที่เปลี่ยนแปลงตลอดเวลา ตัวอย่างเช่น:

minX = x - 16;
minY = y - 16;
maxX = x + 16;
maxY = y + 16;

ในตัวอย่างนี้minXจะน้อยกว่าพิกัด x ของตัวชี้เมาส์ 16 เสมอ ด้วยไลบรารีที่รับรู้ปฏิกิริยาคุณสามารถพูดสิ่งที่ชอบ:

rectangle(minX, minY, maxX, maxY)

และกล่องขนาด 32x32 จะถูกลากไปรอบ ๆ ตัวชี้เมาส์และจะติดตามมันทุกที่ที่มันขยับ

นี่คือสิ่งที่ดีงามกระดาษในการเขียนโปรแกรมปฏิกิริยาการทำงาน


25
ดังนั้นการโปรแกรมเชิงรับก็เป็นรูปแบบหนึ่งของการเขียนโปรแกรมเชิงประกาศ?
troelskn

31
> ดังนั้นการเขียนโปรแกรมปฏิกิริยาเป็นรูปแบบของการเขียนโปรแกรมที่เปิดเผยแล้ว? ฟังก์ชั่นการเขียนโปรแกรมปฏิกิริยาเป็นรูปแบบของการเขียนโปรแกรมการทำงานซึ่งเป็นรูปแบบของการเขียนโปรแกรมที่เปิดเผย
Conal

7
@ user712092 ไม่จริงไม่ ตัวอย่างเช่นถ้าฉันโทรหาsqrt(x)C ด้วยมาโครของคุณนั่นก็แค่คำนวณsqrt(mouse_x())และคืนกลับเป็นสองเท่า ในระบบที่ทำปฏิกิริยาได้จริงsqrt(x)จะคืนค่า "double over time" ใหม่ หากคุณพยายามจำลองระบบ FR โดยที่#defineคุณต้องลบล้างตัวแปรในความโปรดปรานของมาโคร โดยทั่วไปแล้วระบบ FR จะคำนวณสิ่งใหม่เมื่อจำเป็นต้องคำนวณใหม่เท่านั้นในขณะที่ใช้มาโครจะหมายความว่าคุณจะต้องประเมินทุกสิ่งใหม่อย่างต่อเนื่องตลอดไปจนถึงนิพจน์ย่อย
Laurence Gonsalves

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

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

144

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

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


3
ตัวอย่างที่ชัดเจนมาก มันยอดเยี่ยมมากที่มีสิ่งต่าง ๆ ตามทฤษฎีและบางทีบางคนอาจได้รับผลกระทบจากสิ่งนั้นโดยไม่ต้องใช้ตัวอย่างดิน แต่ฉันต้องเริ่มต้นด้วยสิ่งที่ทำเพื่อฉันไม่ใช่สิ่งที่เป็นนามธรรม สิ่งที่ฉันเพิ่งได้รับ (จาก Rx พูดคุยโดย Netflix!) ก็คือ RP (หรือ Rx ต่อไป) ทำให้ชั้น "เปลี่ยนค่า" เหล่านี้และให้คุณเหตุผลเกี่ยวกับพวกเขาหรือเขียนฟังก์ชั่นที่ทำสิ่งกับพวกเขา เขียนฟังก์ชันเพื่อสร้างสเปรดชีตหรือเซลล์หากคุณต้องการ และจะจัดการเมื่อค่าสิ้นสุดลง (หายไป) และช่วยให้คุณล้างข้อมูลโดยอัตโนมัติ
Benjohn

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

131

สำหรับฉันมันมีความหมายแตกต่างกันประมาณ 2 สัญลักษณ์=:

  1. ในวิชาคณิตศาสตร์x = sin(t)หมายถึงว่าxเป็นชื่อที่แตกต่างกันsin(t)สำหรับ ดังนั้นการเขียนเป็นสิ่งเดียวกับx + y sin(t) + yฟังก์ชั่นการตอบสนองการเขียนโปรแกรมเป็นเหมือนคณิตศาสตร์ในแง่นี้: ถ้าคุณเขียนx + yมันจะถูกคำนวณด้วยสิ่งที่มีค่าของtในเวลาที่มันใช้
  2. ในภาษาการเขียนโปรแกรม C-like (ภาษาที่จำเป็น) x = sin(t)คือการมอบหมาย: มันหมายถึงการxเก็บค่าของการ sin(t)ดำเนินการในเวลาที่ได้รับมอบหมาย

5
คำอธิบายที่ดี ฉันคิดว่าคุณสามารถเพิ่ม "เวลา" ในแง่ของ FRP ได้ตามปกติ "การเปลี่ยนแปลงใด ๆ จากอินพุตภายนอก" เมื่อใดก็ตามที่แรงภายนอกเปลี่ยนแปลงอินพุตของ FRP คุณได้เลื่อน "เวลา" ไปข้างหน้าและคำนวณทุกสิ่งอีกครั้งที่ได้รับผลกระทบจากการเปลี่ยนแปลง
Didier A.

4
ในวิชาคณิตศาสตร์x = sin(t)หมายถึงxคือค่าของสำหรับที่กำหนดsin(t) tมันไม่ใช่ชื่อที่แตกต่างกันสำหรับsin(t)ฟังก์ชั่น x(t) = sin(t)มิฉะนั้นมันจะเป็น
Dmitri Zaitsev

+ เครื่องหมาย Dmitri Zaitsev เท่ากับมีความหมายหลายอย่างในวิชาคณิตศาสตร์ หนึ่งในนั้นคือเมื่อใดก็ตามที่คุณเห็นด้านซ้ายคุณสามารถสลับเป็นด้านขวา ยกตัวอย่างเช่นหรือ2 + 3 = 5 a**2 + b**2 = c**2
user712092

71

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

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

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

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

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

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

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

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


1
ตกลงตอนนี้มีคำตอบที่ดีบางข้อ ฉันควรลบโพสต์ของฉัน? หากฉันเห็นคนสองหรือสามคนที่กล่าวว่าไม่เพิ่มอะไรเลยฉันจะลบมันเว้นแต่ว่าจำนวนที่เป็นประโยชน์จะเพิ่มขึ้น ไม่มีจุดที่จะทิ้งไว้ที่นี่เว้นแต่จะเพิ่มสิ่งที่มีคุณค่า
Thomas Kammeyer

3
คุณได้กล่าวถึงการไหลของข้อมูลเพื่อเพิ่มมูลค่า IMHO
Rainer Joswig

นั่นคือสิ่งที่ QML มีความหมายว่าเป็นดูเหมือน;)
mlvljr

3
สำหรับฉันคำตอบนี้ง่ายที่สุดที่จะเข้าใจโดยเฉพาะอย่างยิ่งเนื่องจากการใช้ analogues ธรรมชาติเช่น "ripple through the application" และ "nodes-like nodes" ที่ดี!
Akseli Palén

1
โชคไม่ดีที่ลิงค์ Manchester Dataflow Machine ลิงก์ตาย
Pac0

65

หลังจากอ่านหลาย ๆ หน้าเกี่ยวกับ FRP ในที่สุดฉันก็ได้พบกับงานเขียนที่กระจ่างแจ้งเกี่ยวกับ FRP ในที่สุดมันทำให้ฉันเข้าใจว่า FRP นั้นเกี่ยวกับอะไรจริงๆ

ฉันพูดด้านล่างไฮน์ริช Apfelmus (ผู้เขียนกล้วยปฏิกิริยา)

สาระสำคัญของการเขียนโปรแกรมปฏิกิริยาการทำงานคืออะไร?

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

สาระสำคัญของการเขียนโปรแกรมปฏิกิริยาโต้ตอบการทำงานคือการระบุพฤติกรรมแบบไดนามิกของค่าอย่างสมบูรณ์ในเวลาที่ประกาศ

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

counter := 0                               -- initial value
on buttonUp   = (counter := counter + 1)   -- change it later
on buttonDown = (counter := counter - 1)

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

counter :: Behavior Int
counter = accumulate ($) 0
            (fmap (+1) eventUp
             `union` fmap (subtract 1) eventDown)

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

ดังนั้นในความเข้าใจของฉันโปรแกรม FRP เป็นชุดของสมการ: ป้อนคำอธิบายรูปภาพที่นี่

j ไม่ต่อเนื่อง: 1,2,3,4 ...

fขึ้นอยู่กับtดังนั้นสิ่งนี้รวมความเป็นไปได้ที่จะกระตุ้นภายนอกแบบ

สถานะทั้งหมดของโปรแกรมถูกห่อหุ้มในตัวแปร x_i

ห้องสมุด FRP ดูแลความคืบหน้าเวลาในคำอื่น ๆ การที่จะjj+1

ฉันอธิบายสมการเหล่านี้ในรายละเอียดมากขึ้นในการนี้วิดีโอ

แก้ไข:

ประมาณ 2 ปีหลังจากคำตอบดั้งเดิมเมื่อเร็ว ๆ นี้ฉันได้ข้อสรุปว่าการใช้ FRP มีความสำคัญอีกประการหนึ่ง พวกเขาจำเป็นต้อง (และมักจะทำ) แก้ปัญหาในทางปฏิบัติที่สำคัญ: การทำให้แคชใช้ไม่ได้

สมการสำหรับx_i-s อธิบายกราฟการพึ่งพา เมื่อบางส่วนของx_iการเปลี่ยนแปลงในช่วงเวลาjนั้นไม่ได้ทั้งหมดอื่น ๆx_i'ค่าที่j+1จำเป็นต้องได้รับการปรับปรุงเพื่อให้ไม่ได้ทั้งหมดอ้างอิงจะต้องมีการคำนวณใหม่เพราะบางส่วนอาจจะเป็นอิสระจากx_i'x_i

นอกจากนี้x_i-s ที่เปลี่ยนแปลงสามารถอัปเดตแบบเพิ่มหน่วย ตัวอย่างเช่นลองพิจารณาการดำเนินการแผนที่f=g.map(_+1)ใน Scala ที่fและgมีของList Intsนี่fสอดคล้องกับx_i(t_j)และเป็นg x_j(t_j)ทีนี้ถ้าฉันเติมองค์ประกอบลงไปgมันจะสิ้นเปลืองที่จะดำเนินmapการกับองค์ประกอบgทั้งหมด การใช้งาน FRP บางอย่าง (เช่นreflex-frp ) มีจุดมุ่งหมายเพื่อแก้ไขปัญหานี้ ปัญหานี้เป็นที่รู้จักกันว่าการคำนวณแบบเพิ่มหน่วย

กล่าวอีกนัยหนึ่งคือพฤติกรรม (the x_i-s) ใน FRP สามารถคิดเป็นการคำนวณแคช -ed มันเป็นหน้าที่ของเอ็นจิ้น FRP ที่จะทำให้การแคชและการแคชเหล่านี้ (the x_i-s) มีประสิทธิภาพอีกครั้งหาก -s บางf_iตัวเปลี่ยนไป


4
ฉันอยู่ตรงนั้นกับคุณจนกว่าคุณจะไปกับสมการไม่ต่อเนื่อง แนวคิดการก่อตั้ง FRP เป็นเวลาต่อเนื่องโดยที่ไม่มี " j+1" ให้นึกถึงฟังก์ชั่นของเวลาต่อเนื่องแทน ในฐานะที่เป็นนิวตันไลบนิซและคนอื่น ๆ แสดงให้เราเห็นบ่อยครั้งที่มันมีประโยชน์อย่างมาก (และ "เป็นธรรมชาติ" ในความหมายที่แท้จริง) เพื่ออธิบายฟังก์ชั่นเหล่านี้แตกต่างกัน แต่อย่างต่อเนื่อง มิฉะนั้นคุณกำลังอธิบายอัลกอริทึมการประมาณ (และแย่) แทนสิ่งที่ตัวเอง
Conal

การวางตำแหน่งภาษา HTML เทมเพลตและการ จำกัด เลย์เอาต์ HTMLดูเหมือนจะแสดงองค์ประกอบของ FRP

@ Conal ทำให้ฉันสงสัยว่า FRP แตกต่างจาก ODE อย่างไร พวกเขาต่างกันอย่างไร
jhegedus

@ jhegedus ในการบูรณาการนั้น (อาจจะเรียกซ้ำ, ODEs) ให้หนึ่งในหน่วยการสร้างของ FRP ไม่ใช่ทั้งหมด องค์ประกอบทั้งหมดของคำศัพท์ FRP (รวมถึง แต่ไม่ จำกัด เฉพาะการรวม) ถูกอธิบายอย่างแม่นยำในแง่ของเวลาต่อเนื่อง คำอธิบายนั้นช่วยได้หรือไม่?
Conal

29

กระดาษปฏิกิริยาตอบสนองการทำงานที่มีประสิทธิภาพเพียงแค่โดย Conal Elliott ( PDF โดยตรง 233 KB) เป็นการแนะนำที่ดีพอสมควร ห้องสมุดที่เกี่ยวข้องยังใช้งานได้

ตอนนี้กระดาษถูกแทนที่ด้วยกระดาษอื่นการเขียนโปรแกรมปฏิกิริยาการทำงานแบบกดดึง ( PDF โดยตรง 286 KB)


29

คำเตือน: คำตอบของฉันอยู่ในบริบทของ rx.js - ห้องสมุด 'การเขียนโปรแกรมเชิงโต้ตอบ' สำหรับ Javascript

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

ข้อได้เปรียบที่สำคัญของการใช้สิ่งที่สังเกตได้คือ:
i) มันสรุปสถานะออกจากโค้ดของคุณเช่นหากคุณต้องการให้ตัวจัดการเหตุการณ์ถูกไล่ออกเฉพาะในทุกเหตุการณ์ 'หรือหยุดยิงหลังจากเหตุการณ์' n 'ครั้งแรก หรือเริ่มการยิงหลังจากเหตุการณ์ 'n' ครั้งแรกคุณสามารถใช้ HoFs (ตัวกรอง takeUntil ข้ามตามลำดับ) แทนการตั้งค่าอัปเดตและตรวจสอบเคาน์เตอร์
ii) ปรับปรุงรหัสตำแหน่ง - หากคุณมี 5 ตัวจัดการเหตุการณ์ที่แตกต่างกันเปลี่ยนสถานะขององค์ประกอบคุณสามารถผสานสิ่งที่สังเกตได้และกำหนดตัวจัดการเหตุการณ์เดียวในการรวมที่สังเกตได้แทนรวม 5 ตัวจัดการเหตุการณ์เข้าด้วยกันได้อย่างมีประสิทธิภาพ ง่ายที่จะให้เหตุผลว่าเหตุการณ์ใดในระบบทั้งหมดของคุณที่อาจส่งผลกระทบต่อส่วนประกอบเนื่องจากทั้งหมดอยู่ในตัวจัดการเดียว

  • Observable เป็นสองเท่าของ Iterable

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

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


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

2
"ดังนั้นแนวคิดที่อยู่เบื้องหลัง FRP คือแทนที่จะประมวลผลแต่ละเหตุการณ์สร้างกระแสของเหตุการณ์ (นำไปใช้กับการสังเกต *) และใช้ HoFs กับสิ่งนั้นแทน" ฉันอาจเข้าใจผิด แต่ฉันเชื่อว่านี่ไม่ใช่ FRP จริง ๆ แต่เป็นนามธรรมที่ดีเกี่ยวกับรูปแบบการออกแบบของ Observer ซึ่งอนุญาตให้ใช้งานได้ผ่าน HoF (ซึ่งยอดเยี่ยมมาก!) ในขณะที่ยังคงต้องการใช้กับรหัสที่จำเป็น การสนทนาในหัวข้อ - lambda-the-ultimate.org/node/4982
nqe

18

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

import pygame
from pygame.surface import Surface
from pygame.sprite import Sprite, Group
from pygame.locals import *
from time import time as epoch_delta
from math import sin, pi
from copy import copy

pygame.init()
screen = pygame.display.set_mode((600,400))
pygame.display.set_caption('Functional Reactive System Demo')

class Time:
    def __float__(self):
        return epoch_delta()
time = Time()

class Function:
    def __init__(self, var, func, phase = 0., scale = 1., offset = 0.):
        self.var = var
        self.func = func
        self.phase = phase
        self.scale = scale
        self.offset = offset
    def copy(self):
        return copy(self)
    def __float__(self):
        return self.func(float(self.var) + float(self.phase)) * float(self.scale) + float(self.offset)
    def __int__(self):
        return int(float(self))
    def __add__(self, n):
        result = self.copy()
        result.offset += n
        return result
    def __mul__(self, n):
        result = self.copy()
        result.scale += n
        return result
    def __inv__(self):
        result = self.copy()
        result.scale *= -1.
        return result
    def __abs__(self):
        return Function(self, abs)

def FuncTime(func, phase = 0., scale = 1., offset = 0.):
    global time
    return Function(time, func, phase, scale, offset)

def SinTime(phase = 0., scale = 1., offset = 0.):
    return FuncTime(sin, phase, scale, offset)
sin_time = SinTime()

def CosTime(phase = 0., scale = 1., offset = 0.):
    phase += pi / 2.
    return SinTime(phase, scale, offset)
cos_time = CosTime()

class Circle:
    def __init__(self, x, y, radius):
        self.x = x
        self.y = y
        self.radius = radius
    @property
    def size(self):
        return [self.radius * 2] * 2
circle = Circle(
        x = cos_time * 200 + 250,
        y = abs(sin_time) * 200 + 50,
        radius = 50)

class CircleView(Sprite):
    def __init__(self, model, color = (255, 0, 0)):
        Sprite.__init__(self)
        self.color = color
        self.model = model
        self.image = Surface([model.radius * 2] * 2).convert_alpha()
        self.rect = self.image.get_rect()
        pygame.draw.ellipse(self.image, self.color, self.rect)
    def update(self):
        self.rect[:] = int(self.model.x), int(self.model.y), self.model.radius * 2, self.model.radius * 2
circle_view = CircleView(circle)

sprites = Group(circle_view)
running = True
while running:
    for event in pygame.event.get():
        if event.type == QUIT:
            running = False
        if event.type == KEYDOWN and event.key == K_ESCAPE:
            running = False
    screen.fill((0, 0, 0))
    sprites.update()
    sprites.draw(screen)
    pygame.display.flip()
pygame.quit()

กล่าวโดยย่อ: ถ้าทุกองค์ประกอบสามารถปฏิบัติได้เหมือนตัวเลขระบบทั้งหมดจะถือว่าเป็นสมการทางคณิตศาสตร์ใช่ไหม


1
นี่มันช้าไปหน่อยแล้วล่ะ... Frag เป็นเกมที่ใช้ FRP
arx

14

หนังสือของ Paul Hudak, The Haskell School of Expressionไม่เพียง แต่เป็นการแนะนำที่ดีให้กับ Haskell แต่ยังใช้เวลาพอสมควรใน FRP หากคุณเป็นมือใหม่กับ FRP ฉันขอแนะนำให้คุณเข้าใจว่า FRP ทำงานอย่างไร

นอกจากนี้ยังมีสิ่งที่ดูเหมือนว่าเขียนใหม่ของหนังสือเล่มนี้ (ปล่อยตัว 2011 การปรับปรุง 2014) Haskell โรงเรียนดนตรี


10

ตามคำตอบก่อนหน้านี้ดูเหมือนว่าทางคณิตศาสตร์เราก็คิดในลำดับที่สูงขึ้น แทนที่จะคิดว่าค่าxมีประเภทXเราคิดถึงฟังก์ชันx : TXโดยที่Tคือประเภทของเวลาไม่ว่าจะเป็นจำนวนธรรมชาติจำนวนเต็มหรือค่าต่อเนื่อง ตอนนี้เมื่อเราเขียนy : = x + 1 ในภาษาการเขียนโปรแกรมเราหมายถึงสมการy ( t ) = x ( t ) + 1


9

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

เช่นเดียวกับ "กระบวนทัศน์" ทั้งหมดความแปลกใหม่เป็นที่ถกเถียงกัน

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

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

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

สนุกดี :)


8

ฉันพบวิดีโอที่ดีบน Clojure subreddit เกี่ยวกับ FRP มันค่อนข้างง่ายที่จะเข้าใจแม้ว่าคุณจะไม่รู้จัก Clojure

นี่คือวิดีโอ: http://www.youtube.com/watch?v=nket0K1RXU4

นี่คือแหล่งที่มาที่วิดีโออ้างถึงในช่วงครึ่งหลัง: https://github.com/Cicayda/yolk-examples/blob/master/src/yolk_examples/client/autocomplete.cljs


7

บทความโดย Andre Staltz นี้เป็นคำอธิบายที่ดีที่สุดและชัดเจนที่สุดเท่าที่ฉันเคยเห็นมา

บางคำพูดจากบทความ:

การเขียนโปรแกรมปฏิกิริยาคือการเขียนโปรแกรมด้วยสตรีมข้อมูลแบบอะซิงโครนัส

ยิ่งไปกว่านั้นคุณจะได้รับกล่องเครื่องมืออันน่าทึ่งในการรวมสร้างและกรองกระแสข้อมูลเหล่านั้น

นี่คือตัวอย่างของแผนภาพยอดเยี่ยมที่เป็นส่วนหนึ่งของบทความ:

คลิกแผนภาพสตรีมเหตุการณ์


5

มันเกี่ยวกับการแปลงข้อมูลทางคณิตศาสตร์เมื่อเวลาผ่านไป (หรือไม่สนใจเวลา)

ในรหัสนี้หมายถึงความบริสุทธิ์การทำงานและการเขียนโปรแกรมประกาศ

ข้อบกพร่องของรัฐเป็นปัญหาใหญ่ในกระบวนทัศน์มาตรฐานที่จำเป็น บิตของรหัสต่าง ๆ อาจเปลี่ยนแปลงสถานะที่ใช้ร่วมกันบางอย่างใน "เวลา" ที่แตกต่างกันในการทำงานของโปรแกรม มันยากที่จะจัดการกับ

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

สิ่งนี้จะช่วยลดความซับซ้อนและลดเวลาการดีบักได้อย่างมาก

ลองนึกถึงความแตกต่างระหว่าง A = B + C ในวิชาคณิตศาสตร์และ A = B + C ในโปรแกรม ในวิชาคณิตศาสตร์คุณกำลังอธิบายความสัมพันธ์ที่จะไม่เปลี่ยนแปลง ในโปรแกรมมันบอกว่า "ตอนนี้" A คือ B + C แต่คำสั่งถัดไปอาจเป็น B ++ ซึ่งในกรณี A ไม่เท่ากับ B + C ในการเขียนโปรแกรมคณิตศาสตร์หรือการประกาศ A จะเท่ากับ B + C เสมอไม่ว่าคุณจะขอเวลาใด

ดังนั้นโดยการลบความซับซ้อนของรัฐที่ใช้ร่วมกันและการเปลี่ยนแปลงค่าเมื่อเวลาผ่านไป โปรแกรมของคุณนั้นง่ายกว่าที่จะให้เหตุผล

EventStream เป็น EventStream + ฟังก์ชั่นการแปลงบางส่วน

พฤติกรรมคือ EventStream + ค่าบางอย่างในหน่วยความจำ

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

พฤติกรรมสามารถประกอบขึ้นเพื่อสร้างพฤติกรรมใหม่ที่เป็นการเปลี่ยนแปลงในพฤติกรรมอื่น ๆ ค่าที่ประกอบขึ้นนี้จะคำนวณใหม่เมื่อมีการป้อนเหตุการณ์ (พฤติกรรม) การเริ่มทำงาน

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

อ้างอิงจาก - การเลิกใช้รูปแบบการสังเกตการณ์ http://infoscience.epfl.ch/record/148043/files/DeprecatingObserversTR2010.pdf


นี่คือสิ่งที่ฉันรู้สึกเกี่ยวกับการเขียนโปรแกรมเชิงประกาศและคุณเพียงแค่อธิบายความคิดที่ดีกว่าฉัน
neevek

2

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

[โมดูล / ส่วนประกอบ / วัตถุ] เป็นปฏิกิริยาหมายถึงรับผิดชอบการจัดการสถานะของตนเองโดยการตอบสนองต่อเหตุการณ์ภายนอก

ประโยชน์ของวิธีนี้คืออะไร? มันเป็นสิ่งที่ตรงกันข้ามกับการควบคุมส่วนใหญ่เป็นเพราะ [โมดูล / ส่วนประกอบ / วัตถุ] เป็นผู้รับผิดชอบเองปรับปรุง encapsulation โดยใช้วิธีการส่วนตัวกับคนทั่วไป

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


0

ตรวจสอบ Rx, ส่วนขยายที่มีปฏิกิริยาสำหรับ. NET พวกเขาชี้ให้เห็นว่าด้วย IEnumerable คุณโดยทั่วไปจะ 'ดึง' จากสตรีม คำสั่ง Linq ผ่าน IQueryable / IEnumerable เป็นการตั้งค่าการดำเนินการที่ 'ดูด' ผลลัพธ์ออกจากชุด แต่ด้วยโอเปอเรเตอร์เดียวกันมากกว่า IObservable คุณสามารถเขียนคำสั่ง Linq ที่ 'ตอบสนอง'

ตัวอย่างเช่นคุณสามารถเขียนเคียวรี Linq เช่น (จาก m ใน MyObservableSetOfMouseMovements โดยที่ mX <100 และ mY <100 เลือกจุดใหม่ (mX, mY))

และด้วยส่วนขยาย Rx นั่นคือ: คุณมีรหัส UI ที่ตอบสนองต่อการเคลื่อนไหวของเมาส์ที่เข้ามาและวาดทุกครั้งที่คุณอยู่ในกล่อง 100,100 ...


0

FRP คือการรวมกันของฟังก์ชั่นการเขียนโปรแกรม (กระบวนทัศน์การเขียนโปรแกรมที่สร้างขึ้นบนความคิดของทุกสิ่งที่เป็นฟังก์ชั่น) และกระบวนทัศน์การเขียนโปรแกรมปฏิกิริยา (สร้างขึ้นจากความคิดที่ว่าทุกอย่างเป็นกระแส มันควรจะเป็นสิ่งที่ดีที่สุดในโลก

ลองดูโพสต์ของ Andre Staltz เกี่ยวกับการตั้งโปรแกรมโต้ตอบเพื่อเริ่มต้น

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