Reflex exercises: Behaviors

Posted on September 23, 2017

If you haven’t done it already, I’d work through the Event exercise, since this builds on it.

The first change

We’re going to start off with a small change.

We’re going to take the amount of money in the machine as a Behavior and make it part of the Inputs type:

-- src/Ex02/Common.hs
data Inputs t =
  Inputs {
    ibMoney    :: Behavior t Money
  , ieCarrot   :: Event t ()
  , ieCelery   :: Event t ()
  , ieCucumber :: Event t ()
  , ieRefund   :: Event t ()
  }

and we’re going to use a more descriptive error type:

-- src/Ex02/Common.hs
data Error =
    NotEnoughMoney
  deriving (Eq, Ord, Show)

as part of the output:

-- src/Ex02/Common.hs
data Outputs t =
  Outputs {
    oeVend   :: Event t Text
  , oeSpend  :: Event t Money
  , oeChange :: Event t Money
  , oeError  :: Event t Error
  }

Update your solution to the previous exercise so that it takes into account the changes to the inputs and outputs.

You can play with this in a browser by running:

> nix-shell
nix-shell> ./ex02.sh

from the exercises directory and then visit http://localhost:9090 in your browser (although there is currently a bug in jsaddle-warp effecting Firefox).

It should update the browser every time that you save your code while it is in a compilable state.

What it should look like

If you succeed, you should get something that behaves like this:

which will bear an uncanny resemblance to the result of the first exercise.

Hints

You shouldn’t need to use any of the typeclass instances to do this. If you end up using hold then you have gone too far and should turn back.

There is a solution in src/Ex02/Solution.hs, which you should look at when you’re done or if you give up.

The second change

We’re going to switch from having a “Buy” button for each product to a single “Buy” button and a group of radio buttons to handle the selection.

The selection will be given to you as a Behavior t Text containing the name of the selected product:

-- src/Ex03/Common.hs
data Inputs t =
  Inputs {
    ibMoney    :: Behavior t Money
  , ibSelected :: Behavior t Text
  , ieBuy      :: Event t ()
  , ieRefund   :: Event t ()
  }

Update your solution to reflect this change.

You can play with this in a browser by running:

> nix-shell
nix-shell> ./ex03.sh

from the exercises directory.

What it should look like

If you succeed, you should get something that behaves like this:

Hints

If you can get to an Event t Product which fires when the customer hits “Buy”, then the rest of the program should be unchanged from the previous solution.

You have Map from the containers library in your import list. This is a golden opportunity to do things with sequence and with the Applicative-interface helpers.

Remember that fmapMaybe id is very handy.

There is a solution in src/Ex03/Solution.hs, which you should look at when you’re done or if you give up.

The third change

We’re going to start tracking the stock levels in the vending machine.

We introduce a type for this:

-- src/Ex04/Common.hs
data Stock =
  Stock {
    sProduct  :: Product
  , sQuantity :: Int
  } deriving (Eq, Ord, Show)

and we will pass in the stock levels as Behaviors:

-- src/Ex04/Common.hs
data Inputs t =
  Inputs {
    ibMoney    :: Behavior t Money
  , ibCarrot   :: Behavior t Stock
  , ibCelery   :: Behavior t Stock
  , ibCucumber :: Behavior t Stock
  , ibSelected :: Behavior t Text
  , ieBuy      :: Event t ()
  , ieRefund   :: Event t ()
  }

The output is unchanged, but we have a new error condition to detect:

-- src/Ex04/Common.hs
data Error =
    NotEnoughMoney
  | ItemOutOfStock
  deriving (Eq, Ord, Show)

Update your solution to reflect this change.

You can play with this in a browser by running:

> nix-shell
nix-shell> ./ex04.sh

from the exercises directory.

What it should look like

If you succeed, you should get something that behaves like this:

Hints

The biggest challenge here will be getting the inputs into position. It’s similar to the last exercise, but different enough to be fiddly.

Remember to check the handling of the new error condition properly.

There is a solution in src/Ex04/Solution.hs, which you should look at when you’re done or if you give up.

We’re preparing educational materials about the reflex library, and using it to see what exciting things we can do with FRP.

> Dave Laing

Dave is a programmer working at the Queensland Functional Programming Lab.