Sau khi đọc nhiều trang về FRP, cuối cùng tôi cũng bắt gặp bài viết khai sáng về FRP này, cuối cùng nó cũng khiến tôi hiểu FRP thực sự là gì.
Tôi trích dẫn bên dưới Heinrich Apfelmus (tác giả của chuối phản ứng).
Bản chất của lập trình phản ứng chức năng là gì?
Một câu trả lời chung sẽ là rằng FRP FRP là tất cả về việc mô tả một hệ thống theo các chức năng thay đổi theo thời gian thay vì trạng thái biến đổi, và điều đó chắc chắn sẽ không sai. Đây là quan điểm ngữ nghĩa. Nhưng theo tôi, câu trả lời sâu sắc hơn, thỏa mãn hơn được đưa ra bởi tiêu chí cú pháp thuần túy sau đây:
Bản chất của lập trình phản ứng chức năng là xác định hành vi động của một giá trị hoàn toàn tại thời điểm khai báo.
Chẳng hạn, lấy ví dụ về một bộ đếm: bạn có hai nút được dán nhãn là Up Up và và Down Down có thể được sử dụng để tăng hoặc giảm bộ đếm. Về cơ bản, trước tiên bạn sẽ chỉ định một giá trị ban đầu và sau đó thay đổi nó bất cứ khi nào nhấn nút; đại loại như thế này:
counter := 0 -- initial value
on buttonUp = (counter := counter + 1) -- change it later
on buttonDown = (counter := counter - 1)
Vấn đề là tại thời điểm khai báo, chỉ có giá trị ban đầu cho bộ đếm được chỉ định; hành vi động của bộ đếm được ẩn trong phần còn lại của văn bản chương trình. Ngược lại, lập trình phản ứng chức năng chỉ định toàn bộ hành vi động tại thời điểm khai báo, như sau:
counter :: Behavior Int
counter = accumulate ($) 0
(fmap (+1) eventUp
`union` fmap (subtract 1) eventDown)
Bất cứ khi nào bạn muốn hiểu động lực của bộ đếm, bạn chỉ cần nhìn vào định nghĩa của nó. Mọi thứ có thể xảy ra với nó sẽ xuất hiện ở phía bên tay phải. Điều này trái ngược hoàn toàn với cách tiếp cận bắt buộc trong đó các khai báo tiếp theo có thể thay đổi hành vi động của các giá trị được khai báo trước đó.
Vì vậy, theo tôi hiểu, một chương trình FRP là một bộ phương trình:
j
là rời rạc: 1,2,3,4 ...
f
phụ thuộc vào t
điều này kết hợp khả năng mô hình hóa các kích thích bên ngoài
tất cả trạng thái của chương trình được gói gọn trong các biến x_i
Thư viện FRP sẽ chăm sóc của tiến triển thời gian, nói cách khác, dùng j
để j+1
.
Tôi giải thích các phương trình này chi tiết hơn nhiều trong video này .
BIÊN TẬP:
Khoảng 2 năm sau câu trả lời ban đầu, gần đây tôi đã đi đến kết luận rằng việc triển khai FRP có một khía cạnh quan trọng khác. Họ cần (và thường làm) giải quyết một vấn đề thực tế quan trọng: vô hiệu hóa bộ đệm .
Các phương trình cho x_i
-s mô tả một biểu đồ phụ thuộc. Khi một số x_i
thay đổi tại thời điểm j
sau đó không phải tất cả các x_i'
giá trị khác j+1
cần được cập nhật, do đó, không phải tất cả các phụ thuộc cần phải được tính toán lại vì một số x_i'
có thể độc lập với x_i
.
Hơn nữa, x_i
-s mà thay đổi có thể được cập nhật tăng dần. Ví dụ: hãy xem xét một hoạt động bản đồ f=g.map(_+1)
trong Scala, nơi f
và g
là List
của Ints
. Ở đây f
tương ứng với x_i(t_j)
và g
là x_j(t_j)
. Bây giờ nếu tôi thêm một phần tử vào g
thì sẽ rất lãng phí khi thực hiện map
thao tác cho tất cả các phần tử trong đó g
. Một số triển khai FRP (ví dụ phản xạ-frp ) nhằm giải quyết vấn đề này. Vấn đề này còn được gọi là tính toán gia tăng.
Nói cách khác, các hành vi ( x_i
-s) trong FRP có thể được coi là tính toán bộ đệm. Nhiệm vụ của công cụ FRP là vô hiệu hóa một cách hiệu quả và tính toán lại các bộ đệm (s-s x_i
) này nếu một số f_i
-s thay đổi.