首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >混合三倍-桂和StateT

混合三倍-桂和StateT
EN

Stack Overflow用户
提问于 2014-06-09 09:57:34
回答 1查看 333关注 0票数 2

我有一个问题,关于三分炮与StateT的相互作用.考虑一下这个玩具程序,每当单击按钮时,在列表中添加一个“嗨”项:

代码语言:javascript
复制
import           Control.Monad
import           Control.Monad.State

import qualified Graphics.UI.Threepenny      as UI
import           Graphics.UI.Threepenny.Core hiding (get)

main :: IO ()
main = startGUI defaultConfig setup

setup :: Window -> UI ()
setup w = void $ do
  return w # set title "Ciao"
  buttonAndList <- mkButtonAndList
  getBody w #+ map element buttonAndList

mkButtonAndList :: UI [Element]
mkButtonAndList = do
  myButton <- UI.button # set text "Click me!"
  myList <- UI.ul
  on UI.click myButton $ \_ -> element myList #+ [UI.li # set text "Hi"]
  return [myButton, myList]

现在,我想用的不是“嗨”,而是打印自然数。我知道,我可以利用UI monad是IO的包装器,并在数据库中读取/写入我到目前为止达到的数字,但出于教育目的,我想知道是否可以使用StateT,或者通过Threepenny-gui接口访问列表的内容。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2014-06-10 02:34:42

在这种情况下,StateT不起作用。问题是,您需要计数器的状态在按钮回调的调用之间持久化。由于回调(以及startGUI )生成UI操作,使用它们运行的任何StateT计算都必须是自包含的,这样您就可以调用runStateT并使用生成的UI操作。

使用Threepenny保持持久状态的主要方法有两种。第一个也是最直接的方法是使用IORef (它只是一个驻留在IO中的可变变量)来保存计数器状态。这就产生了类似于用常规事件-回调GUI库编写的代码。

代码语言:javascript
复制
import           Data.IORef
import           Control.Monad.Trans (liftIO)

-- etc.

mkButtonAndList :: UI [Element]
mkButtonAndList = do
  myButton <- UI.button # set text "Click me!"
  myList <- UI.ul

  counter <- liftIO $ newIORef (0 :: Int) -- Mutable cell initialization.

  on UI.click myButton $ \_ -> do
    count <- liftIO $ readIORef counter -- Reads the current value.
    element myList #+ [UI.li # set text (show count)]
    lift IO $ modifyIORef counter (+1) -- Increments the counter.

  return [myButton, myList]

第二种方法是从命令式回调接口切换到Reactive.Threepenny提供的声明式玻璃钢接口。

代码语言:javascript
复制
mkButtonAndList :: UI [Element]
mkButtonAndList = do
  myButton <- UI.button # set text "Click me!"
  myList <- UI.ul

  let eClick = UI.click myButton  -- Event fired by button clicks.
      eIncrement = (+1) <$ eClick -- The (+1) function is carried as event data.
  bCounter <- accumB 0 eIncrement -- Accumulates the increments into a counter.

  -- A separate event will carry the current value of the counter.
  let eCount = bCounter <@ eClick
  -- Registers a callback.
  onEvent eCount $ \count ->
    element myList #+ [UI.li # set text (show count)]

  return [myButton, myList]

Reactive.Threepenny的典型用法如下:

  • 首先,您通过Graphics.UI.Threepenny.Events (或者domEvent,如果您所选择的事件未被该模块覆盖)从用户输入中获取该事件。在这里,“原始”输入事件是eClick
  • 然后,使用Control.ApplicativeReactive.Threepenny组合器对事件数据进行按摩。在我们的示例中,我们将eClick转发为eIncrementeCount,在每种情况下设置不同的事件数据。
  • 最后,通过从事件数据中构建Behavior (如bCounter)或回调(通过使用onEvent)来利用事件数据。行为有点像一个可变变量,只不过对它的更改是由事件网络以原则的方式指定的,而不是通过散布在代码库中的任意更新来指定的。处理此处未显示的行为的一个有用函数是sink函数,它允许您将DOM中的属性绑定到行为的值。

this question和Apfelmus对这两种方法的答复中提供了另一个例子,再加上对这两种方法的更多评论。

细节:在玻璃钢版本中,你可能关心的一件事是,eCount是否会在eIncrement触发的更新之前或之后在bCounter中得到值。答案是,这个值肯定是原来的值,因为正如Reactive.Threepenny文档所提到的,Behavior更新和回调触发有一个在其他Event操作中不会发生的名义延迟。

票数 5
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/24117788

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档