2011-12-23 23 views
16

Giả sử tôi có sự kiện kích hoạt mà tôi muốn thực hiện hai việc khi được kích hoạt. Trước tiên, tôi muốn cập nhật giá trị của một số hành vi . Thứ hai, nếu các điều kiện khác được đáp ứng, tôi muốn điều kiện đó kích hoạt một sự kiện khác send_off với giá trị được cập nhật của hành vi. Được thể hiện dưới dạng mã, giả sử tôi cóphản ứng chuối: Sự kiện kích hoạt có chứa giá trị cập nhật nhất của Hành vi

trigger :: Event b 
trigger = ... 

updateFromTrigger :: b -> (a -> a) 
updateFromTrigger = ... 

conditionFromTrigger :: b -> Bool 
conditionFromTrigger = ... 

behavior :: Behavior a 
behavior = accumB initial_value (updateFromTrigger <$> trigger) 

send_off :: Event a 
send_off = ?????? (filterE conditionFromTrigger trigger) 

Sau đó, câu hỏi đặt ra là: tôi đặt gì vào ?????? để send_off gửi giá trị cập nhật nhất của hành vi, theo đó tôi có nghĩa là giá trị bao gồm cập nhật từ kích hoạt chỉ được áp dụng cho nó. Thật không may, nếu tôi hiểu chính xác, ngữ nghĩa của Hành vi là như vậy mà giá trị cập nhật không có sẵn ngay lập tức cho tôi, do đó, lựa chọn duy nhất của tôi ở đây về bản chất là trùng lặp công việc và tính toán lại giá trị cập nhật của hành vi để tôi có thể sử dụng nó ngay lập tức trong một sự kiện khác, tức là điền vào ?????? với một cái gì đó giống như

send_off = 
    flip updateFromTrigger 
    <$> 
    behavior 
    <@> 
    filterE conditionFromTrigger trigger 

Bây giờ, có một cảm giác mà tôi thể làm cho thông tin được cập nhật trong hành vi có sẵn cho tôi ngay lập tức bằng cách sử dụng một rời rạc thay vì một hành vi, nhưng thực sự đây chỉ là tương đương với cho tôi một sự kiện được kích hoạt đồng thời với sự kiện ban đầu của tôi với giá trị cập nhật, và trừ khi tôi đã bỏ lỡ thứ gì đó phản ứng-chuối không cho tôi một cách để kích hoạt một sự kiện chỉ khi hai sự kiện khác đã bắn đồng thời; có nghĩa là, nó cung cấp các đoàn thể của sự kiện chứ không phải giao lộ.

Vì vậy, tôi có hai câu hỏi. Đầu tiên, là sự hiểu biết của tôi về tình trạng này đúng, và đặc biệt là tôi đúng trong kết luận rằng giải pháp của tôi ở trên là cách duy nhất để làm việc xung quanh nó? Thứ hai, hoàn toàn không tò mò, đã có bất kỳ suy nghĩ hoặc kế hoạch nào của các nhà phát triển về cách đối phó với các giao điểm của các sự kiện?

Trả lời

6

Câu hỏi hay!

Thật không may, tôi nghĩ rằng có vấn đề cơ bản ở đây không có giải pháp dễ dàng. Vấn đề là như sau: bạn mong muốn giá trị tích luỹ gần đây nhất, nhưng trigger có thể chứa đồng thời sự kiện xảy ra (vẫn được đặt hàng). Sau đó,

Cập nhật tích lũy đồng thời nào sẽ là bản cập nhật mới nhất?

Vấn đề là các bản cập nhật được sắp xếp trong luồng sự kiện mà chúng thuộc về, nhưng không liên quan đến các luồng sự kiện khác. Các ngữ nghĩa FRP được sử dụng ở đây không còn biết cập nhật đồng thời cho behavior tương ứng với sự kiện send_off đồng thời nào. Cụ thể, điều này cho thấy rằng triển khai được đề xuất của bạn cho send_off có thể không chính xác; nó không hoạt động khi trigger chứa các sự kiện đồng thời vì hành vi có thể được cập nhật nhiều lần, nhưng bạn chỉ tính toán lại bản cập nhật một lần.

Với điều này trong tâm trí, tôi có thể nghĩ ra nhiều cách tiếp cận với các vấn đề:

  1. Sử dụng mapAccum chú thích mỗi sự kiện kích hoạt với giá trị ắc mới được cập nhật.

    (trigger', behavior) = mapAccum initial_value $ f <$> trigger 
        where 
        f x acc = (x, updateFromTrigger acc) 
    
    send_off = fmap snd . filterE (conditionFromTrigger . fst) $ trigger' 
    

    Tôi nghĩ rằng giải pháp này thiếu một chút về tính mô đun, nhưng trong ánh sáng của cuộc thảo luận ở trên, điều này có lẽ khó tránh.

  2. Tồn lại mọi thứ theo số Discrete.

    Tôi không có đề xuất cụ thể nào ở đây, nhưng có thể sự kiện send_off của bạn cảm thấy giống như bản cập nhật cho giá trị hơn là sự kiện thích hợp. Trong trường hợp đó, nó có thể có giá trị để đúc tất cả mọi thứ trong điều khoản của Discrete, có Applicative dụ làm "điều đúng" khi các sự kiện đồng thời xảy ra.

    Trong tinh thần tương tự, tôi thường sử dụng changes . accumD thay vì accumE vì cảm giác tự nhiên hơn.

  3. Phiên bản tiếp theo của phản ứng-chuối (> 0.4.3) có thể sẽ bao gồm các chức năng

    collect :: Event a -> Event [a] 
    spread :: Event [a] -> Event a 
    

    mà cụ thể hóa, resp. phản ánh các sự kiện đồng thời. Tuy nhiên, tôi cần chúng để tối ưu hóa Discrete nhưng chúng có thể hữu ích cho các nội dung như câu hỏi hiện tại.

    Đặc biệt, họ sẽ cho phép bạn xác định giao điểm của các sự kiện thusly:

    intersect :: Event a -> Event b -> Event (a,b) 
    intersect e1 e2 
         = spread . fmap f . collect 
         $ (Left <$> e1) `union` (Right <$> e2) 
        where 
        f xs = zipWith (\(Left x) (Right y) -> (x,y)) left right 
         where (left, right) = span isLeft xs 
    

    Tuy nhiên, trong bối cảnh các cuộc thảo luận ở trên, chức năng này có thể ít hữu ích hơn bạn muốn nó được. Đặc biệt, nó không phải là duy nhất, có nhiều biến thể.

+0

Bất kỳ cảm giác nào khi chúng tôi có thể mong đợi cập nhật? :-) –

+0

@GregoryCrosswhite: Bạn tiếp tục đặt câu hỏi hay. ;-) Tôi đã nghĩ ra một thiết kế đẹp cho những người mới, nhưng việc thực hiện nó sẽ mất một thời gian. –

Các vấn đề liên quan