Search code examples
haskellthreepenny-guiioref

Using IORef in threepenny gui


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.


Solution

  • 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.