The 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.
Even haskell, with its static types and referential transparency, is capable of
emulating this pattern. Here's an example:
-- RussianDoll.hs
-- 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!")
>> 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> :t join
join :: (Monad m) => m (m a) -> 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.
James Clerk Maxwell was a pretty scruffy-looking guy.
I imagine him panhandling in a dingy subway station deep underground.
Work
[ 2010-04-20 13:44:02 UTC ]
I am at work.
I don't remember deciding to come in today.
I wonder how many choices I actually make.
I wonder how a collection of cells can make choices at all.
Perhaps the concept of personal continuity is just a convenient fiction.
An evolutionary artifact devised by my selfish genes
To further their own agenda.
The evidence suggests that I am just a loose connection of divisible parts.
Perhaps it is not so much that I think therefore I am,
But that someone thinks.
Or perhaps something.
Can things think?
Am I someone or something?
Is there a difference?
Perhaps I am just an emergent property of an indifferent universe
Which is expanding into nothingness at an accelerating rate.
Even universes die.
I will die too.
In a sense I have already died countless times.
As the person I am gives way to an imperfect copy.
I am an impostor in my own skin,
Coasting in the wake of my previous self:
The previous self who decided I should come to work today.
But life is much shorter than I had supposed.
The haskell
gd module
provides bindings to a small but useful subset of libgd.
It's a nice enough module and I am grateful that
Björn Bringert
took the time to put it together but it's just not... functional enough
for my tastes. Consider setPixel.
setPixel :: Point -> Color -> Image -> IO ()
This function takes a point, a color, and an image, returning the
unit type from the IO
monad, which means that somewhere inside of Image there must lie a mutable
reference. As good as GHC's garbage collection and algebraic trickery are for
optimizing away many sorts of unused intermediate state,
I can understand the appeal of these in-place updates, especially
considering how much the underlying C library caters to this programming style.
Not all of the actions are performed in-place, however.
rotateImage :: Int -> Image -> IO Image
The rotateImage function creates and returns a new Image type which has been
rotated some integer number of degrees. However, this return type is still
wrapped up in the IO monad and threading this Image type around looks tedious.
Luckily, there is a more functional way to handle both types of updates efficiently
while providing a handy way to automatically thread the state at the same time.
-- State.hs
module Graphics.GD.State where
import Control.Monad.State.Lazy (State(..),modify,execState)
import qualified Graphics.GD as GD
data GDCmd = SetPixel GD.Point GD.Color
data GD' = GD' { gdCmds :: [GDCmd] }
type GD a = State GD' a
Here, the GD type lets us create State monads that operate on GD' data.
The GD' type contains the image and a list of operations to perform on the
image. For simplicity, only SetPixel is defined. With these types, we can write
a helper function consCmd that adds a command to the gdCmds of the state and a
function setPixel that registers a SetPixel action.
consCmd :: GDCmd -> GD ()
consCmd cmd = modify $ \gd -> gd { gdCmds = cmd : (gdCmds gd) }
setPixel :: GD.Point -> GD.Color -> GD ()
setPixel = (consCmd .) . SetPixel
Finally, a newImage function is defined that takes a size and GD () action
and executes the commands in the IO monad using runCmd.
newImage :: GD.Size -> GD () -> IO GD.Image
newImage size f = do
im <- GD.newImage size
-- run each of the commands for the image
mapM_ (flip runCmd $ im) $ gdCmds $ execState f $ GD' []
return im
runCmd :: GDCmd -> GD.Image -> IO ()
runCmd (SetPixel pt c) = GD.setPixel pt c
With just these pieces, it's possible to build something useful!
This program creates a file noise.png filled with random color values.
-- Main.hs
module Main where
import Graphics.GD.State
import qualified Graphics.GD as GD
import System.Random (newStdGen,randomRs)
import Control.Monad (mapM_,liftM2)
main = do
g <- newStdGen
let (w,h) = (400,300)
(GD.savePngFile "noise.png" =<<) . newImage (w,h)
$ mapM_ (uncurry setPixel)
$ zip (liftM2 (,) [0..w-1] [0..h-1]) -- all the pixel coordinates
$ map fromInteger $ randomRs (0,256^3-1) g -- random colors
The code here is just enough to demonstrate a use of the state monad to
create a mini-domain specific language on top of another library.
I forked dancor's haskell-gd
to include a more complete version of this Graphics.GD.State module.
You can check out the code
here.
With the extended module, here's code that
computes a gradient:
import Graphics.GD.State
main = (savePngFile "gradient.png" =<<) . newImage (400,300) $ do
(w,h) <- getSize
eachPixel $ \(x,y) ->
let
r = ((128 * x) `div` w) + ((128 * y) `div` h)
g = 127 + ((128 * x) `div` w)
b = 127
in rgb r g b
And this one draws a
circle and a line:
import Graphics.GD.State
main = (savePngFile "circle.png" =<<) . newImage (400,300) $ do
(w,h) <- getSize
fill $ rgb 100 63 127 -- dark purple background
drawArc
(w `div` 2,h `div` 2) -- centered
(180,180) -- (width,height)
0 360 -- a circle
(rgb 255 255 255) -- white
drawLine (0,0) (w-1,h-1) (rgb 127 255 127)
This might seem like a lot of effort for something that doesn't matter very
much, but writing beautiful code is important.
Clean, abstract interfaces save precious mental horsepower for the bigger,
harder problems.
"Beauty is a consequential thing, a product of solving problems correctly."
-- Joseph Esherick
My Thinkpad's battery life had gotten pathetic. When I first bought my R61i in
April 2008, its 7 cells could endure 4 solid hours of continuous use before
needing to be recharged. But after 22 months of heavy use, it could hold a
charge for about 25 minutes. Lenovo sells replacement batteries for $143, but I
found a seller on Amazon who was selling the same 9-cells listed on Lenovo's
site for $45. My laptop's battery only has 7 cells, but I didn't process this
before ordering.
The battery sat in the mailbox I never check for a week before I thought to
check for it there. Sitting in a mailbox for a week in Fairbanks, Alaska in
January probably wasn't great for the life of the battery, but thankfully
lithium-ion electrolytes won't freeze above -40 C.
After I got the battery, I finally noticed that it didn't fit in my Thinkpad
R61i. Rather than return the battery, I decided it would be less hassle to
figure out how to make this battery work than to return it.
Li-ion batteries are pretty dangerous, as it happens. Consumers should never
open an Li-ion battery's protective case, but I did anyways. Note: this is
very dangerous, and probably a bad idea.
Knowing full well that Li-ion batteries can ignite or explode if mishandled or
punctured, I very carefully opened the new battery I bought online with a pocket
knife to see what was inside.
I first tried to make this new battery fit into my laptop's battery slot by
trimming the plastic, but realized that the locking mechanism and dimensions
couldn't be made to fit easily. Once I got the case apart, I was able to test
that the new battery would work with my laptop by plugging in the short
connector cord, fully expecting both the battery and my laptop to burst into a
spectacular carmine blaze.
However, the new battery worked fine without so much as a beep or an entry in
the system log. At this point I decided to replace my original laptop's innards
with the guts of the new battery.
Getting the original 7-cell battery apart was much more difficult and
time-consuming than the new 9-cell, but I finally succeeded and didn't break too
much plastic in the process.
I was cautiously optimistic that I could just swap out the Li-ion cells
directly, but the original battery had electronics around every cell. Probably
these electronics are to keep the battery from exploding.
It was pretty obvious beforehand that not all the cells would fit in the
original case. After removing the old cells, I removed excess plastic inside
that was getting in the way and cut slots on the back so that 3 cells could just
hang from the back of the case. Note: this is also probably a bad idea, since
puncturing an Li-ion could cause it to ignite, and metal fires are serious
business.
Those warnings aside, I covered everything in electrical tape, including some
stripes to match the art on the back of my display, plugged in the
frankensteinian battery, and it continues to work! At the time of this writing,
it hasn't even exploded yet. I will update this post if/when it does ignite or
explode, supposing I survive the incident. The TSA screeners will probably have
some questions for me if I ever decide to board an airplane with this thing, but
it probably looks just like any other laptop battery on an X-ray scanner.