testling - automated browser tests
browserling - interactive browser testing
commit 76a9280156b903ce4974d92a1d3e70cc4f2d84a3
Author: James Halliday
Date: Wed May 12 05:56:18 2010 +0000

The a href="http://javascriptweblog.wordpress.com/2010/04/27/the-russian-doll-principle-re-writing%C2%A0functions%C2%A0at%C2%A0runtime/"Russian Doll Pattern occurs when functions replace themselves. The example in that link uses javascript, but this technique should be applicable to any language with first-class functions and mutable containers.

nesting robots

Even haskell, with its static types and referential transparency, is capable of emulating this pattern. Here's an example:

-- <a href="http://substack.net/scripts/RussianDoll.hs">RussianDoll.hs</a>
-- Russian Doll principle in haskell
module Main where

import Control.Concurrent.MVar
import Control.Monad (join)

main :: IO ()
main = do
    fn <- newEmptyMVar
    putMVar fn $ do
        putStrLn "First!"
        swapMVar fn (putStrLn "Again!")
            &gt;&gt; return ()

    join $ readMVar fn
    join $ readMVar fn
    join $ readMVar fn

Which when executed prints:

First! Again! Again!

An empty MVar is created and bound to fn. MVars are especially convenient for this example since they have an empty state built-in. The first time fn is executed, "First!" is printed, but then the fn MVar is swapped for a new action that prints "Again!".

The inferred type for fn is MVar (IO ()), so join can be used to execute the IO () action inside the MVar.

ghci&gt; :t join
join :: (Monad m) =&gt; m (m a) -&gt; m a

which means

:t join (readMVar (undefined :: (MVar (IO ()))))
join (readMVar (undefined :: (MVar (IO ())))) :: IO ()

Neat!

It's usually a better idea in these cases to use the shared mutable state to delegate to functions instead of storing and swapping the functions themselves. I am however optimistic that this approach could be used in some code to build a more beautiful abstraction.

more
git clone http://substack.net/blog.git