天道酬勤,学无止境

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!"
  myList <- UI.ul
  on UI.click myButton $ \_ -> element myList #+ [UI.li # set text "Hi"]
  return [myButton, myList]

Now, instead of "Hi", I'd like it to print the natural numbers. I know that I could use the fact that the UI monad is a wrapper around IO, and read/write the number I reached so far in a database, but, for educational purposes, I'd like to know if I can do it using StateT, or otherwise accessing the content of the list via Threepenny-gui interface.

评论

StateT won't work in this case. The problem is that you need the state of your counter to persist between invocations of the button callback. Since the callback (and startGUI as well) produce UI actions, any StateT computation to be ran using them has to be self-contained, so that you can call runStateT and make use of the resulting UI action.

There are two main ways to keep persistent state with Threepenny. The first and most immediate is using an IORef (which is just a mutable variable which lives in IO) to hold the counter state. That results in code much like that written with conventional event-callback GUI libraries.

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]

The second way is switching from the imperative callback interface to the declarative FRP interface provided by Reactive.Threepenny.

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]

Typical usage of Reactive.Threepenny goes like this:

  • First, you get hold of an Event from user input through Graphics.UI.Threepenny.Events (or domEvent, if your chosen event is not covered by that module). Here, the "raw" input event is eClick.
  • Then, you massage event data using Control.Applicative and Reactive.Threepenny combinators. In our example, we forward eClick as eIncrement and eCount, setting different event data in each case.
  • Finally, you make use of the event data, by building either a Behavior (like bCounter) or a callback (by using onEvent) out of it. A behavior is somewhat like a mutable variable, except that changes to it are specified in a principled way by your network of events, and not by arbitrary updates strewn through your code base. An useful function for handling behaviors not shown here is sink function, which allows you to bind an attribute in the DOM to the value of a behavior.

An additional example, plus some more commentary on the two approaches, is provided in this question and Apfelmus' answer to it.


Minutiae: one thing you might be concerned about in the FRP version is whether eCount will get the value in bCounter before or after the update triggered by eIncrement. The answer is that the value will surely be the old one, as intended, because, as mentioned by the Reactive.Threepenny documentation, Behavior updates and callback firing have a notional delay that does not happen with other Event manipulation.

受限制的 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>
  • 自动断行和分段。
  • 网页和电子邮件地址自动转换为链接。

相关推荐
  • 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
  • 在 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
  • 结合 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
  • 是否可以实现`(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
  • 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
  • 结合 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
  • 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
  • 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
  • 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) =>
  • Precise flow control in Haskell
    The Idea Hello! I'm trying to implement in Haskell an image processing library based on dataflow ideology. I've got a problem connected to how I want to handle the flow of control. The main idea is to introduce a time. The time is a Float, which could be accessed anywhere in the code (you can think of it like about State monad, but a little funnier). The funny thing about it, is that we can use timeShift operation on results, affecting the time corresponding operations would see. An example would be best to explain this situation. Lets use following dataflow diagram: -- timeShift(*2) -- -- / \
  • StatET in Eclipse and R
    I've managed to install StatET into eclipse, and I have a Java project called "Test" with some code in there. I'd like to be able to keep a .R file in that project folder and run it as a stand alone R script, seperate from the Java program. How can I do this? If I try to highlight some R code in a .R file and run it, it says "No session of R is active in the current workbench window." Update: I added a run configuration and I now have the following error in a pop up box when I try to run the R script: Launching the R Console was cancelled, because it seems starting the R engine failed. Please
  • 如何设计单子堆栈?(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
  • 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
  • 返回最佳运动中的最小动静算法(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; }
  • Haskell: Overlapping instances
    Consider the following example program: 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 () It can only use get instead of lift get because instance MonadState s m => MonadState s (MaybeT m) is defined in the MaybeT module. Many such instances are defined in kind of a combinatoric explosion manner. It would have been nice (although impossible? why?) if
  • StatET debugging tool
    I think I'm just being dense, but I can't seem to figure out how to use the debugging tool while working in R in Eclipse (StatET plugin). Has anyone got any tips or tutorials on the subject?