5

WX インターフェイスで Reactive-Banana を使用しています。ボタンが押されたときに、外部サービス API から値を取得する必要があります。

関数変換に基づいて変換された変更を「蓄積」するBehaviorデータ型に基づくジェネリックがあります ( )。変換された値はイベントによって転送され、インターフェイスのボタンが押されたときにリモート API ( ) から取得されます。重要な部分を表すコードのスリムなバージョンを作成しました。AppStatedoSomeTransformationgetRemoteValue

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

そしてカバールのconf:

name:                brg
version:             0.1.0.0
synopsis:            sample frp gui
-- description:
license:             PublicDomain
license-file:        LICENSE
author:              me
maintainer:          me@gmail.com
-- 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

ここでの問題は、リモート API 値を通常のイベント値として使用できるように構成する方法です doSomeTransformation。バナナ反応性から次の署名があります。myRemoteValuechanges

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

IO IntfromをラップしgetRemoteApiValueます。

だから基本的に私はどのように私はから行くことができます:

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

?

ところで、この異なる関数シグネチャを持つ方がきれいかどうかはわかりません: doSomeTransformation :: Int -> AppState -> AppStateInt値は API の戻り値で表されます。Behavior2 つの と 1 つのストリームのように聞こえます。問題を解決するための悪い方法でしょうか?

4

1 に答える 1

2

簡単な答え:変換関数は、API からの値であるもう 1 つの引数を取る必要があります。

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

(つまり、定数値で上書きする<$>)の代わりに(つまり、関数を適用する)使用する必要があります。<$

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

長い答え:

注:いくつかの名前を変更/変更したので、それに応じて私の説明を読んでください

変更する必要があるのは、 を使用して入力値を折り畳む方法ですaccumB。機能する方法accumBは、一連の関数a -> aをシード値aに適用して、 type の最終値を計算することaです。現在 API 値を折りたたんでいる方法は、常にアプリの状態カウント インクリメント関数を初期状態に適用し、着信値を完全に破棄することです ( を使用<$)。代わりに、 を使用して、入力値を置き換えずにマップする必要があります。値を何にマッピングする必要がありますか? 関数 ( の型による)! そしてその機能は.<$>accumBtransformValue eventValue :: AppState -> AppState

リストとフォールドに基づく例:

*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

( 、またはリストの場合とa <$> b同じであることを忘れないでください)fmap a bmap a b

remoteValueB <@ ebtここで、(関数) 定数を使用してイベントを現在どのように「上書き」しているかを考えてみましょう。transformStateこれは、到着したすべての上書きされたイベントが常に同じ内容 (関数) を保持することを意味しtransformStateます。

代わりに、受信値を実際の関数にマップする必要があります。たとえば、古い状態を取り、それを到着した値に結合して新しい状態値を生成する関数などです。

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

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

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

getRemoteApiValue実際の API を模倣するために、変化する値を返すようにも変更しました。したがって、コードにいくつかの変更を加えると、次のように機能します。

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
于 2015-09-28T14:18:34.973 に答える