天道酬勤,学无止境

混合 Threepenny-Gui 和 StateT(Mixing Threepenny-Gui and StateT)

问题

我有一个关于 Threepenny-Gui 与 StateT 交互的问题。 考虑这个玩具程序,每次单击按钮时,都会在列表中添加一个“Hi”项:

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 接口以其他方式访问列表的内容。

回答1

StateT在这种情况下不起作用。 问题是您需要计数器的状态在按钮回调的调用之间保持不变。 由于回调(以及startGUI )产生UI动作, StateT使用它们运行的​​任何StateT计算都必须是独立的,以便您可以调用runStateT并使用生成的UI动作。

使用 Threepenny 有两种主要方法可以保持持久状态。 第一个也是最直接的方法是使用 IORef(它只是一个位于IO的可变变量)来保存计数器状态。 这导致代码与使用传统事件回调 GUI 库编写的代码非常相似。

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 提供的声明式 FRP 接口。

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

在这个问题和 Apfelmus 的回答中提供了一个额外的例子,以及对这两种方法的更多评论。


细节:在 FRP 版本中您可能会担心的一件事是eCount是否会在eIncrement触发的更新之前或之后获取bCounter的值。 答案是该值肯定会是旧的,正如预期的那样,因为正如Reactive.Threepenny文档中提到的那样, Behavior更新和回调触发具有其他Event操作不会发生的概念延迟。

受限制的 HTML

  • 允许的HTML标签:<a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <h5 id> <h6 id>
  • 自动断行和分段。
  • 网页和电子邮件地址自动转换为链接。

相关推荐
  • Mixing Threepenny-Gui and StateT
    I have a question on the interaction of Threepenny-Gui with StateT. Consider this toy program that, every time the button is clicked, adds a "Hi" item in the list: 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!"
  • using threepenny-gui/reactive in client/server programming
    I am trying to figure out how to use Haskell threepenny-gui with its reactive functionality to write a program that lets the user select an item from a listBox send the selection to an external server get back a list of results from the server populate the listBox with the results repeat It seems I will need to use Handler, newEvent and register to do the above. If someone could point me to some existing code that does something like the above that would be great. The closest I have found is GameThing.hs in the threepenny-gui samples directory (but it doesn't use register). UPDATE: I am asking
  • Dynamic Elements based on Behaviour in threepenny-gui
    To put it simple, I am looking for a way to display a Behaviour (UI Element). My actual use-case is displaying a table, which can be filtered. So I have a function tableElement :: String -> UI Element (the String parameter being the filter condition) and an input field filterElement :: Element, which represents the filter. The most natural way for me to combine these would be something like this: bFilter <- stepper "" (valueChange filterElement) displaySomehow (fmap tableElement bFilter) This is also the way it is done in Elm. The closest thing I have found so far is using sink children, but
  • 结合 StateT 和 State monad(Combining StateT and State monads)
    问题 假设我有一个功能 f :: State [Int] Int 和一个功能: g :: StateT [Int] IO Int 我想在g使用f并在它们之间传递状态。 有没有库函数 StateT (return . runState f) ? 或者一般来说,给定一个具有相应 monad 的 monad 转换器,是否有它的库函数? 回答1 更一般地说,您要做的是将转换应用到转换器堆栈的内层。 对于两个任意 monad,类型签名可能如下所示: fmapMT :: (MonadTrans t, Monad m1, Monad m2) => (m1 a -> m2 a) -> t m1 a -> t m2 a 基本上是一个更高级别的fmap 。 事实上,将它与最终参数上的映射结合起来可能更有意义: fmapMT :: (MonadTrans t, Monad m1, Monad m2) => (m1 a -> m2 b) -> t m1 a -> t m2 b 显然,这在所有情况下都不可能,尽管当“源”monad 是Identity它可能会更容易,但我可以想象为它工作的地方定义另一个类型类。 我认为在典型的 monad 转换器库中没有这样的东西; 然而,在 hackage 上的一些浏览在 Monatron 包中发现了一些非常相似的东西: class MonadT t => FMonadT
  • 陷入状态 Monad(Stuck in the State Monad)
    问题 我想使用节点和唯一键的 IntMap 创建图形结构。 这个主题在这里和这里都有很好的介绍。 我理解 state monad 是如何工作的,基本上是将 state -> (val,state) 的函数包装在一个 newtype 中,以便我们可以为它创建一个 monad 实例。 我已经阅读了很多关于该主题的内容。 我似乎仍然无法理解如何在我的程序执行过程中获得唯一(或只是增量)值。 获得一系列连续的 ID 很容易,但是一旦我“运行状态”离开 monad,我似乎又回到了必须跟踪当前 ID 的起点。 我觉得我被困在了单子中。 我考虑的另一个选择是保留整个 IntMap 和当前的“下一个”ID 作为状态,但这似乎非常“必要”和极端。 这个问题非常相似,但没有得到很多答案(或者我可能只是遗漏了一些明显的东西)。 在整个程序执行过程中利用状态 monad 获取唯一 ID 的惯用方法是什么? 谢谢。 回答1 假设我们要IO化State monad。 那会是什么样子? 我们纯粹的State monad 只是一个新类型: s -> (a, s) 好吧, IO版本在返回最终值之前可能会产生一些副作用,如下所示: s -> IO (a, s) 这种模式非常常见,它有一个名字,特别是StateT : newtype StateT s m a = StateT { runStateT :: s -> m
  • 是否可以实现`(Applicative m)=> Applicative(StateT sm)`?(Is it possible to implement `(Applicative m) => Applicative (StateT s m)`?)
    问题 我目前正在分别处理Data.Fresh和Control.Monad.Trans.Fresh 。 定义一个用于生成新鲜变量的接口,以及一个实现该接口的monad转换器。 最初,我认为可以仅针对存在Applicative m要求为FreshT vm实现Applicative实例。 但是,我被卡住了,看来我需要Monad m 。 然后,我不信任我的Haskell-fu,便转向了变形金刚包,并为Control.Monad.Trans.State.Lazy和.Strict内容感到惊讶: instance (Functor m, Monad m) => Applicative (StateT s m) where pure = return (<*>) = ap 所以这是我的问题:是否可以使用以下实例头创建具有等效语义的实例? instance (Applicative m) => Applicative (StateT s m) where 回答1 请考虑您具有两个功能: f :: s -> m (s, a -> b) g :: s -> m (s, a) 您想创建一个函数h = StateT f <*> StateF g h :: s -> m (s, b) 从上面您可以得到一个s您可以将其传递给f这样您就有了: f' :: m (s, a -> b) g :: s -> m (s
  • 如何在 Windows 上设置 Eclipse + StatET + Rcpp(How to set up Eclipse + StatET + Rcpp on Windows)
    问题 当我知道我可以使用 Rcpp 使用 C++ 创建 R 包时,我对此感到兴奋并渴望了解它的开发环境。 多亏了 Fell Stat 博客,我可以使用 Eclipse 和 StatET(它的 R 插件)快速建立一个很好的环境,以便在 Windows 上使用 Rcpp 和 RInside(另一个将 R 嵌入到 C++ 应用程序中的包)。 然而,由于博客是基于 OS X 的,所以有几件事需要反复试验(几乎放弃)才能适应 Windows - 我花了 6 个小时的闲暇时间。 例如,您需要安装 Rtools 才能使用 R CMD 语句和 Miktex 编译 C/C++ 将您的 Rd 文件转换为 pdf 文件。 此外,构建设置与 OS X 不同。 为了让其他人免于我经历过的可能的试错期,我在 Windows 上的 Eclipse + StatET + Rcpp 上制作了这个 wiki。 随意发表评论或其他答案。 否则,请享受您可以获得的这个用于分析软件开发的出色开源环境。 更新(2016 年末) :几年前,我将 R 的 IDE 从 Eclipse 更改为 RStudio,并向使用 R 和 Rcpp 的每个人推荐它。 回答1 对于这个 wiki,我将根据 R 的最新版本 R 2.15.1 继续我的演讲,这是您需要遵循的步骤列表。 如果您已经在 Windows 系统中安装了 R
  • 结合 StateT 和 InputT(combining StateT with InputT)
    问题 这是对这个问题的跟进。 我试图在我的InputT循环中结合来自@ErikR 的答案的shell 。 main :: IO [String] main = do c <- makeCounter execStateT (repl c) [] repl :: Counter -> StateT [String] IO () repl c = lift $ runInputT defaultSettings loop where loop = do minput <- getLineIO $ in_ps1 $ c case minput of Nothing -> lift $ outputStrLn "Goodbye." Just input -> (liftIO $ process c input) >> loop getLineIO :: (MonadException m) => IO String -> InputT m (Maybe String) getLineIO ios = do s <- liftIO ios getInputLine s 并得到一个错误 Main.hs:59:10: Couldn't match type ‘InputT m0’ with ‘IO’ Expected type: StateT [String] IO () Actual type
  • 在 scalaz 中堆叠 StateT(stacking StateT in scalaz)
    问题 我试图通过移植 Dan Piponi 的本教程中的一些示例来理解 Scala 中的 Monad Transformers:http://blog.sigfpe.com/2006/05/grok-haskell-monad-transformers.html 我做了几个简单的: import Control.Monad.State import Control.Monad.Identity test1 = do a <- get modify (+1) b <- get return (a,b) test2 = do a <- get modify (++"1") b <- get return (a,b) go1 = evalState test1 0 go2 = evalState test2 "0" 变成: import scalaz._, Scalaz._ val test1 = for { a <- get[Int] _ <- modify[Int](1+) b <- get } yield (a,b) val test2 = for { a <- get[String] _ <- modify[String](_ + "1") b <- get } yield (a,b) val go1 = test1.eval(0) val go2 = test2.eval("0
  • 如何设计单子堆栈?(How to design a monadic stack?)
    问题 您如何设计和构建单子堆栈? 第一次,我需要构建一个单子堆栈(使用变压器)来解决现实世界中的问题,但是我不确定要以哪种顺序堆叠变压器。 如您所知,只要计算具有种类* -> * ,基本上任何东西都可以在变换器中扮演内部monad的角色,因此有两个问题: 某个特定的变压器是否应该位于堆栈的顶部(例如ReaderT?WriterT?) 是什么驱动设计? 直觉? 类型? (例如,根据您的API的需要调整堆栈的形状) 每个堆栈都是同构的(在一定程度上),还是如果我不正确地构建堆栈,我可能最终会导致无法使用某些底层的monad或产生大量的mess肿的lift . lift . liftIO [...] lift . lift . liftIO [...] lift . lift . liftIO [...] ? 我的直觉表明,如果转换器派生某些实例(例如,MonadReader,MonadIO等,就像mtl大多数转换器一样),则将转换器放置的顺序无关紧要。 我很想听听经验丰富的Haskellers提供的有关最佳做法或经验法则的信息。 forever $ print "Thanks!" 一种。 回答1 这需要经验。 要记住的一件事是,单声道转换器对转换的单声道一无所知,因此外部转换器受内部转换器的行为“束缚”。 所以 StateT s (ListT m) a 首先,由于内部monad
  • How to write a test for StateT using QuickCheck
    The StateT is in Control.Monad.Trans.State.Lazy The function inside and m being higher kinded makes it hard {-# LANGUAGE FlexibleContexts #-} import Test.QuickCheck newtype StateT s m a = StateT { runStateT :: s -> m (a,s) } instance (CoArbitrary s, Arbitrary s, Arbitrary a) => Arbitrary (StateT s (Maybe) a) where -- doesn't quite work arbitrary = undefined The reason I want to do this is because I want to check using QuickCheck if the applicative instance for StateT that I write (for practice) is correct Edit: ok, here is the instance I want to test (supposedly incorrect) instance (Monad m) =
  • Haskell:重叠实例(Haskell: Overlapping instances)
    问题 考虑以下示例程序: next :: Int -> Int next i | 0 == m2 = d2 | otherwise = 3 * i + 1 where (d2, m2) = i `divMod` 2 loopIteration :: MaybeT (StateT Int IO) () loopIteration = do i <- get guard $ i > 1 liftIO $ print i modify next main :: IO () main = do (`runStateT` 31) . runMaybeT . forever $ loopIteration return () 它只能使用get而不是lift get因为在MaybeT模块中定义了instance MonadState sm => MonadState s (MaybeT m) 。 以组合爆炸的方式定义了许多这样的实例。 如果我们具有以下类型类,那会很好(尽管不可能吗?为什么?): {-# LANGUAGE MultiParamTypeClasses #-} class SuperMonad m s where lifts :: m a -> s a 让我们尝试这样定义它: {-# LANGUAGE FlexibleInstances, ... #-} instance
  • stacking StateT in scalaz
    I'm trying to understand Monad Transformers in Scala by porting some examples from this tutorial by Dan Piponi: http://blog.sigfpe.com/2006/05/grok-haskell-monad-transformers.html I did a couple of easy ones: import Control.Monad.State import Control.Monad.Identity test1 = do a <- get modify (+1) b <- get return (a,b) test2 = do a <- get modify (++"1") b <- get return (a,b) go1 = evalState test1 0 go2 = evalState test2 "0" becomes: import scalaz._, Scalaz._ val test1 = for { a <- get[Int] _ <- modify[Int](1+) b <- get } yield (a,b) val test2 = for { a <- get[String] _ <- modify[String](_ + "1"
  • ClassNotFoundException for StatEt Eclipse plugin / RJ. Compatibility issue with Java9?
    I installed RJ for StatEt with: > install.packages(c("rj", "rj.gd"), repos="http://download.walware.de/rj-2.1") Installiere Pakete nach 'C:/Users/eis/Documents/R/win-library/3.4' (da 'lib' nicht spezifiziert) versuche URL 'http://download.walware.de/rj-2.1/bin/windows/contrib/3.4/rj_2.1.0-13.zip' Content type 'application/zip' length 342681 bytes (334 KB) downloaded 334 KB versuche URL 'http://download.walware.de/rj-2.1/bin/windows/contrib/3.4/rj.gd_2.1.0-2.zip' Content type 'application/zip' length 97222 bytes (94 KB) downloaded 94 KB Paket 'rj' erfolgreich ausgepackt und MD5 Summen
  • combining StateT with InputT
    It is a follow-up to this question. I'm trying to combine shell from @ErikR's answer in my InputT loop. main :: IO [String] main = do c <- makeCounter execStateT (repl c) [] repl :: Counter -> StateT [String] IO () repl c = lift $ runInputT defaultSettings loop where loop = do minput <- getLineIO $ in_ps1 $ c case minput of Nothing -> lift $ outputStrLn "Goodbye." Just input -> (liftIO $ process c input) >> loop getLineIO :: (MonadException m) => IO String -> InputT m (Maybe String) getLineIO ios = do s <- liftIO ios getInputLine s And getting an error Main.hs:59:10: Couldn't match type
  • 返回最佳运动中的最小动静算法(Return bestMove in minimax algorithm for tictactoe)
    问题 我已经尝试对Russel Norvig的人工智能书中给出的井字游戏的极小极大算法进行编码。 它拥有一切,除了向用户返回bestMove的方式。 我正在努力返回bestMove,但无法决定何时选择bestMove。 帮忙,有人吗? moveT MiniMax(stateT state) { moveT bestMove; max_move(state,bestMove); return bestMove; } int max_move(stateT state,int & bestMove) { int v = -10000; if(GameIsOver(state)) { return EvaluateStaticPosition(state); } vector<moveT> moveList; GenerateMoveList(state, moveList); int nMoves = moveList.size(); for(int i = 0 ; i < nMoves ; i++) { moveT move = moveList[i]; MakeMove(state, move); int curValue = min_move(state,bestMove); if(curValue > v) { v = curValue; bestMove = move; }
  • install.packages 中的错误:在 R 控制台中工作时,无法使用 StatET 加载 Internet 例程(Error in install.packages: internet routines cannot be loaded using StatET while it works in R console)
    问题 我是 Ubuntu 14.04 上 StatET 的快乐用户,直到最近 R 升级后奇怪的事情开始发生。 与网络操作相关的任何事情都失败了。 例如,我无法再从 CRAN 安装软件包: > install.packages("Hmisc") Installing package into ‘/usr/local/lib/R/site-library’ (as ‘lib’ is unspecified) --- Please select a CRAN mirror for use in this session --- Error in url("http://cran.r-project.org/CRAN_mirrors.csv") : internet routines cannot be loaded In addition: Warning message: In url("http://cran.r-project.org/CRAN_mirrors.csv") : unable to load shared object '/usr/lib/R/modules//internet.so': /usr/lib/R/modules//internet.so: symbol curl_multi_wait, version CURL_OPENSSL_3 not defined
  • Combining StateT and State monads
    Lets say I have a function f :: State [Int] Int and a function: g :: StateT [Int] IO Int I want to use f in g and pass the state between them. Is there a library function for StateT (return . runState f)? Or in general, given a monad transformer with a corresponding monad, is there a library function for it?
  • How to set up Eclipse + StatET + Rcpp on Windows
    When I came to know that I can create a R package with C++ using Rcpp, I was excited about it and eager to know development environment for it. And thanks to Fell Stat Blog, I could quickly establish a great environment using Eclipse with StatET, its plugin for R, to use Rcpp and RInside (another package for embedding R into your C++ application) on Windows. Since the blog was, however, based on OS X, several things required trial & error (& almost give-up) to adjust for Windows - it took me 6 hours of my leisure time. For example, you need to install Rtools to be able to compile C/C++ using R
  • Is it possible to implement `(Applicative m) => Applicative (StateT s m)`?
    I'm currently working on Data.Fresh and Control.Monad.Trans.Fresh, which resp. define an interface for generating fresh variables, and a monad transformer which implements this interface. I initially thought it would be possible to implement the Applicative instance for my FreshT v m with the only requirement that Applicative m exists. However, I got stuck and it seemed like I need to require Monad m. Not trusting my Haskell-fu, I then turned to the transformers package, and was surprised by what I found in Control.Monad.Trans.State.Lazy and .Strict: instance (Functor m, Monad m) =>