Skip to content

Instantly share code, notes, and snippets.

@mchav
Created January 24, 2017 17:32
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mchav/dae6b4be69c76f8c42ef11f07d36dcca to your computer and use it in GitHub Desktop.
Save mchav/dae6b4be69c76f8c42ef11f07d36dcca to your computer and use it in GitHub Desktop.
Simple Counter in froid using callbacks.
module io.github.mchav.simplecounter.CounterActivity where
import froid.app.Activity
import froid.os.Bundle
import froid.view.View
import froid.widget.Button
import froid.widget.TextView
native module type Activity where {}
onCreate :: MutableIO Activity -> Maybe (MutableIO Bundle) -> IO ()
onCreate this bundle = do
-- assume we have defined some XML resources as mentioned below
this.setContentView activityCounter
txtOutput <- asTextView this.findViewById textViewOutput
btnIncrement <- asButton this.findViewById buttonIncrement
btnDecrement <- asButton this.findViewById buttonDecrement
-- click listener takes in a view and returns an IO action
btnDecrement.setOnClick (\v -> modifyCounter txtOutput (-1))
btnIncrement.setOnClick (\v -> modifyCounter txtOutput 1)
modifyCounter :: MutableIO TextView -> Int -> IO ()
modifyCounter txt n = do
curr <- liftM (_.toInt) txt.getText
txt.setText (curr + n)
@HeinrichApfelmus
Copy link

There are several ways to incorporate reactive-banana into a GUI framework.

Here is a leightweight way, where the GUI framework doesn't really know anything about reactive-banana. A small glue library, here named "reactive.banana.froid", simplifies using the GUI widgets from the reactive-banana side. The result is very similar to the Counter.hs example from reactive-banana-wx. Here is my attempt at pseudo-code:

module io.github.mchav.simplecounter.CounterActivity where

import froid.app.Activity
import froid.os.Bundle
import froid.view.View
import froid.widget.Button
import froid.widget.TextView

import reactive.banana
import reactive.banana.froid

native module type Activity where {}

onCreate :: MutableIO Activity -> Maybe (MutableIO Bundle) -> IO ()
onCreate this bundle = do
    -- assume we have defined some XML resources as mentioned below
    this.setContentView activityCounter
    txtOutput    <- asTextView this.findViewById textViewOutput
    btnIncrement <- asButton this.findViewById buttonIncrement
    btnDecrement <- asButton this.findViewById buttonDecrement

    let networkDescription :: MomentIO ()
        networkDescription = do
    
        eup   <- event0 btnIncrement click
        edown <- event0 btnDecrement click
    
        (counter :: Behavior Int)
            <- accumB 0 $ unions
                [ (+1)       <$ eup
                , subtract 1 <$ edown
                ]

        reactimate $ txtOutput.setText . show <$> counter

    actuate =<< compile networkDescription    

It is also possible to make the integration more tight. This is what my threepenny-gui library is trying to incorporate. For an example in this style, see the CRUD.hs example. The difference to the previous example is essentially that the GUI library now knows about reactive-banana, and can use Events and Behaviors directly in widget code. In Froid, this would probably look like this:

onCreate :: MutableIO Activity -> IO ()
onCreate this = do
    -- assume we have defined some XML resources as mentioned below
    this.setContentView activityCounter
    txtOutput    <- asTextView this.findViewById textViewOutput
    btnIncrement <- asButton this.findViewById buttonIncrement
    btnDecrement <- asButton this.findViewById buttonDecrement

    (counter :: Behavior Int)
        <- accumB 0 $ unions
            [ (+1)       <$ click btnIncrement
            , subtract 1 <$ click btnDecrement
            ]
    text txtOutput :== show <$> counter

As you can see, the widgets btnIncrement and btnDecrement can now return a click event directly, and the txtOutput widget can accept a string behavior directly.

The more tightly you want to integrate FRP, the more you probably have to rethink your approach to GUI frameworks in general.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment