2015-09-27 11 views
5

Tôi đang sử dụng phản ứng chuối trong giao diện WX. Tôi cần truy lục giá trị từ API dịch vụ bên ngoài khi nhấn nút.Banana phản ứng: cách sử dụng các giá trị từ một API từ xa và hợp nhất chúng trong luồng sự kiện

Tôi có chung Behavior dựa trên loại dữ liệu AppState rằng “tích lũy” các thay đổi được chuyển đổi dựa trên phép chuyển đổi chức năng (doSomeTransformation). Các giá trị được chuyển đổi được vận chuyển bởi các sự kiện và chúng đến từ một API từ xa (getRemoteValue) khi nhấn một nút trên giao diện. Tôi đã viết một phiên bản mỏng của mã đại diện cho phần thiết yếu:

module Main where 

{-# LANGUAGE ScopedTypeVariables #-} -- allows "forall t. Moment t" 

import Graphics.UI.WX hiding (Event) 
import Reactive.Banana 
import Reactive.Banana.WX 

{----------------------------------------------------------------------------- 
    Main 
------------------------------------------------------------------------------} 
data AppState = AppState { 
    count :: Int 
} deriving (Show) 

type String = [Char] 

main :: IO() 
main = start $ do 
    f  <- frame [text := "AppState"] 
    myButton <- button f [text := "Go"] 
    output <- staticText f [] 

    set f [layout := margin 10 $ 
      column 5 [widget myButton, widget output]] 

    let networkDescription :: forall t. Frameworks t => Moment t() 
     networkDescription = do 

     ebt <- event0 myButton command 

     remoteValueB <- fromPoll getRemoteApiValue 
     myRemoteValue <- changes remoteValueB 

     let    
      doSomeTransformation :: AppState -> AppState 
      doSomeTransformation ast = ast { count = count ast } 

      coreOfTheApp :: Behavior t AppState 
      coreOfTheApp = accumB initialState $ (doSomeTransformation to combine with myRemoteValue) <$ ebt 

     sink output [text :== show <$> coreOfTheApp] 

    network <- compile networkDescription  
    actuate network 

getRemoteApiValue :: IO Int 
getRemoteApiValue = return 5 

và conf cabal:

name:    brg 
version:    0.1.0.0 
synopsis:   sample frp gui 
-- description: 
license:    PublicDomain 
license-file:  LICENSE 
author:    me 
maintainer:   [email protected] 
-- copyright: 
category:   fun 
build-type:   Simple 
-- extra-source-files: 
cabal-version:  >=1.10 

executable bgr 
    main-is:    Main.hs 
    -- other-modules: 
    -- other-extensions: 
    build-depends:  base >=4.7 && <4.8 
         , text 
         , wx ==0.92.0.0 
         , wxcore ==0.92.0.0 
         , transformers-base 
         , reactive-banana >=0.9 && <0.10 
         , reactive-banana-wx ==0.9.0.2 
    hs-source-dirs:  src 
    default-language: Haskell2010 
    ghc-options:   -Wall -O2 

Vấn đề của tôi ở đây là làm thế nào để soạn doSomeTransformationmyRemoteValue trong một cách mà tôi có thể sử dụng giá trị API từ xa làm giá trị sự kiện bình thường. changes từ chuối phản ứng có chữ ký sau đây:

changes :: Frameworks t => Behavior t a -> Moment t (Event t (Future a)) 

mà nó sẽ quấn tôi IO Int từ getRemoteApiValue.

Vì vậy, về cơ bản làm thế nào tôi có thể đi từ:

IO Int -> Moment t (Event t (Future AppState)) -> AppState 

?

BTW Tôi không chắc liệu nó có sạch hơn không với chữ ký hàm khác này: doSomeTransformation :: Int -> AppState -> AppState, trong đó giá trị Int được biểu thị bằng giá trị trả về API. Có vẻ như hai số Behavior s và một luồng. Có lẽ một cách xấu để giải quyết vấn đề?

+0

làm thế nào về việc bạn đã cung cấp bản sao của sự cố? một dự án hoàn thành FP có lẽ, hoặc ít nhất là một mã nguồn đầy đủ. –

+0

Một trong những điều tôi đang cố gắng hiểu về Haskell và fp nói chung là nếu nó đôi khi đủ cho các chữ ký hàm để mô tả vấn đề. Dù sao tôi sẽ cố gắng viết một cái gì đó trong fpcomplete. – Randomize

+1

Đủ để mô tả vấn đề không giống như đủ để tái tạo và chơi với sự cố trong hộp cát. –

Trả lời

2

Câu trả lời ngắn: chức năng chuyển đổi cần phải mất thêm một đối số, giá trị từ API:

transformState v (AppState x) = AppState $ x + v 

và bạn cần phải sử dụng <$> (tức là áp dụng chức năng) thay vì <$ (tức ghi đè lên với giá trị không đổi):

accumB (AppState 0) $ transformState <$> remoteValueB <@ ebt 

Long trả lời:

Lưu ý: Tôi đã đổi tên/thay đổi một vài điều vì vậy hãy đọc lời giải thích của tôi phù hợp

gì cần phải thay đổi là cách bạn lần so với giá trị sử dụng đến accumB. Cách hoạt động accumB là nó áp dụng một chuỗi các hàm a -> a giá trị hạt giống a, để tính toán giá trị cuối cùng của loại a. Cách bạn hiện đang xếp chồng lên các giá trị API là luôn áp dụng hàm gia tăng số trạng thái ứng dụng cho trạng thái ban đầu, hoàn toàn vứt bỏ giá trị đến (bằng cách sử dụng <$). Thay vào đó, bạn cần phải bản đồ giá trị đến không phải là thay thế nó bằng cách sử dụng <$>.Bạn cần gì để ánh xạ giá trị? Một hàm (theo loại accumB)! Và chức năng đó là transformValue eventValue :: AppState -> AppState.

Một danh sách và nếp gấp dựa dụ:

*Frp> data State = State Int deriving Show 
*Frp> let transform x (State c) = State $ x + c 
*Frp> let xs = [1, 2, 3, 4, 5]      -- the API values 
*Frp> let xsE = transform <$> xs :: [State -> State] -- the event stream 
*Frp> let accumB = foldr ($) 
*Frp> accumB (State 0) xsE 
State 15 

(đừng quên rằng a <$> b cũng giống như fmap a b, hoặc chỉ map a b trong trường hợp danh sách)

Bây giờ xem xét cách mà bạn đang xem "ghi đè" bất kỳ sự kiện nào từ remoteValueB <@ ebt với hằng số (function) transformState, có nghĩa là tất cả các sự kiện bị ghi đè luôn đến cùng nội dung: hàm transformState.

Thay vào đó, những gì bạn muốn là để lập bản đồ giá trị đến với một số chức năng thực tế, ví dụ một trong đó có tình trạng cũ và kết hợp nó với giá trị đến và mang lại một giá trị trạng thái mới:

remoteValueE :: Event t Int 
remoteValueE = remoteValueB <@ ebt 

transformsE :: Event t (AppState -> AppState) 
transformsE = transformState <$> remoteValueE 

coreOfTheApp :: Behavior t AppState 
coreOfTheApp = accumB initialState $ transformsE 

tôi cũng đã thay đổi getRemoteApiValue để trả lại giá trị thay đổi để bắt chước một API thực. Vì vậy, với một số sửa đổi đối với mã của bạn, đây là một số thay đổi đối với mã của bạn, đây là một số hoạt động:

import System.Random 

type RemoteValue = Int 

-- generate a random value within [0, 10) 
getRemoteApiValue :: IO RemoteValue 
getRemoteApiValue = (`mod` 10) <$> randomIO 

data AppState = AppState { count :: Int } deriving Show 

transformState :: RemoteValue -> AppState -> AppState 
transformState v (AppState x) = AppState $ x + v 

main :: IO() 
main = start $ do 
    f  <- frame [text := "AppState"] 
    myButton <- button f [text := "Go"] 
    output <- staticText f [] 

    set f [layout := minsize (sz 300 200) 
        $ margin 10 
        $ column 5 [widget myButton, widget output]] 

    let networkDescription :: forall t. Frameworks t => Moment t() 
     networkDescription = do  
      ebt <- event0 myButton command 

      remoteValueB <- fromPoll getRemoteApiValue 
      myRemoteValue <- changes remoteValueB 

      let 
      events = transformState <$> remoteValueB <@ ebt 

      coreOfTheApp :: Behavior t AppState 
      coreOfTheApp = accumB (AppState 0) events 

      sink output [text :== show <$> coreOfTheApp] 

    network <- compile networkDescription  
    actuate network 
+1

wow giải thích rất rõ ràng-tiếc là tôi có thể bỏ phiếu chỉ một lần :) – Randomize

+0

không có probs; đó là một bài tập tốt cho tôi! –

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