Using IORef in threepenny gui

244 Views Asked by At

I'm trying to set an IORef in threepenny-gui but I can't get it to work. In my app the IORef itself will be more complicated and not itself be displayed - but this example demonstrates the problem I think.

Here is my try:

testIORef2 :: IORef String -> Window -> UI ()
testIORef2 ref window = void $ do
    return window # set title "Test IORef"

    inCell <- UI.input
    outCell   <- UI.input

    getBody window #+ [
            column [
                grid [[string " In cell::", element inCell]
                     ,[string "Out cell::"  , element outCell  ]]
            , string "Cells should update while typing."
            ]]

    -- When value changes write to IORef
    on  UI.valueChange inCell $ \_ -> do
        inValue <- get value inCell
        liftIO $ writeIORef ref inValue    

    -- Read the IORef
    refVal <- liftIO $ readIORef ref

    -- Behaviour which holds the string value in the input cell
    inValue <- stepper "0" $ UI.valueChange inCell
    -- Behaviour which holds the value in the ref
    let outValue = (const refVal) <$> inValue

    -- Set the value of the output cell to the outValue
    element outCell # sink value outValue

The code sort of works but the outValue is not quite up to date.

How do I fix it so that the updates are on time. Also, any improvements to the code would be welcome.

Thanks.

2

There are 2 best solutions below

3
Heinrich Apfelmus On BEST ANSWER

The code you wrote is probably not what you intended to do. The line

let outValue = (const refVal) <$> inValue

specifies that outValue is a Behavior whose value is constant and equal to refValue. In turn, the latter value is obtained from

refVal <- liftIO $ readIORef ref

which means that its the value stored by IORef at this point in time in the UI monad.


When using IORef, you want to read the value of the reference when something changes, and use this value to modify the UI content, for instance like this:

on  UI.valueChange inCell $ \_ -> do
    inValue  <- get value inCell
    liftIO $ writeIORef ref inValue
    outValue <- liftIO $ readIORef ref    
    element outCell # set value outValue

For reasons of consistency (order of operations), it is not advisable to use an IORef as a source for a Behavior — it's either the latter or the former.

1
chi On

I'm not an expert on threepenny-gui, but here's my guess.

The code you write outside the on event handler is executed just once. You therefore want to include the outValue update inside said handler, e.g.

-- When value changes write to IORef
on  UI.valueChange inCell $ \_ -> do
    inValue <- get value inCell
    liftIO $ writeIORef ref inValue    
    ... -- compute outValue
    element outCell # sink value outValue