Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Automatically recalculate results

Basically, I want to be able to write some code like the following:

main = do
  x <- newVal (2 :: Int)
  y <- newVal (3 :: Int)
  z <- newFunc (x, y) (\(a,b) -> a * b) 
  r1 <- readVal z
  print r1 -- prints 6 (2 * 3)
  setVal x 5
  r2 <- readVal z
  print r2 -- prints 15 (5 * 3)

Could someone provide some sample code from scratch or from a library that would allow me to achieve something like the above?

like image 469
Clinton Avatar asked Mar 21 '23 07:03

Clinton


2 Answers

This is almost exactly the functionality of STRef and IORef, except as I alluded to in my comment above, you can't get a completely polymorphic newFunc. You have to do something analagous to liftA2.

import Data.IORef

main = do
  x <- newVal (2 :: Int)
  y <- newVal (3 :: Int)
  let z = liftIORef2 (x, y) (\(a,b) -> a * b) 
  r1 <- readVal z
  print r1 -- prints 6 (2 * 3)
  setVal x 5
  r2 <- readVal z
  print r2 -- prints 15 (5 * 3)

liftIORef2 (a, b) f = do
  a' <- readIORef a
  b' <- readIORef b
  return (f (a', b'))

newVal = newIORef

setVal = writeIORef

readVal = id

*Main> main
6
15
like image 102
Tom Ellis Avatar answered Apr 01 '23 04:04

Tom Ellis


One can see your program as an example of incremental computing, and here is one solution that uses the library Adaptive, which can be used to express such incremental computing problems.

import Control.Monad.Adaptive (newMod, readMod, inM, change, propagate, run)

main :: IO ()
main = run $ do
  x <- newMod $ return 2
  y <- newMod $ return 3
  z <- newMod $ do
    a <- readMod x
    b <- readMod y
    return (a * b)
  newMod $ do -- For observing 'z'
    r <- readMod z
    inM $ print r
  -- prints 6 (2 * 3)
  change x 5
  propagate  -- prints 15 (5 * 3)

The Adaptive library is a Haskell implementation by me that closely follows the nice POPL 2002 paper Adaptive Functional Programming by Acar, Blelloch and Harper.

Instead of newVal, one uses newMod, which creates a "modifiable", a computation that keeps track of which other modifiables it depends on when it reads them by readMod. Later, one can change modifiables by change followed by propagate, and all the modifiables that depend on the changed ones automatically gets recomputed in the right order. One can observe what is going on by adding side effects to the computations that define modifiables, and that is how we can see what happens to z.

You can read more about the Haskell implementation in the paper Monads for Incremental Computing that I wrote for ICFP 2002.

like image 35
Magnus Carlsson Avatar answered Apr 01 '23 05:04

Magnus Carlsson