Planet Haskell

February 08, 2010

Neil Brown

Progression version 0.2 released


Version 0.2 of Progression is now available on Hackage. There’s a few minor changes; here’s the relevant entry from the ChangeLog:

Fixed a bug where the argument to the -c and -p options were not split on commas as the documentation described. Also added in settings for: drawing the graph with a logarithmic Y axis, plot size, and the way the benchmarks are sorted on the X axis.

Most changes to Progression are likely to touch the Config types, which will therefore entail a major version number bump (in accordance with the PVP).

by Neil Brown at February 08, 2010 06:51 PM

Haskell Weekly News

Haskell Weekly News: Issue 149 - February , 2010

Haskell Weekly News: February 08, 2010

Welcome to issue 149 of HWN, a newsletter covering developments in the Haskell community.

Hello Haskellers, this week's HWN was delayed a bit in the hopes of making it a bit more substantial. I hate putting up thin HWNs, but of course this must occasionally happen. We have several new CFPs for workshops this week, a new benchmarking package, and some fun quotes. Till next week, Haskellers, your Haskell Weekly News!

Announcements

Call for Papers: Haskell Symposium 2010. Jeremy.Gibbons announced a call for papers for the 2010 ACM SIGPLAN Haskell Symposium 2010 in Baltimore, Maryland; on 30th September.

2nd CfP: LOPSTR 2010. Temur Kutsia announced a second call for papers for the 20th International Symposium on Logic-Based Program Synthesis and Transformation, being held in Hagenberg, Austria, July 23-25, 2010 (co-located with PPDP 2010).

PAR 2010: First CFP. Koen Claessen announced a first call for papers for PAR'10, the Workshop on Partiality and Recursion in Interactive Theorem Provers.

data-ordlist-0.2. Leon Smith announced a new release of ordlist, including a change to the module name and bug fixes.

progression-0.1. Neil Brown announced Progression, a metalibrary which consolidates various existing tools for Haskell optimization (notably Criterion).

HList darcs repo. Oleg He who inhabits all types, but is not _|_, has announced a new darcs repo for HList (and OOHaskell) on community.haskell.org.

Discussion

a beginner question: decorate-op-undecorate. Aran Donohue asked about how to write a function which can 'inspect' inside a datatype.

Translation of Haskell type classes. Enrique Martin talked about some experiments he's done with type classes, and asked some questions regarding optimization related to them.

Category Theory woes. Mark Spezzano asked about resources for learning about category theory.

Blog noise

Haskell news from the blogosphere. Blog posts from people new to the Haskell community are marked with >>>, be sure to welcome them!

Quotes of the Week

  • lispy|web: This curses binding appears to be terminally broken
  • lispy: I did, 'cabal install mage' and it complains about curses
  • lament: Just use fix to find the least funny joke
  • copumpkin: A monad is just a lax functor from a terminal bicategory, duh. fuck that monoid in category of endofunctors shit

About the Haskell Weekly News

New editions are posted to the Haskell mailing list as well as to the Haskell Sequence and Planet Haskell. RSS is also available, and headlines appear on haskell.org.

To help create new editions of this newsletter, please see the information on how to contribute. Send stories to jfredett . at . gmail . dot . com. The darcs repository is available at darcs get http://patch-tag.com/r/jfredett/HWN2/pullrepo HWN2 .

by jfredett at February 08, 2010 05:43 PM

Epilogue for Epigram

Quotients

I’ve had an enquiry for more details on quotients in the new Epigram setup, so I’ll take that as a cue to blog a bit. First, I’d better sketch some basics about propositions and equality. It’s the novel treatment of these things which lets us handle quotients in (hopefully) a neater way than has been possible [...]

by Conor at February 08, 2010 02:02 PM

Tom Schrijvers

Haskell @ AOSD 2010

How to reason about effectful advice? Write your AOP programs in Haskell, the world's best imperative programming language. Use monads and monad transformers for effects and functional mixins for advice. In return you get powerful reasoning tools: equational reasoning and parametricity.

Update: Our paper on the topic has been accepted at AOSD 2010. The technical report has some pretty cool parametricity proofs in its appendix on non-interference of advice, based on Janis' Voigtlaender's ICFP'09 paper "Free Theorems Involving Type Constructor Classes". Slides of my PLSIG seminar at K.U.Leuven are also available.

EffectiveAdvice: Disciplined Advice with Explicit Effects
Bruno Oliveira, Tom Schrijvers and William Cook

Abstract

Advice is a mechanism, widely used in aspect-oriented languages, that allows one program component to augment or modify the behavior of other components. Advice is useful for modularizing concerns, including logging, error handling, and some optimizations, that would otherwise require code to be scattered throughout a system. When advice and other components are composed together they become tightly coupled, sharing both control and data flows. However this creates important problems: modular reasoning about a component becomes very difficult; and two tightly coupled components may interfere with the control and data flows of each other.

This paper presents EffectiveAdvice, a disciplined model of advice, inspired by Aldrich's Open Modules, that has full support for effects in both base components and advice. With EffectiveAdvice, equivalence of advice, as well as base components, can be checked by equational reasoning. The paper describes an implementation of EffectiveAdvice as a Haskell library and shows how to use it to solve well-known programming problems. Advice is modeled by mixin inheritance and effects are modeled by monads. Interference patterns previously identified in the literature are expressed as combinators. Parametricity, together with the combinators, is used to prove two harmless advice theorems. The result is an effective model of advice that supports effects in both advice and base components, and allows these effects to be separated with strong non-interference guarantees, or merged as needed.

by Tom Schrijvers (noreply@blogger.com) at February 08, 2010 09:01 AM

February 07, 2010

Joachim Breitner

If I were a caricaturist

I’d draw a caricature involving a Toyota car, representing capitalism, with a stuck gas petal and Barack Obama trying to fix it. But as I cannot draw very well, especially recognizable people, I created this collage:

The photo of Obama was created by Beth Rankin.

by mail@joachim-breitner.de (nomeata) at February 07, 2010 08:33 PM

Dan Piponi (sigfpe)

The Categorification of the Naturals

A heavyweight looking title, but this post is really about nothing more than doing arithmetic.

Peano Arithmetic
I've seen many articles on type level arithmetic. They all seem to share the idea that the Haskell type system can be made to perform computations by treating types as symbols that can be manipulated according to rules. But every article I have seen seems to miss the important idea that the naturals don't have to simply be empty symbols - that they are perfectly good types with elements and that the basic operations of arithmetic have nice a interpretation as functions between types. Implementing these missing pieces will also give an example of categorification.

As usual, some Haskell administration first because this post is runnable Haskell code:


> {-# LANGUAGE ScopedTypeVariables, UndecidableInstances #-}
> {-# OPTIONS -fglasgow-exts #-}


Here are what are commonly called (some of) the Peano axioms defining addition and multiplication:

1. 0+b = b
2. Sa+b = S(a+b)
3. 0.b = 0
4. Sa.b = b+a.b

The idea is that S represents the "successor" function maping n to n+1. Using just these definitions, and induction, we can define addition and multiplication for all natural numbers. For example, 3 is represented by SSS0 and 2 by SS0 and we can compute 3+2 using

2+3
= SS0+SSS0 by definition
= S(S0+SSS0) by 2
= S(S(0+SSS0)) by 2
= SSSSS0 by 1
= 5 by definition


But where do addition and multiplication come from? One point of view is that the natural numbers are what we get when we take finite sets but consider sets of the same size to be equal. We can do the same with finite types. The type Bool and Maybe () both have two elements (ignoring bottoms) and are isomorpic. We can just consider these to be the same type, called 2. Given two types A and B we can form Either A B. The number of elements in this new type is the sum of the number of elements in A and B. If we blur the distinction between isomorphic types we can think Either as being the addition operator. Similarly, (,) can be thought of as multiplication. The Peano axioms now describe the properties of addition and multiplication defined in this way.

When we consider different types to be equal we lose some information. In particular, we lose that fact that given two types of the same size, we can construct an explicit isomorphism between them. But there's no need to do this. We can go back to the Peano axioms and reinterpret them as a recipe for constructing the isomorphism. If we do this, then any theorem we prove (constructively) using the Peano axioms can be interpreted as explicitly constructing an isomorphism between types. We normally just forget about the isomorphism. This 'forgetting' is so common that it has a name: decategorification. Putting the structure back is called categorification.

Type Level Naturals
We will represent the natural number n as a type with precisely n elements. We'll start with the type representing zero. Obviously it must have no elements. It's traditionally called Void.


> data Void
> instance Show Void where
> show _ = undefined


That undefined will cause no problems as we can never pass an argument into show.

If Void is playing the role of 0 we need something to play the role of S. That's Maybe. Given a type A, Maybe A is the type with one more element. So we can mimic the definitions of the natural numbers:


> type One = Maybe Void
> type Two = Maybe One
> type Three = Maybe Two
> type Four = Maybe Three
> type Five = Maybe Four
> type Six = Maybe Five


and so on. I'll call these the natural number types. We can also label the elements of these types. Here are some elements:


> zero = Nothing
> one = Just zero
> two = Just one
> three = Just two
> four = Just three
> five = Just four


Addition
Now we can define addition. We want to be able to take a pair of natural number types A and B and construct an explicit isomorphism between Either A B and a natural number type which I'll label Plus A B. I'll call the isomorphisms one way plus and the other way plus'. Here's a suitable type class:


> class Plussable a b where
> type Plus a b
> plus :: Either a b -> Plus a b
> plus' :: Plus a b -> Either a b


From axiom 1 we want 0+b=b. This immediately gives:


> instance Plussable Void b where
> type Plus Void b = b

> plus (Right b) = b
> plus' b = Right b


We can view axiom 2, Sa+b = S(a+b), as:



The implementation of plus implements the mapping of the shaded square directly. If we ignore the shaded square and consider only the unshaded ones, then we are left with another simpler addition. We can implement the isomorphism for that by using plus recursively. Here's the code:


> instance Plussable a b => Plussable (Maybe a) b where
> type Plus (Maybe a) b = Maybe (Plus a b)
> plus (Left Nothing) = Nothing
> plus (Left (Just a)) = Just ((plus :: Either a b -> Plus a b) (Left a))
> plus (Right b) = Just ((plus :: Either a b -> Plus a b) (Right b))

> plus' Nothing = Left Nothing
> plus' (Just x) =
> let i' = plus' :: Plus a b -> Either a b
> in case i' x of
> Left a -> Left (Just a)
> Right b -> Right b


Multiplication
Now we can implement multiplication similarly. First the type class:


> class Timesable a b where
> type Times a b
> times :: (a, b) -> Times a b
> times' :: Times a b -> (a, b)


Multiplication by zero gives zero. This is straightforward to implement for the simply reason that we don't actually have to implement isomorphisms for the empty type:


> instance Timesable Void b where
> type Times Void b = Void
> times _ = undefined
> times' _ = undefined


(That's not quite true, Haskell, for some reason, forces us to write a line of code that can never be used. I think this ought to be fixed.)


> instance (Timesable a b, Plussable b (Times a b)) => Timesable (Maybe a) b where
> type Times (Maybe a) b = Plus b (Times a b)

> times (Nothing, b) =
> let i = plus :: Either b (Times a b) -> Plus b (Times a b)
> in i (Left b)

> times (Just a, b) =
> let i = plus :: Either b (Times a b) -> Plus b (Times a b)
> in i (Right (times ((a, b))))

> times' b =
> let i' = plus' :: Plus b (Times a b) -> Either b (Times a b)
> in case i' b of
> Left b -> (Nothing, b)
> Right ab -> let (a, b) = times' ab in (Just a, b)


That's it. We've decategorified type level arithmetic. Given an equality like 2*3=6 we automatically get an isomorphism like

times :: (Two, Three) -> Six


Isomorphisms from Equations
But what about more general equation like 2*2+5 = 3*3? Can we automatically construct the isomorphism?

One approach is simply to reduce each side of the equation to its canonical form, in this case 9, and then use this to construct a pair of isomorphisms, one from the left hand side to the 9 element natural number type, and one from 9 element natural number type to the right hand side. We'll use a type class to indicate that a type can be reduced to canonical form. The map doing the reduction will be called canonical:


> class Canonicable a where
> type Canonical a

> canonical :: a -> Canonical a
> canonical' :: Canonical a -> a


Void is already in canonical form so there's nothing to do in this case:


> instance Canonicable Void where
> type Canonical Void = Void

> canonical = id
> canonical' = id


If something is of type Maybe A, and A is reducible to canonical form, then we can simply reduce Maybe A in two steps:


> instance Canonicable a => Canonicable (Maybe a) where
> type Canonical (Maybe a) = Maybe (Canonical a)

> canonical Nothing = Nothing
> canonical (Just n) = Just (canonical n)
> canonical' Nothing = Nothing
> canonical' (Just n) = Just (canonical' n)


Now I give the rule for reducing Either A B to canonical form. We just have to reduce A and B to canonical form and then apply plus:


> instance (Canonicable m, Canonicable n, Plussable (Canonical m) (Canonical n)) => Canonicable (Either m n) where
> type Canonical (Either m n) = Plus (Canonical m) (Canonical n)
> canonical (Left m) =
> let i = plus :: Either (Canonical m) (Canonical n) -> Plus (Canonical m) (Canonical n)
> in i (Left (canonical m))
> canonical (Right n) =
> let i = plus :: Either (Canonical m) (Canonical n) -> Plus (Canonical m) (Canonical n)
> in i (Right (canonical n))
> canonical' x =
> let i' = plus' :: Plus (Canonical m) (Canonical n) -> Either (Canonical m) (Canonical n)
> in case i' x of
> Left m -> Left (canonical' m)
> Right n -> Right (canonical' n)


Now we need to do the same for multiplication. I'm beginning to feel sorry for the stress we're putting the compiler through:


> instance (Canonicable m, Canonicable n, Timesable (Canonical m) (Canonical n)) => Canonicable (m, n) where
> type Canonical (m, n) = Times (Canonical m) (Canonical n)
> canonical (m, n) =
> let i = times :: (Canonical m, Canonical n) -> Times (Canonical m) (Canonical n)
> in i (canonical m, canonical n)
> canonical' x =
> let i' = times' :: Times (Canonical m) (Canonical n) -> (Canonical m, Canonical n)
> (m, n) = times' x
> in (canonical' m, canonical' n)


Now using the canonical forms we can build the isomorphism for any equation:


> iso :: (Canonical m ~ Canonical n, Canonicable m, Canonicable n) => m -> n
> iso m = canonical' (canonical m)


So let's return to 2*2+5=3*3. The isomorphism should be:


> test = iso :: Either (Two, Two) Five -> (Three, Three)


If we've done our job correctly, the compiler won't complain that it can't build the isomorphism.

If you really want you can try running this code for a few values:


> go1 = Left (zero, one)
> go2 = Left (one, zero)
> go3 = Right four


Try writing code to implement the inverse, checking that it does give the inverse for these three cases.

Conclusions
So there you have it, categorified arithmetic. Of course categorifying the naturals isn't so hard. But what does it mean to categorify the number π? You'll have to read some John Baez to find out more.

There sort of is an application of the operations defined above. The type Three say is the type of indices into a three element type. More generally, these natural number types give indices into fixed length containers and the addition and multiplication operations give type safe ways to map between containers that have the same size. This could be used to pack n-dimensional fixed size arrays into 1-dimensional arrays and vice-versa with compile-time checking of array indices. In practice, however, the compiler would need to be smart enough to realise it could use integers internally rather than the more complex structures it's probably using. But it's curious to see similar operations appear in some OpenCL array manipulation code I've been playing with.

The code above isn't all that pretty. As I've said before: Haskell is two languages. There's the value level language and the type level one. The former is much prettier than the latter, especially if you can use type inference to eliminate the latter.

By the way, you can view iso as a command to trigger the Haskell compiler to prove there is an isomorphism between two types of a certain class. This is very similar to what a tactic in Coq does. In fact, the code I've written above is very similar to what a proof in Coq might look like. The main difference is that Coq gives you a helping hand and can fill in details whereas Haskell forces us to do all of the work ourselves.

An Irrelevant Aside
When I was still at high school a friend returned to visit after a few months at university. He'd been playing with Prolog and showed me how to define Peano arithmetic in that language. Since then, I've sort of been obsessed with squeezing Peano arithmetic out of every computational system that can do it. Hence my C++ code here. I looked him up on the web and it turns out he also wrote the original BSD automounter. Small world.


by sigfpe (noreply@blogger.com) at February 07, 2010 10:24 AM

Roman Cheplyaka

hledger

Since Maria and I moved to Kiev in September 2009 we actively use hledger to track our money.

On average, we record 5 transactions each day — or 35 transactions if you record them at the end of the week. In order for this operation not to consume too much time, transaction entry should be very quick and easy. That's why I made several changes to hledger so that it better fits my workflow.

It took a while before my changes made their way to the main hledger repository. Benefits of some of these patches may be not obvious, so I am going to make a series of blog posts which describe my workflow and show how to efficiently use hledger.

I am going to start with the chart facility. It is useful e.g. when you want to know the structure of your expenses, like: where the most of the money go, where you can save and where saving does not make sense etc.

By the way, I find a lot of criticism of pie charts. But for this purpose I don't see any good alternative.

In order to create ledger charts you have to enable it. E.g. if you use cabal-install, just type "cabal install -fchart" from inside hledger source directory (as of 2010-02-06, hledger with chart facility is not yet released). You will need Chart package installed.

Everything else is trivial. Just type hledger chart ^Expenses to get a fancy pie chart packed in a PNG file. Here's an example:
ledger pie chart

Some tips:

  1. Plotting everything does not make sense since ledger has both positive and negative accounts. Make sure that you plot only accounts with the same sign. «Expenses» accounts are almost always positive.
  2. Usual hledger filtering constructs apply. Some useful examples:
    • hledger chart --depth 2 ^Expenses — plot only top-level subaccounts of Expenses
    • hledger chart -p Dec ^Expenses —make a report for the last December
    • hledger chart ^Expenses not:Rent — ignore Rent account (useful if its balance is too big relative to other expenses)
  3. chart command has some additional options. Use hledger chart -o mychart.png --size 300x500 ^Expenses to specify output file name and dimensions of the image.

by Roman Cheplyaka (noreply@blogger.com) at February 07, 2010 08:13 AM

February 06, 2010

Luke Palmer

Associative Alpha Blending


I recently revamped my graphics-drawingcombinators module to have a handsome denotational semantics. I may write a post going into full detail later, but the executive summary is that Image a = R2 → (Color, a). Due to TCM, I get semantics for Image’s Functor instance from this, and if Color is a monoid I get the semantics for the Applicative and Monoid instances as well. OpenGL combines colors by alpha blending, so I happily defined my monoid instance as alpha blending:

 mempty = Color 0 0 0 0
 mappend c@(Color _ _ _ a) c' = a*c + (1-a)*c'

It doesn’t take a genius to see that this violates two of the three monoid laws (the only one it satisfies is left unit: mempty `mappend` x = x). This is embarrassing. My new rigorous denotational semantics has a pretty awesome flaw.

To make this right, let’s redefine Color not as something which is drawn, but as a transformation on alpha-free colors. I do this because functions make great monoids: composition is always associative and identity is always a unit. But it’s not just any function, it’s an alpha blending function. So we say that f is a “Color” if there exists constants a and x such that f(c) = a x + (1-a) c, which I will write as f = [a; x]. Here a is a scalar and x is another alpha-free color like c. We would really like it if the composition of two Colors were also a Color. Let’s try:

f(g(c)) = [fa;fx]([ga;gx](c))
        = fa fx + (1 - fa) ([ga;gx](c))
        = fa fx + (1 - fa) (ga gx + (1 - ga) c)
        = fa fx + (1 - fa) ga gx + (1 - fa) (1 - ga) c
        = fa fx + (1 - fa) ga gx + (1 - fa - ga + fa ga) c
        = (fa fx + (1 - fa) ga gx) + (1 - (fa + ga - fa ga)) c

It looks like the “alpha” of our composition is (fa + ga – fa ga). Let’s call this a’. Now we have to get the left side of the addition into the form a’ r for some r. Let’s just hack it: multiply and divide1 by a’.

        = a' (fa fx + (1 - fa) ga gx) / a' + (1 - a') c

And then we can read off the answer:

[fa;fx] . [ga;gx] = [a' ; (fa fx + (1 - fa) ga gx) / a']
   where a' = fa + ga - fa ga

For the identity, we need:

a x + (1 - a) c = c

Which is satisfied for a = 0 with x = anything, so we can use [0,0].

Because we derived this from composition and identity, the laws must hold. The mathematically untrusting may wish to check this.

And there you have it: my new Color monoid which actually satisfies the laws. This is the way to compose alpha-blended colors — it reflects what happens if you draw one after the other, blending as you go. This is the math that pre-blends any segment of that sequence.

I should have known that OpenGL’s colors were transformations all along, since the color of an object that you see can depend on the color you used to clear the screen.


1 But what if (fa + ga – fa ga) = 0? Fortunately, the only place this happens when these variables are between 0 and 1 is fa = ga = 0, which means both f and g are the identity color.

by Luke at February 06, 2010 07:02 PM

John Goerzen (CosmicRay)

The Big-Publisher Ebook Scam

There’s been a lot written about the Amazon vs. Macmillan dust-up. I’ve seen a lot of posts by people that work for publishers saying that there are costs to making a book, and that $9.99 just won’t cut it for an ebook. They say that publishers invest in typesetting, editing, selection, art, and various stages of quality control. All of that is true.

Too bad they aren’t doing it with ebooks.

I’ve had some books published, and while the process varies from publisher to publisher, the editing process usually involves technical editors (people that check my facts), copy editors (people that help the writing and grammar), cover designers, and QC staff. Often I will see PDFs or printed pages at the final stage, and at that point can catch things like bad table formatting or lines split at inopportune places. My point here is that there’s a lot of editing going on, and there are many pairs of eyeballs looking at the printed page before it goes to the presses.

In the year or so since I’ve owned my Kindle, I can absolutely guarantee you that this process is not happening with ebooks. Most of the time, it is quite obvious that nobody has even looked at the finished product. Some intern has whipped up a quick conversion from whatever typesetting software they use, give it a quick glance, and call it good. One of my own books, Real World Haskell, is available in Kindle form. O’Reilly took better than average care of that process, but even so, I certainly didn’t approve screenshots before it went out like I did for paper (not that I’d have had time after the paper project was done anyway.) From memory, some of the flaws I’m aware of:

In some of these cases, it is quite obvious that a human didn’t even bother to look at the result. Harper Collins got a huge black eye after their LOTR fiasco, and still took quite a long time to fix it.

Now, if the publishers were actually going to put as much care into the quality of their ebooks as they do into the quality of their paper books, then sure, I’d pay almost the cost of a paperback. But very few of them are doing that. It is quite obvious to me usually by the end of the first chapter of a book whether anybody even looked at the result of their conversion.

Bottom line: If they’re going to sell me an inferior product, don’t expect me to pay near full price. If they can get their act together on quality, only then would they have room to start arguing for higher prices. If all you’re going to do with the ebook is run the paper version through some buggy filter, you haven’t incurred much additional cost, and it is plainly visible to all.

Note: I would like to say that Lonely Planet and O’Reilly have done good jobs with the tools available, and while their results aren’t perfect, they have done a good job working with rendering their sometimes very complex print layouts for a Kindle.

by John Goerzen at February 06, 2010 04:07 AM

February 05, 2010

Bryn Keller

How to Measure Anything

how-to-measure-anything

 

We find no sense in talking about something unless we specify how we measure it; a definition by the method of measuring a quantity is the one sure way of avoiding talking nonsense…
- Sir Hermann Bondi

This book was a great read and completely changed the way I think about measurement. It offers many techniques for taking the data you already have and analyzing them in ways that allow you make better decisions. It also teaches you how to evaluate what kinds of additional information would be most helpful to solving your problem, and even put a dollar value on how much it would be worthwhile to spend get that information in order to make a better decision.

Most importantly, it does an excellent job of challenging the basic assumption that some things just aren’t measurable. After reading this book, I feel like there’s always something I can do to help get the information I need, understand my problem better, and make a better decision. I heartily recommend this book.

by xoltar at February 05, 2010 09:59 PM

Neil Brown

Progression: Supporting Optimisation in Haskell


Progression

I have recently been working on optimising CHP. In order to optimise a program, you need a set of benchmarks/tasks for which you want to improve performance. Then, you need to record how long the original version takes to complete the benchmark, record how long a changed version takes to complete the benchmark and compare the two to see if your change was sufficiently beneficial. This process is tedious, and staring at two columns of numbers is not always instructive, so I constructed a small Haskell library to help with optimisation. I’ve called it Progression, and it is now available on Hackage. Progression allows you to specify some benchmarks, run them, and graph their performance against each other; so what you get is a graph like this:

Each line is a different version of my CHP library, and each group of points is a different benchmark. (The versions were made in the order purple, blue, green, red; I ultimately stuck with the green version.)

Using Progression

Progression uses the excellent Criterion library for doing the benchmarking, so it is used in a similar fashion to Criterion. You construct a small wrapper program that defines/imports your benchmarks and passes them to the Progression library to be run. For example, here is the end of my file containing my CHP benchmarks:

runAll = defaultMain . bgroup "" . map (uncurry bench . second runCHP_)
main = runAll [("SimpleChannel", simpleChannel)
              ,("SimpleChoice 2", choice 2)
              ,("SimpleChoice 5", choice 5)
              ,("SimpleChoice 10", choice 10)
              ]

My runAll function turns the second part of each pair from a CHP () type into IO (), then I map the (Criterion) function bench over the list, use the (Criterion) function bgroup to join them all together, then pass them to the (Progression) function defaultMain. I compile this file into an executable.

When you run the program, you can either pass in settings via the command-line arguments, or if they are not present, you will be prompted with an interactive tab-completing prompt (thanks to the easy-to-use Haskeline library). There are three main settings:

  • which benchmark groups you want to run (if you only have one group, you won’t be prompted),
  • what to label the recording you are about to make, and
  • which previous labels you want to compare against in the graph.

Once you’ve entered these two or three items, the benchmarks will all be run by Criterion, the results stored into a CSV file (using the handy txt-sushi library; this way you can easily manually inspect the data or further process it in a spreadsheet program), which is then combined with the previous stored CSV files and fed to GNUplot. (Unfortunately the gnuplot binding in Haskell is not in great shape at the moment, so I’m using a plain system call to run it — if you want graphs, make sure you have GNUplot installed on your system.) The program creates a graph like the one shown at the beginning of the post — by default in the file plot.png (but again, configurable by command-line option). If, later on, you want to just graph previous results against each other, you can do that by running the program with “-m graph”. On the graph you get points plotted at the means (staggered for each benchmark to aid readability and comparison) and error bars for the bounds that Criterion gives — by default these are the 95% confidence intervals, but that is configurable, too (by passing through an option to Criterion).

Installing Progression

1. Make sure gnuplot is installed on your system and available in your path. Gnuplot is very likely to be in your package manager if you’re on Linux.
2. cabal update && cabal install progression

Note that by default, Criterion uses the Chart library for plotting (a feature of Criterion that Progression does not use), which is in turn dependent on gtk2hs, which can be problematic for some people to install. If you get messages about cairo not being installed and you don’t want to install gtk2hs, you can install Criterion without this support for plotting (and thus without the gtk2hs dependency). The command cabal install criterion -f-Chart should do this (the minus between f and Chart is crucial), but unfortunately it seems after that, that you must install progression manually by downloading it and running Setup.lhs (I had hoped cabal install progression would work but that seems to attempt to satisfy the Chart dependency even though criterion is by then installed).

Sharing Progression

I’ve now uploaded the progression repository onto patch-tag. You can get a copy using darcs get http://patch-tag.com/r/twistedsquare/progression if you want the very latest version or wish to contribute some patches.

Issues with the 0.1 release

I realise that the graph is technically invalid. I shouldn’t connect the points with a line because the X-axis is discrete, not continuous. However, without the line (i.e. with just points and error bars) it’s much less readable at a glance, and a bar chart with error bars didn’t seem too readable either when I tried it. The graph display still isn’t perfect though; it works best when you have benchmarks that take roughly similar times, and if you make one huge saving on one benchmark, as I did (a factor of about 100), this throws off the whole display. Normalising all the times, so that one of the versions has all its times normalised to one, would partially fix the problems. Also, if you run a lot of benchmarks, the CSV files do start to litter the directory; I wonder if I should store them in a subdirectory.

Pipe Dream

That’s the summary of Progression. I hope that Progression helps people who are optimising Haskell programs (especially alongside profiling and tools such as ThreadScope, that can help pinpoint possible candidates for optimisation). But the work-flow is not perfect. Often, not all the benchmarks are written and complete when you start benchmarking; you both make changes and add new benchmarks as you work. Currently, when graphing, Progression ignores any benchmarks for which it does not have data for every recording being graphed (this should perhaps be fixed). Ideally, you would re-run the old version of your library/program (for example, the original version before you began optimising) with the benchmarks to get some data. For this, we need access to an old version. All developers store their projects in version control (and with Haskell, that’s typically darcs) so we could automatically pull out the old version from there rather than making the programmer do it themselves.

So perhaps what we would do is dig through the version history for the latest tag starting “OPT:” (which is the “original” for this current round of optimisation), then re-run that version with the latest benchmarks. In fact, why restrict it to just the original? We could look back to find the last tag with “OPT:”, then work forwards from there, looking for patches, marking out those starting “BENCH:” (the ones that introduce new benchmarks). We would then try and create versions of your program from the OPT tag forwards, with all the different versions since then, but always featuring all the BENCH patches. This would give us complete benchmark data for all old versions, and would also mean you wouldn’t have to tell Progression what the labels are, it could just use the patch names/dates. We could also try different combinations of patches (if you’ve recorded A, B, and C, where B depends on A, try A, A&B, A&B&C, A&C) to see how different independent changes combine together to find the fastest combination.

I’m not sure how much work that would be, but it sounds like it might form quite a useful tool. Or, it might turn out to be too over-the-top and awkward — it might be best to just stick to Progression’s current straightforward design. Comments are welcome below, as are any questions or feedback about the current release of Progression.

by Neil Brown at February 05, 2010 09:46 PM

Kevin Reid (kpreid)

The people who have come to rely on features that are actually implementation errors are called ‘mistakeholders’.
— Chip Morningstar, today's friam

by kpreid@mac.com at February 05, 2010 07:53 PM

February 04, 2010

Joachim Breitner

FontForge-Article in the German Linux-Magazin

Yesterday, I found the 3/10-issue of the German “Linux-Magazin” in my mailbox. (I don’t dare to call it the March issue – they are a bit off schedule...) On page 62, you can find my 3½ page article about creating a symbol font with FontForge. I briefly covered the topic on my blog and later thought that it would made a nice article, even though I’m not an expert on this area. The article will be freely available in about three years.This is already my third publication, after my article on the Cross-Site-Authentication attack that was published in the same magazine (circulation ~63.000) and in its international counterpart in 2005 and my recent article in the “freeX” magazine (circulation ~15.000). Looks like I’ll have to add a  “Publications” section to my website soon...

by mail@joachim-breitner.de (nomeata) at February 04, 2010 09:50 PM

Kevin Reid (kpreid)

“Not an exit”

This is the door by which I exit the dorm every morning:

316 STAIRWAY/NOT AN EXIT/B

This is what Geoffrey K. Pullum of Language Log calls “nerdview”. By what definition is this not an exit? Fire safety! This stairwell does not have an exterior door at ground level, but only leads back into the building (right next to the lobby and main entrance). Therefore it is not an exit.

But for the ordinary mind, this sign is crying wolf. You'd think they could have said “Do not use in case of fire” or “Not an emergency exit”.

by kpreid@mac.com at February 04, 2010 08:28 PM

Lee Pike

“Schrodinger’s Probability” for Error-Checking Codes


In a previous post, I discussed the notion of Schrödinger CRCs, first described by Kevin Driscoll et al. in their paper Byzantine Fault Tolerance, from Theory to Reality. The basic idea is that error-detecting codes do not necessarily prevent two receivers from obtaining messages that are semantically different (i.e., different data) but syntactically valid (i.e., the CRC matches the respective data words received). The upshot is that even with CRCs, you can suffer Byzantine faults, with some probability.

… So what is that probability of a Schrödinger’s CRC? That’s the topic of this post.  I’m trying out these ideas for the first time, so feedback is appreciated.

Background

To begin, recall the idea of a Hamming Weight (HW).  The HW is a function on the set of data words of a fixed length and a fixed number of bit-corruptions that may occur (in either the data word or its associated CRC code).  It returns the number of corruptions that the CRC fails to detect.  For example, for the parity bit on data words of length three, the following is one instance in the HW count for 2-bit corruptions:

           data word  bit
sent       0 1 1      0
corrupted  1 0 1      0

The Hamming Distance (HD) for a fixed data word length and CRC is the smallest number of bit-corruptions resulting in a non-zero Hamming Weight (for the parity bit, it’s two).  Koopan and Chakravarty document the HDs of a number of CRCs.

While the concept of a HD might be appropriate for measuring the resilience of a CRC to random bit errors, it may not be a good metric for calculating the probability of Schrödinger’s CRCs.  Consider the following quotation from Paulitsch et al. in their paper, Coverage and the Use of Cyclic Redundancy Codes in Ultra-Dependable Systems:

…Network inter-stages can exhibit arbitrary faults, accidentally forging valid CRC check sequences. These faults can dominate system dependability issues, resulting in undetected failures at the 10^{-6} component failure rate.

The point here is that hardware that manipulates transmitted data (i.e., the inter-stages) may suffer random failures that cause correlated faults in the transmitted data.  For example, a bus driver may be “stuck at 1/2″ so that it delivers an intermediate voltage that will be interpreted by some receives as a “1″ and by others as a “0″.  Or an inter-stage may have a bad oscillator, causing it suffer a slightly-out-of-spec timing fault, so that different receivers interpret the incoming bits differently.

In data corruption caused by these kinds of hardware failures, we must change our assumptions:

  1. Data transmission faults are random but correlated.  By this I mean that we might presume that all bit errors are “in the same direction”: either 0s become 1s or 1s become 0s.  For example, with a stuck-at-1/2 fault in which a 1 is transmitted at a slightly too-high voltage (assuming 1s are denoted by a low signal), a receiver might interpret the signal arbitrarily, but consistently.
  2. Bit-error rates are much higher.  Paulitsch et al. note that typical assumed bit-error rates range from 10^{-6} to 10^{-13}, but actual rates vary wildly.  However, if there is a hardware failure (e.g., a stuck-at-1/2 fault), a dramatically higher BER (for correlated bit-errors) might be induced.

We define Schrödinger’s Hamming Distance (SHD) for a error-checking code as follows. Suppose in a data word and its associated error-checking code all bit-errors are exclusively of one of the following type:

  1. 0s may randomly be flipped to 1s.
  2. 1s may randomly be flipped to 0s.

Then the Schrödinger Hamming Weight (SHW) of the code for a fixed data word width is the number of undetected errors from (1) or (2).  We’ll refer to a bit corruption that contributes to a non-zero SHW as a Schrödinger CRC. The Schrödinger Hamming Distance is the least number of errors required for a non-zero SHW.

The SHWs for a CRC and data word size are no greater than the respective HWs.  For example, the parity-bit example above would be included in the calculation of the Hamming Weight but would be excluded in the calculation of Schrödinger Hamming Weight (becuase we flipped a 0 to a 1 and a 1 to a zero).  However, the following would be included in both, since we only flip 0s to 1s:

           data word  bit
sent       1 0 1      0
corrupted  1 1 1      1

Clearly, SHWs can never be greater than HWs.  However, I do not know whether for some CRC and data word length, the Schrödinger Hamming Distance is ever strictly greater than the Hamming Distance. (My suspicion is not.)

To calculate the probability of a Schrödinger CRC involves a small analytical analysis together with a some probabilistic simulation.  We’ll describe the simulation first.

Simulations

For a fixed CRC and data word length, we’ll randomly generate data words and then compute their CRCs, within some constraints.  We do not simulate arbitrary bit-flips, since we want our simulation to be as productive as possible—we do not want to have to simulate 10s of millions of times to get reliable statisitical data.

So without loss of generality, we’ll simulate some number of 0s being flipped to 1s (we could just have easily simulated 1s being flipped to 0s) in the data word together with its CRC.  Of course, we can flip no more 0s than appear in the data word and CRC.  Furthermore, it does no good to simulate less than HD bit-errors, since the CRC is guaranteed to protect against any bit-errors up to (but not including) the HD.  So we’ll simulate at least HD bit-errors.  If there are less than HD 0s in the data word and its CRC, then it is counted as a  test run in which the CRC successfully catches errors.

To carry out the simulation, I wrote a little Haskell program.  (By the way, this is not the most beautiful or most efficient code possible, but I can do a million simulations in minutes on a commodity laptop.  Good enough for my purposes.)  The simulation uses QuickCheck.  To execute it, you will need to add a small patch to QuickCheck—see the patch and installation instructions here.

So this gives us some failure rate.  For example, for the USB-5 CRC (x^5 + x^2 + 1) on 11-bit data words (with a  HD of 3), after 10,000 simulations, we get somewhere around 3% of the generated runs producing bit-errors that result in the CRC failing to capture the fault.  For something like the CAN CRC (x^{15} + x^{14} + x^{10} + x^8 + x^7 + x^4 + x^3 + 1) on 64-bit data words (with a HD of 6), after a million simulations, we get a failure rate of around 3.9 \times 10^{-5}.

Calculations

Now for the analytical part.  For an arbitrary data word and its corresponding CRC, we need to calculate the probability of the simulation runs occurring.

We do this as follows.  First, on average, if a data word together with its CRC comprises n bits, we’ll assume that roughly half are 1s and half are 0s.  Thus, the maximum number of 0s that could be flipped is approximately half the total number of bits in the data word and its CRC.

If hd is the Hamming Distance, then we want to sum the probabilities of suffering exactly hd, hd+1, hd+2, …, \frac{n}{2} bit-errors.  The total number of possible bit-errors for each number of corrupted bits is determined with the choose function:

{n \choose i} = \dfrac{n!}{i!(n-i)!}

So, for example, the total number of coin tosses in which exactly two coins show heads out of three tosses is

{3 \choose 2} = \dfrac{3!}{2!(3-2)!} = 3

Now, if the probability of a bit-error is p, then the probability of i bit-errors is

p^i (1-p)^{n-i}

Thus, the probability of any of the bit-errors introduced in our simulation occurring (regardless of whether they led to an undetected corruption), is

\displaystyle\sum^{n/2}_{i=hd}{\frac{n}{2} \choose i} \times p^i (1-p)^{\frac{n}{2} - i}

We multiply this summation by the percentage of undetected corruptions determined from the simulation.  So for USB-5 mentioned above on 11-bit data words, recall that our simulation showed a ~3% of the simulations resulting in a uncaught error.  If the probability p of a single bit corruption is 0.01 (assuming the hardware is faulty), then

\displaystyle\sum^{\frac{16}{2}}_{i=3}{\frac{16}{2} \choose i} \times 0.01^i (1-0.01)^{\frac{16}{2} - i} = 5.39 \times 10^{-5}

5.39 \times 10^{-5} multiplied by 3% gives us 1.617 \times 10^{-6}.  For a single message, assuming a fault-arrival rate for bit corruptions, that’s the probability of a Schrödinger CRC.

Combining the Results

But that number by itself is an incomplete portrayal of the arrival rate of Schrödinger CRCs in a system.  We must also take into account the following to get a system-wide Schrödinger CRC arrival-rate per hour of operation:

  1. The number of inter-stages.
  2. The failure rate of the hardware in the inter-stages (particularly for failures that can lead to slightly-out-of-spec faults, so this includes transient faults).
  3. The number of receivers.
  4. The number of messages per hour.

Specifically, from the hardware failure rate and the number of inter-stages, we’ll calculate the probability of hardware failure, and we’ve already computed the probability of a Schrödinger CRC given a hardware failure.  Then we’ll do a final calculation to get the probability per hour.

For a concrete example, let’s assume there are 10 inter-stages messages pass through, the failure rate of the hardware is 10^{-3} per hour (again, we’re including transient faults, so we’re using a high rate), and there are 20 receivers in the broadcast.  We’ll assume a fairly slow data-rate, of 1 kilobit/second.

Let’s find the probability of a faulty inter-stage first.  If there are 10 inter-stages, and we assume the probability of hardware failures is independent, then the probability of at least one of them being faulty is 1 - ((10^{-3})^0 \times (1 - 10^{-3})^{10}) = 9.96 \times 10^{-3}.

So now we have the probability of a uncaught error per message, assuming faulty hardware (1.617 \times 10^{-6}) and the probability of faulty hardware (9.96 \times 10^{-3}).  Multiplying these, we get that the probability of a Schrödinger CRC per message is 1.61 \times 10^{-8}.  If the throughput is 1kb/s, then that’s 225,000 16-bit messages/hour.  So the probability of a Schrödinger CRC per hour of operation is 1 - ((1.61 \times 10^{-8})^0 \times (1 - 1.61 \times 10^{-8})^{225,000}) = 3.61 \times 10^{-3}.  That’s a high rate, which is not acceptable for an ultra-reliable system that depends on CRCs to protect against Byzantine faults.  Of course, the constants we used in our calculations are system-specific, but I hope this suffices to demonstrate that Schrödinger CRCs should be of concern in reliable system design.

For fun, let’s run the numbers on the CAN CRC using the same constants as above.  Our simulations for CAN on 64-bit data words gives us a probability of a Schrödinger CRC, assuming faulty hardware, of 3.9 \times 10^{-5} \times 7.248 \times 10^{-7} = 2.83 \times 10^{-11}.  This gives a probability of a Schrödinger CRC at 2.82 \times 10^{-13} per message or 1 - (2.82 \times 10^{-13})^56250 = 1.59 \times 10^{-8}, potentially still too probable for ultra-dependable systems with required failure rates of 10^{-9} or lower.

Conclusions

What are the lessons to draw from this?

  1. We’ve described new concepts of Schrödinger Hamming Weights and Schrödinger Hamming Distance
    as another metric for the quality of error-checking codes.
  2. We’ve discussed how one might use a combination of simulation and probability calculations to discover the probability of Schrödinger CRCs in a system (assuming I got all my calculations correct!).
  3. Depending on the architecture and requirements, these calculations could be as important in determining system reliability as simply using the Hamming Distance of a CRC.

Open questions include

  1. How Schrödinger Hamming Weights compare with Hamming Weights and similarly for Schrödinger Hamming Distances and Hamming Distances.  In particular, is the Schrödinger Hamming Distance ever strictly greater than the Hamming Distance?
  2. Are any CRCs well-suited to protect against Schrödinger CRCs?
  3. Under what constraints should Schrödinger HWs be the primary reliability measure in system design?  Should error-detecting codes ever be used to protect against Byzatine behavior?

by Lee Pike at February 04, 2010 08:38 AM

Bryn Keller

Tech Note: Finding All the Assemblies Available to Your Application

I sometimes have .Net library code that needs to work both in web applications and in console or service applications. The differences between the two environments, even for a library, are sometimes surprising. For example, I recently needed to find all the assemblies available to (i.e., in the same folder with) the application. My first implementation worked fine for the command line app, but failed miserably for the web site. After trying a few different approaches, I found one that works well. I’m recording this for the benefit of anyone else who has to perform this trick.

The “obvious” way to do this, I thought, was to use Assembly.GetExecutingAssembly().Location to find where the assembly lived, use DirectoryInfo.GetFiles() to find the dll and exe files in that location’s directory, and then use Assembly.LoadFile() to load them. This all works just fine in the command line application, but in a web site, there’s shadow copying involved – the assembly returned to you from GetExecutingAssembly isn’t in your Bin folder, it’s in a semi-randomly named folder nesteded several levels deep from your Temporary ASP.Net Files folder. To add difficulty, that one assembly (along with its pdb file, perhaps) is all that’s in the folder. Each of the assemblies in your Bin folder gets copied to a unique folder of its own before it’s loaded into the application, so there’s no easy way to find them all and iterate over them. I didn’t check this, but it’s entirely possible that the assembly isn’t copied at all until it needs to be loaded, in which case even scrubbing through the funny folders looking for assemblies wouldn’t work.

So if Assembly.Location doesn’t work, what does? Something on the AppDomain, perhaps. AppDomain.CurrentDomain.BaseDirectory works for command line apps, but for web sites it gives you the root of the site, not the bin folder. For a web site, the Bin folder is in PrivateBinPath. PrivateBinPath is null for command line apps, though. So it turns out the way to find all your assemblies is by using PrivateBinPath if it’s not null, or else BaseDirectory.

Now that we have a way to find all the available assemblies, what about loading them? If your application is doing shadow copying, you can’t use Assembly.LoadFromFile, since you’re working with assemblies from the Bin folder, but your application is loading them from the shadow copy directories with the funny names, which means if you need a type called “Foo.Bar.Baz,MyAssembly” and you load it from an assembly in the Bin folder, it won’t be compatible with the types that are already loaded in your application, because they were all loaded from different assemblies in the shadow copy folders.

A better way to load them is to ask the AppDomain to Load() them. Get the name from the file, strip the extension, pass it to AppDomain.CurrentDomain.Load(). That way it will load types from the correct assemblies – either from the application directory, or from the shadow copy directories, whichever is correct for your application.

by xoltar at February 04, 2010 03:59 AM

Lee Pike

10 to the -9


10^{-9}, or one-in-a-billion, is the famed number given for the maximum probability of a catastrophic failure, per hour of operation, in life-critical systems like commercial aircraft.  The number is part of the folklore of the safety-critical systems literature; where does it come from?

First, it’s worth noting just how small that number is.  As pointed out by Driscoll et al. in the paper, Byzantine Fault Tolerance, from Theory to Reality, the probability of winning the U.K. lottery is 1 in 10s of millions, and the probability of being struck by lightening (in the U.S.) is 1.6 \times 10^{-6}, more than a 1,000 times more likely than 10^{-9}.

So where did 10^{-9} come from?  A nice explanation comes from a recent paper by John Rushby:

If we consider the example of an airplane type with 100 members, each flying 3000 hours per year over an operational life of 33 years, then we have a total exposure of about 107 flight hours. If hazard analysis reveals ten potentially catastrophic failures in each of ten subsystems, then the “budget” for each, if none are expected to occur in the life of the fleet, is a failure probability of about 10^{-9} per hour [1, page 37]. This serves to explain the well-known 10^{-9} requirement, which is stated as follows: “when using quantitative analyses. . . numerical probabilities. . . on the order of 10^{-9} per flight-hour. . . based on a flight of mean duration for the airplane type may be used. . . as aids to engineering judgment. . . to. . . help determine compliance” (with the requirement for extremely improbable failure conditions) [2, paragraph 10.b].

[1] E. Lloyd and W. Tye, Systematic Safety: Safety Assessment of Aircraft Systems. London, England: Civil Aviation Authority, 1982, reprinted 1992.

[2] System Design and Analysis, Federal Aviation Administration, Jun. 21, 1988, advisory Circular 25.1309-1A.

(By the way, it’s worth reading the rest of the paper—it’s the first attempt I know of to formally connect the notions of (software) formal verification and reliability.)

So there a probabilistic argument being made, but let’s spell it out in a little more detail.  If there are 10 potential failures in 10 subsystems, then there are 10 \times 10 = 100 potential failures.  Thus, there are 2^{100} possible configurations of failure/non-failure in the subsystems.  Only one of these configurations is acceptable—the one in which there are no faults.

If the probability of failure is x, then the probability of non-failure is 1 - x.  So if the probability of failure for each subsystem is 10^{-9}, then the probability of being in the one non-failure configuration is

\displaystyle(1 - 10^{-9})^{100}

We want that probability of non-failure to be greater than the required probability of non-failure, given the total number of flight hours.  Thus,

\displaystyle (1 - 10^{-9})^{100} > 1 - 10^{-7}

which indeed holds:

\displaystyle (1 - 10^{-9})^{100} - (1 - 10^{-7})

is around 4.95 \times 10^{-15}.

Can we generalize the inequality?  The hint for how to do so is that the number of subsystems (100) is no more than the overall failure rate divided by the subsystem rate:

\displaystyle \frac{10^{-7}}{10^{-9}}

This suggests the general form is something like


Subsystem reliability inequality: \displaystyle (1 - C^{-n})^{C^{n-m}} \geq 1 - C^{-m}


where C, n, and m are real numbers, C \geq 1, n \geq 0, and n \geq m.

Let’s prove the inequality holds.  Joe Hurd figured out the proof, sketched below (but I take responsibility for any mistakes in it’s presentation).  For convenience, we’ll prove the inequality holds specifically when C = e, but the proof can be generalized. 

First, if n = 0, the inequality holds immediately. Next, we’ll show that

\displaystyle (1 - e^{-n})^{e^{n-m}}

is monotonically non-decreasing with respect to n by showing that the derivative of its logarithm is greater or equal to zero for all n > 0.  So the derivative of its logarithm is

\displaystyle \frac{d}{dn} \; e^{n-m}\ln(1-e^{-n}) = e^{n-m}\ln(1-e^{-n})+\frac{e^{-m}}{1-e^{-n}}

We show

\displaystyle e^{n-m}\ln(1-e{-n})+\frac{e^{-m}}{1-e^{-n}} \geq 0

iff

\displaystyle e^{-m}\left(e^{n}\ln(1-e^{-n}) + \frac{1}{1-e^{-n}}\right) \geq 0

and since e^{-m} \geq 0,

\displaystyle e^{n}\ln(1-e^{-n}) + \frac{1}{1-e^{-n}} \geq 0

iff

\displaystyle e^{n}\ln(1-e^{-n}) \geq - \frac{1}{1-e^{-n}}

Let x = e^{-n}, so the range of x is 0 < x < 1.
\displaystyle\ln(1-x) \geq - \frac{x}{1-x}

Now we show that in the range of x, the left-hand side is bounded below by the right-hand side of the inequality.
\displaystyle \lim_{x \to 0} \; \ln(1-x) = 0

and
\displaystyle - \frac{x}{1-x} = 0

Now taking their derivatives
\displaystyle \frac{d}{dx} \; \ln(1-x) = \frac{1}{x-1}

and
\displaystyle \frac{d}{dx} \; - \frac{x}{1-x} = - \frac{1}{(x-1)^2}

Because \displaystyle x-1 \geq - (x-1)^2 in the range of x, our proof holds.

The purpose of this post was to clarify the folklore of ultra-reliable systems.  The subsystem reliability inequality presented allows for easy generalization to other reliable systems.

Thanks again for the help, Joe!

by Lee Pike at February 04, 2010 03:41 AM

February 03, 2010

Holumbus

Hayoo! Webservice API

We have just deployed a new version of Hayoo!, which now provides a JSON-based webservice API.

Queries may be posted to Hayoo! via this URI:

http://holumbus.fh-wedel.de/hayoo/hayoo.json?query=YOUR_QUERY

Search results are returned as JSON encoded string. More details about the structure of the result can be found here.

We would be very happy to see many (web-) applications to include Hayoo! search functionality. Your own creativity is the only limit ;)

If you need help or come across any problems, just drop us a line.

by tbh at February 03, 2010 09:22 PM

apfelmus

The Operational Monad Tutorial

This article was first published in issue 15 of The Monad.Reader.

Introduction

Another monad tutorial? Oh my god, why!? Fear not, this article is aimed at Haskellers who are already familiar with monads, though I have of course tried to keep the material as accessible as possible; the first two sections may serve as an initial introduction to monads and the monad laws for the brave.

In this tutorial, I would like to present monads from the viewpoint of operational semantics and how it makes designing and implementing new monads a piece of cake. Put differently, s -> (a,s) is not the only way to implement the state monad and this tutorial aims to present a much more systematic way. I think it is still regrettably underused, hence this text.

The main idea is to view monads as a sequence of instructions to be executed by a machine, so that the task of implementing monads is equivalent to writing an interpreter. The introductory example will be a stack automaton, followed by a remark on a monad for random numbers. Then, to showcase the simplicity of this approach, we will implement backtracking parser combinators, culminating in a straightforward breadth-first implementation equivalent to Claessen's parallel parsing processes.

For those in the know, I'm basically going to present the principles of Chuan-kai Lin's Unimo paper. The approach is neither new nor unfamiliar; for example, John Hughes already used it to derive the state monad. But until I read Lin's paper, I did not understand how valuable it is when done systematically and in Haskell. Ryan Ingram's MonadPrompt package is another recent formulation.

To encourage reuse, I have also released a package operational on hackage which collects the generic bits of these ideas in a small library. For convenient study, the source code from each section of this article is also available.

Stack Machine - List of Instructions?

Our introductory example will be a stack machine, i.e. an imperative mini-language featuring two instructions push and pop for pushing and popping values onto and from a stack.

In other words, I have imperative programs like the following in mind:

push 5; push 42; pop;

Instructions are separated by semicolons. As shown in the following picture, this program first puts the number 5 on the stack, then puts the number 42 on top of the stack and proceeds to remove it again.

Example stack program

How can we embed such programs into Haskell?

Representation

First we need some way of representing the program text, for instance as a list of instructions:

type Program instr    = [instr]
type StackProgram     = Program StackInstruction
data StackInstruction = Push Int | Pop

Our example is represented as

example = Push 5 : Push 42 : Pop : []

In a sense, the colon (:) for building lists takes the role of the semicolon for sequencing instructions.

Concatenation and thoughts on the interface

Note that this representation gives us a very convenient tool for assembling bigger programs from smaller subprograms: list concatenation (++). For instance,

exampleTwice = example ++ example
    = Push 5 : Push 42 : Pop : Push 5 : Push 42 : Pop : []

is a program that executes example twice. Together with the empty program

empty = []

concatenation obeys the following three well-known laws:

 empty ++ is      =  is                  -- left unit
    is ++ empty   =  is                  -- right unit
(is ++ js) ++ ks  =  is ++ (js ++ ks)    -- associativity

which seem almost too evident to be worth mentioning. For example, it is customary to leave out the parenthesis in the last line altogether.

Once accustomed to the notion of programs and (++) to combine them, the special case of single instructions and (:) for sequencing them is unnecessary. The user of our language does not care that we deem push and pop to be primitive operations but not, for example, the program

replace a = Pop : Push a : []

which replaces the topmost stack element with a; he is entirely content to be given two programs

push :: Int -> StackProgram
pop  :: StackProgram

and two general combinators for building new ones

empty :: StackProgram
(++)  :: StackProgram -> StackProgram -> StackProgram 

without any mention of the distinction between single instruction and compound program. Their difference is but an implementation detail.

Interpreter

Well, to be entirely content, the user also needs a way to run programs. In particular, we need to implement a function interpret that maps the program text to its intended meaning, here a function that transforms a stack of integers.

type Stack a = [a]

interpret :: StackProgram -> (Stack Int -> Stack Int)

The implementation follows the style of operational semantics: inspect the first instruction, change the stack accordingly, and recursively proceed with the remaining list of instructions is:

interpret (Push a : is) stack = interpret is (a : stack)
interpret (Pop    : is) stack = interpret is (tail stack)
interpret []            stack = stack

Oops

"All well and good, but why all the fuss with 'monads' then, when lists of instructions will do?" you may ask. Alas, the problem is of course that lists won't do! We forgot something very important: our programs are completely unable to inspect values from the stack.

For instance, how to write a program that pops the two topmost values and pushes their sum onto the stack? Clearly, we want something like

a <- pop;
b <- pop;
push (a+b);

where each pop returns the element just removed and the arrow <- binds it to a variable. But binding variables is simply impossible to express with our current representation of programs as lists of instructions.

Stack Machine - Monad

Well, if ordinary lists of instructions are not enough to represent programs that involve binding variables like

a <- pop; b <- pop; push (a+b);

then let's invent some fancy kind of list of instructions that will! The following presentation will be in close analogy to the structure of the previous section.

Representation

Return types

First, if we want to interpret pop as a function that returns something, we had better label it with the type of the value returned! Hence, instead of a plain type

Pop :: StackInstruction

we need an additional type argument

Pop :: StackInstruction Int

which indicates that the Pop instruction somehow returns a value of type Int.

For simplicity, we attribute a return type to push as well, even though it doesn't really return anything. This can modeled just fine with the unit type ().

Push 42 :: StackInstruction ()
Push    :: Int -> StackInstruction ()

Putting both together, our type of instructions will become

data StackInstruction a where
    Pop  :: StackInstruction Int
    Push :: Int -> StackInstruction ()

If this syntax is alien to you: this is a Generalized Algebraic Data Type (GADT) which allows us to define a data type by declaring the types of its constructors directly. As of Haskell 2010, GADTs are not yet part of the language standard, but they are supported by GHC.

Like instructions, we also have to annotate programs with their return type, so that the definition for StackProgram becomes

data Program instr a where ...

type StackProgram a = Program StackInstruction a

As before, instr is the type of instructions, whereas a is the newly annotated return type.

Binding variables

How to represent the binding of variables? Lambda abstractions will do the trick; imagine the following:

take a bindinga <- pop; rest
turn the arrow to the rightpop -> a; rest
and use a lambda expression to move it past the semicolonpop; \a -> rest

Voila, the last step can be represented in Haskell, with a constructor named Then taking the role of the semicolon:

Pop `Then` \a -> rest 

The idea is that Then plugs the value returned by pop into the variable a. By the way, this is akin to how let expressions can be expressed as lambda abstractions in Haskell:

let a = foo in bar   <=>   (\a -> bar) foo

Anyway, our motivating example can now be represented as

example2 = Pop `Then` (\a -> Pop `Then`
                        (\b -> Push (a+b) `Then` Return))

where Return represents the empty program which we will discuss in a moment. Remember that parentheses around the lambda expressions are optional, so we can also write

example2 = Pop `Then` \a ->
           Pop `Then` \b ->
           Push (a+b) `Then`
           Return

It is instructive to think about the type of Then. It has to be

Then :: instr a -> (a -> Program instr b) -> Program instr b

Except for the return type a in instr a and the lambda abstraction, this is entirely analogous to the "cons" operation (:) for lists.

Empty program

The empty program, corresponding to the empty list [], is best represented by a constructor

Return :: a -> Program instr a

that is not "entirely empty" but rather denotes a trivial instruction that just returns the given value a (hence the name). This is very useful, since we can now choose return values freely. For instance,

example3 = Pop `Then` \a -> Pop `Then` \b -> Return (a*b)

is a program that pops two values from the stack but whose return value is their product.

The fancy list

Taking everything together, we obtain a fancy list of instructions, once again a GADT:

data Program instr a where
    Then   :: instr a -> (a -> Program instr b) -> Program instr b
    Return :: a -> Program instr a

And specialized to our stack machine language, we get

type StackProgram a = Program StackInstruction a

Interpreter

Before thinking thinking further about our new representation, let's first write the interpreter to see the stack machine in action. This time, however, we are not interested in the final stack, only in the value returned.

interpret :: StackProgram a -> (Stack Int -> a)
interpret (Push a `Then` is) stack     = interpret (is ()) (a:stack)
interpret (Pop    `Then` is) (b:stack) = interpret (is b ) stack
interpret (Return c)         stack     = c

The implementation is like the previous one, except that now, we also have to pass the return values like () and b to the remaining instructions is.

Our example program executes as expected:

GHCi> interpret example3 [7,11]
77

Concatenation and interface

Just as with lists, we can build large programs by concatenating smaller subprograms. And as before, we don't want the user to bother with the distinction between single instruction and compound program.

We begin with the latter: the function

singleton :: instr a -> Program instr a
singleton i = i `Then` Return

takes the role of \x -> [x] and helps us blur the line between program and instructions:

pop  :: StackProgram Int
push :: Int -> StackProgram ()

pop  = singleton Pop
push = singleton . Push

Now, we define the concatenation operator (often dubbed "bind") that glues two programs together:

(>>=) :: Program i a -> (a -> Program i b) -> Program i b
(Return a)    >>= js  = js a
(i `Then` is) >>= js  = i `Then` (\a -> is a >>= js)

Apart from the new symbol (>>=) and the new type signature, the purpose and implementation is entirely analogous to (++). And as before, together with the empty program,

return = Return

it obeys three evident laws

return a >>= is     = is a                        -- left unit
is >>= return       = is                          -- right unit
(is >>= js) >>= ks  = is >>= (\a -> js a >>= ks)  -- associativity

also called the monad laws. Since we need to pass return values, the laws are slightly different from the concatenation laws for ordinary lists, but their essence is the same.

The reason that these equations are called the "monad laws" is that any data type supporting two such operations and obeying the three laws is called a monad. In Haskell, monads are assembled in the type class Monad, so we'd have to make an instance

instance Monad (Program instr) where
    (>>=)  = ...
    return = ...

This is similar to lists which are said to constitute a monoid.

We conclude the first part of this tutorial by remarking that the (>>=) operator is the basis for many other functions that build big programs from small ones; these can be found in the Control.Monad module and are described elsewhere.


What we've done so far

Those familiar with the state monad will recognize that the whole stack machine was just

State (Stack Int)

in disguise. But surprisingly, we haven't used the pattern s -> (a,s) for threading state anywhere! Instead, we were able to implement the equivalent of

evalState :: State s -> (s -> a)

directly, even though the type s -> a by itself is too "weak" to serve as an implementation of the state monad.

This is a very general phenomenon and it is of course the main benefit of the operational viewpoint and the new Program instr a type. No matter what we choose as interpreter function or instruction set, the monad laws for (>>=) and return will always hold, for they are entirely independent of these choices. This makes it much easier to define and implement new monads and the remainder of this article aims to give a taste of its power.

Multiple Interpreters

A first advantage of the operational approach is that it allows us to equip one and the same monad with multiple interpreters. We'll demonstrate this flexibility with an example monad Random that expresses randomness and probability distributions.

The ability to write multiple interpreters is also very useful for implementing games, specifically to account for both human and computer opponents as well as replaying a game from a script. This is what prompted Ryan Ingram to write his MonadPrompt package.

Random Numbers

At the heart of random computations is a type Random a which denotes random variables taking values in a. Traditionally, the type a would be a numeric type like Int, so that Random Int denotes "random numbers". But for the Haskell programmer, it is only natural to generalize it to any type a. This generalization is also very useful, because it reveals hidden structure: it turns out that Random is actually a monad.

There are two ways to implement this monad: one way is to interpret random variables as a recipe for creating pseudo-random values from a seed, which is commonly written

type Random a = StdGen -> (a,StdGen)

The other is to view them as a probability distribution, as for example expressed in probabilistic functional programming as

type Probability = Double
type Random a    = [(a,Probability)]

Traditionally, we'd have to choose between one way or the other depending on the application. But with the operational approach, we can have our cake and eat it, too! The two ways of implementing random variables can be delegated to two different interpreter functions for one and the same monad Random.

For demonstration purposes, we represent Random as a language with just one instruction uniform that randomly selects an element from a list with uniform probability

type Random a = Program RandomInstruction a

data RandomInstruction a where
    Uniform :: [a] -> RandomInstruction a

uniform :: [a] -> Random a
uniform = singleton . Uniform

For example, a roll of a die is modeled as

die :: Random Int
die = uniform [1..6]

and the sum of two dice rolls is

sum2Dies = die >>= \a -> die >>= \b -> return (a+b)

Now, the two different interpretations are: sampling a random variable by generating pseudo-random values

sample :: Random a -> StdGen -> (a,StdGen)
sample (Return a)             gen = (a,gen)
sample (Uniform xs `Then` is) gen = sample (is $ xs !! k) gen'
    where (k,gen') = System.Random.randomR (0,length xs-1) gen

and calculating its probability distribution

distribution :: Random a -> [(a,Probability)]
distribution (Return a)             = [(a,1)]
distribution (Uniform xs `Then` is) =
    [(a,p/n) | x <- xs, (a,p) <- distribution (is x)]
    where n = fromIntegral (length xs)

Truth to be told, the distribution interpreter has a flaw, namely that it never tallies the probabilities of equal outcomes. That's because this would require an additional Eq a constraint on the types of return and (>>=), which is unfortunately not possible with the current Monad type class. A workaround for this known limitation can be found in the norm function from the paper on probabilistic functional programming.

Monadic Parser Combinators

Now, it is time to demonstrate that the operational viewpoint also makes the implementation of otherwise advanced monads a piece of cake. Our example will be monadic parser combinators and for the remainder of this article, I will assume that you are somewhat familiar with them already. The goal will be to derive an implementation of Koen Claessen's ideas from scratch.

Primitives

At their core, monadic parser combinators are a monad Parser with just three primitives:

symbol :: Parser Char
mzero  :: Parser a
mplus  :: Parser a -> Parser a -> Parser a

which represent

  • a parser that reads the next symbol from the input stream
  • a parser that never succeeds
  • a combinator that runs two parsers in parallel

respectively. (The last two operations define the MonadPlus type class.) Furthermore, we need an interpreter, i.e. a function

interpret :: Parser a -> (String -> [a])

that runs the parser on the string and returns all successful parses.

The three primitives are enough to express virtually any parsing problem; here is an example of a parser number that recognizes integers:

satisfies p = symbol >>= \c -> if p c then return c else mzero 
many  p     = return [] `mplus` many1 p
many1 p     = liftM2 (:) p (many p) 
digit       = satisfies isDigit >>= \c -> return (ord c - ord '0')
number      = many1 digit >>= return . foldl (\x d -> 10*x + d) 0

A first implementation

The instruction set for our parser language will of course consist of these three primitive operations:

data ParserInstruction a where
    Symbol :: ParserInstruction Char
    MZero  :: ParserInstruction a
    MPlus  :: Parser a -> Parser a -> ParserInstruction a

type Parser a = Program ParserInstruction a

A straightforward implementation of interpret looks like this:

interpret :: Parser a -> String -> [a]
interpret (Return a)            s = if null s then [a] else []
interpret (Symbol    `Then` is) s = case s of
    c:cs -> interpret (is c) cs
    []   -> []
interpret (MZero     `Then` is) s = []
interpret (MPlus p q `Then` is) s =
    interpret (p >>= is) s ++ interpret (q >>= is) s

For each instruction, we specify the intended effects, often calling interpret recursively on the remaining program is. In prose, the four cases are

  • Return at the end of a program will return a result if the input was parsed completely.
  • Symbol reads a single character from the input stream if available and fails otherwise.
  • MZero returns an empty result immediately.
  • MPlus runs two parsers in parallel and collects their results.

A note on technique

The cases for MZero and MPlus are a bit roundabout; the equations

interpret mzero       = \s -> []
interpret (mplus p q) = \s -> interpret p s ++ interpret q s

express our intention more plainly. Of course, these two equations do not constitute valid Haskell code for we may not pattern match on mzero or mplus directly. The only thing we may pattern match on is a constructor, for example like this

interpret (Mplus p q `Then` is) = ...

But even though our final Haskell code will have this form, this does not mean that jotting down the left hand side and thinking hard about the ... is the best way to write Haskell code. No, we should rather use the full power of purely functional programming and use a more calculational approach, deriving the pattern matches from more evident equations like the ones above.

In this case, we can combine the two equations with the MonadPlus laws

    mzero >>= m = mzero
mplus p q >>= m = mplus (p >>= m) (q >>= m)

which specify how mzero and mplus interact with (>>=), to derive the desired pattern match

  interpret (Mplus p q `Then` is)
=   { definition of concatenation and mplus }
  interpret (mplus p q >>= is)
=   { MonadPlus law }
  interpret (mplus (p >>= is) (q >>= is))
=   { intended meaning }
  \s -> interpret (p >>= is) s ++ interpret (q >>= is) s

Now, in light of the first step of this derivation, I even suggest to forget about constructors entirely and instead regard

interpret (mplus p q >>= is) = ...

as "valid" Haskell code; after all, it is straightforwardly converted to a valid pattern match. In other words, it is once again beneficial to not distinguish between single instructions and compound programs, at least in notation.

Depth-first

Unfortunately, our first implementation has a potential space leak, namely in the case

interpret (MPlus p q `Then` is) s =
    interpret (p >>= is) s ++ interpret (q >>= is) s

The string s is shared by the recursive calls and has to be held in memory for a long time.

In particular, the implementation will try to parse s with the parser p >>= is first, and then backtrack to the beginning of s to parse it again with the second alternative q >>= is. That's why this is called a depth-first or backtracking implementation. The string s has to be held in memory as long the second parser has not started yet.

Breadth-first

To ameliorate the space leak, we would like to create a breadth-first implementation, one which does not try alternative parsers in sequence, but rather keeps a collection of all possible alternatives and advances them at once.

How to make this precise? The key idea is the following equation:

  (symbol >>= is) `mplus` (symbol >>= js)
=  symbol >>= (\c -> is c `mplus` js c)

When the parsers on both sides of mplus are waiting for the next input symbol, we can group them together and make sure that the next symbol will be fetched only once from the input stream.

Clearly, this equation readily extends to more than two parsers, like for example

  (symbol >>= is) `mplus` (symbol >>= js) `mplus` (symbol >>= ks)
=  symbol >>= (\c -> is c `mplus` js c `mplus` ks c)

and so on.

We want to use this equation as a function definition, mapping the left hand side to the right hand side. Of course, we can't do so directly because the left hand side is not one of the four patterns we can match upon. But thanks to the MonadPlus laws, what we can do is to rewrite any parser into this form, namely with a function

expand :: Parser a -> [Parser a]
expand (MPlus p q `Then` is) = expand (p >>= is) ++
                               expand (q >>= is)
expand (MZero     `Then` is) = []
expand x                     = [x]

The idea is that expand fulfills

foldr mplus mzero . expand = id

and thus turns a parser into a list of summands which we now can pattern match upon. In other words, this function expands parsers matching mzero >>= is and mplus p q >>= is until only summands of the form symbol >>= is and return a remain.

With the parser expressed as a big "sum", we can now apply our key idea and group all summands of the form symbol >>= is; and we also have to take care of the other summands of the form return a. The following definition will do the right thing:

interpret :: Parser a -> String -> [a]
interpret p = interpret' (expand p)
    where
    interpret' :: [Parser a] -> String -> [a]
    interpret' ps []     = [a | Return a <- ps]
    interpret' ps (c:cs) = interpret'
        [p | (Symbol `Then` is) <- ps, p <- expand (is c)] cs

Namely, how to handle each of the summands depends on the input stream:

  • If there are still input symbols to be consumed, then only the summands of the form symbol >>= is will proceed, the other parsers have ended prematurely.
  • If the input stream is empty, then only the parsers of the form return x have parsed the input correctly, and their results are to be returned.

That's it, this is our breadth-first interpreter, obtained by using laws and equations to rewrite instruction lists. It is equivalent to Koen Claessen's implementation.

As an amusing last remark, I would like to mention that our calculations can be visualized as high school algebra if we ignore that (>>=) has to pass around variables, as shown in the following table:

TermMathematical operation
return1
(>>=) ×  multiplication
mzero0
mplus+ addition
symbolx indeterminate

For example, our key idea corresponds to the distributive law

x × a+x × b=x × (a+b)

and the monad and MonadPlus laws have well-known counterparts in algebra as well.

Conclusion

Further Examples

I hope I have managed to convincingly demonstrate the virtues of the operational viewpoint with my choice of examples.

There are many other advanced monads whose implementations also become clearer when approached this way, such as the list monad transformer (where the naive m [a] is known not to work), Oleg Kiselyov's LogicT, Koen Claessen's poor man's concurrency monad, as well coroutines like Peter Thiemann's ingenious WASH which includes a monad for tracking session state in a web server.

The operational package includes a few of these examples.

Connection with the Continuation Monad

Traditionally, the continuation monad transformer

data Cont m a = Cont { runCont :: forall b. (a -> m b) -> m b }

has been used to implement these advanced monads. This is no accident; both approaches are capable of implementing any monad. In fact, they are almost the same thing: the continuation monad is the refunctionalization of instructions as functions

\k -> interpret (Instruction `Then` k)

But alas, I think that this unfortunate melange of instruction, interpreter and continuation does not explain or clarify what is going on; it is the algebraic data type Program that offers a clear notion of what a monad is and what it means to implement one. Hence, in my opinion, the algebraic data type should be the preferred way of presenting new monads and also of implementing them, at least before program optimizations.

Actually, Program is not a plain algebraic data type, it is a generalized algebraic data type. It seems to me that this is also the reason why the continuation monad has found more use, despite being conceptually more difficult: GADTs simply weren't available in Haskell. I believe that the Program type is a strong argument to include GADTs into a future Haskell standard.

Drawbacks

Compared to specialized implementations, like for example s -> (a,s) for the state monad, the operational approach is not entirely without drawbacks.

First, the given implementation of (>>=) has the same quadratic running time problem as (++) when used in a left-associative fashion. Fortunately, this can be ameliorated with a different (fancy) list data type; the operational library implements one.

Second, and this cannot be ameliorated, we lose laziness. The state monad represented as s -> (a,s) can cope with some infinite programs like

evalState (sequence . repeat . State $ \s -> (s,s+1)) 0

whereas the list of instructions approach has no hope of ever handling that, since only the very last Return instruction can return values.

I also think that this loss of laziness also makes value recursion a la MonadFix very difficult.

About the author

After some initial programming experience in Pascal, Heinrich Apfelmus picked up Haskell and purely functional programming just at the dawn of the new millenium. He has never looked back ever since, for he not only admires Haskell's mathematical elegance, but also its practicality in personal life. For instance, he was always too lazy to tie knots, but that has changed and he now accepts shoe laces instead of velcro.

February 03, 2010 02:11 PM

Mikael Vejdemo Johansson (Syzygy-)

Testing out the wplatex package

Eric Finster, over at Curious Reasoning has built a python script to allow you to write Wordpress posts entirely in LaTeX , and upload them. The script parses the LaTeX code and generates HTML that expresses the same structure.

This, here, is me trying it out. With any luck, the appearance of a new toy will get me back to actually blogging some more – it’s been winding down a bit much here lately.

by Michi at February 03, 2010 04:38 AM

February 01, 2010

Gareth Smith (gds)

Random 11pm thought: Privacy through insecurity.

If you haven't considered the potential for (and potential dangers of) total lack of privacy in the information age, go watch this before reading the rest of this post.

I've met a whole bunch of cool new people in the last year, and I keep "in touch" with many of them primarily using facebook. You know how this story goes - you go to a conference or take a short course of gardening classes or something ; you meet new folk ; you spend a lot of time with them for a few weeks ; then you go your separate ways and communicate for a year or two only through the medium of broadcast "status updates" that can be read by anyone you've ever met.

One of the folk I met last year has been getting increasingly facebook-eccentric. They've been posting increasingly embarrassing and personal status updates, starting their own fan club, joining the support groups for controversial political parties, and most recently, writing long essays defending their strange and cultish religious views. Of course, none of that stuff was actually my new friend - all they're really guilty of is forgetting to log out of facebook after using a public terminal. Repeatedly.

So now, any time I see anything embarrassing on my new friend's facebook page, I'll just assume that they've left themselves logged in again, and someone's having a laugh at their expense. I don't know what's actually going on - I haven't seen or spoken to them in months. I'll just assume that anything "normal" is the truth, and anything out of the ordinary is a practical joke. And so will anyone else who knows them. If they run for the presidency of the US in 20 years time, and some journalist finds something juicy in the old digital records, the spin doctors will be able to laugh it all off. That wasn't our candidate - that was a well documented series of attacks by notorious hackers of the time.

Of course, this is just a special case of increasing the signal to noise ratio on the net - which you can do any number of ways. It's fairly well known that companies will post fake product reviews if they can get away with it. Perhaps we could all open large numbers of facebook accounts, and use each of them to communicate with 1/n of our friends. Perhaps spin doctors should spend their time inventing implausible stories about their candidates, and filling the net with them, so the real stories get lost in the mess. Perhaps they already do. I'm sure none of this is a new idea, but it tickled me that forgetting to log out, or having an easy to guess password might offer my friend more privacy in the long run, not less.

Also: I decided to try using the plural in this note - if anyone has an opinion on that vs GNPs or any other way of writing what I wanted to write, then feel free to comment.

February 01, 2010 11:47 PM