gpt4 book ai didi

haskell - react 香蕉 : how to use values from a remote API and merge them in the event stream

转载 作者:行者123 更新时间:2023-12-02 18:51:02 27 4
gpt4 key购买 nike

我在 WX 界面中使用 Reactive-Banana。当按下按钮时,我需要从外部服务 API 检索值。

我有一个基于数据类型AppState的通用Behavior,它根据函数转换(doSomeTransformation)“累积”转换后的更改。当按下界面上的按钮时,转换的值由事件传输,并且它们来自远程 API (getRemoteValue)。我编写了代表基本部分的精简版本的代码:

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

和 cabal session :

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

我的问题是如何组合 doSomeTransformationmyRemoteValue ,以便我可以将远程 API 值用作正常事件值。Banana-reactive 的更改具有以下签名:

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

它将包装来自 getRemoteApiValue 的我的 IO Int

那么基本上我该怎么做:

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

顺便说一句,我不确定拥有这个不同的函数签名是否更干净:doSomeTransformation::Int -> AppState -> AppState,其中 Int 值由 API 返回值表示。听起来像是两个Behavior和一个流。也许解决问题的方法不好?

最佳答案

简短回答:转换函数需要再接受一个参数,即来自 API 的值:

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

并且您需要使用<$> (即应用功能)而不是 <$ (即用常量值覆盖):

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

长答案:

注意:我已重命名/更改了一些内容,因此请相应地阅读我的解释

需要更改的是使用 accumB 折叠传入值的方式。 。方式accumB工作原理是它应用一系列函数 a -> a为种子值 a ,计算 a 类型的最终值。当前折叠 API 值的方式是始终将应用程序状态计数增量函数应用于初始状态,完全丢弃传入值(通过使用 <$ )。相反,您需要使用<$>映射传入值而不是替换它。 。您需要将值映射到什么?一个函数(根据 accumB 的类型)!该函数是 transformValue 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 <$> bfmap a b 相同,或者在列表的情况下只是 map 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

关于haskell - react 香蕉 : how to use values from a remote API and merge them in the event stream,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32806125/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com