<?xml version='1.0' encoding='UTF-8'?><rss xmlns:atom='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:blogger='http://schemas.google.com/blogger/2008' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0' version='2.0'><channel><atom:id>tag:blogger.com,1999:blog-7435412383881764254</atom:id><lastBuildDate>Wed, 02 Jan 2013 06:20:31 +0000</lastBuildDate><category>smtp</category><category>single dispatch</category><category>Application Platform</category><category>Eclipse RCP</category><category>Mnesia</category><category>Apple</category><category>RIA</category><category>Concurrency</category><category>Dayfindr.com</category><category>Software Development Process</category><category>Charts</category><category>multiple dispatch</category><category>TDD</category><category>Acid3</category><category>OTP</category><category>Safari</category><category>Distributed databases</category><category>Apache</category><category>Powerpoint</category><category>Unit Testing</category><category>Rake</category><category>HTML5</category><category>Scalaris</category><category>Web server performance</category><category>Make</category><category>Google Wave</category><category>REST</category><category>Macbook Pro</category><category>Opera</category><category>XPlanner</category><category>erlang exchange</category><category>Google OS</category><category>Mochiweb</category><category>Yaws</category><category>Java</category><category>Presentations</category><category>Google Chrome</category><category>Firefox</category><category>Iterative Development</category><category>FUSE</category><category>Observer Pattern</category><category>Rakefile</category><category>Visitor pattern</category><category>Quicksort</category><category>IE</category><category>Keynote</category><category>Internet Explorer</category><category>DHTML</category><category>Agile Development</category><category>Object Oriented Programming</category><category>Erlang factory</category><category>Big Mac Index</category><category>Erlang</category><title>21st Century Code Works</title><description></description><link>http://21ccw.blogspot.com/</link><managingEditor>noreply@blogger.com (Benjamin Nortier)</managingEditor><generator>Blogger</generator><openSearch:totalResults>35</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-7435412383881764254.post-4582542653477526639</guid><pubDate>Thu, 04 Feb 2010 17:40:00 +0000</pubDate><atom:updated>2010-07-23T01:45:40.066-07:00</atom:updated><title>Goodbye Blogger</title><description>&lt;p&gt;2010 has brought a few changes. I'm now a freelance software engineer with my own company, 1011 Ltd, based in London.  This is also ideal opportunity to do something I've been planning for a while - stop using Blogger.&lt;/p&gt;&lt;p&gt;Blogger used to be fine, but it just hasn't kept up. Wordpress, for example, (and there are plenty of others) offers so much more in terms of usability, customizability and administration. Some real pain points for me are:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;In Blogger you have to work with a minuscule editing window that you cannot enlarge. Wordpress has a fullscreen mode. &lt;/li&gt;&lt;li&gt;You can download and install Wordpress to your own domain, even modify it as you like, and have ownership of all your posts.&lt;/li&gt;&lt;li&gt;Blogger has doesn't have proper previews using the blog template, whereas the preview in Wordpress is an &lt;i&gt;actual&lt;/i&gt; preview.&lt;/li&gt;&lt;li&gt;At this very moment the image feature is broken&lt;/li&gt;&lt;li&gt;The html in uneditable&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;&lt;p&gt;It feels to me like Google bought Blogger and then just stop working on it. A bit like Microsoft &lt;a href="http://www.telegraph.co.uk/technology/6530310/Firefox-new-version-planned-for-December-as-browser-celebrates-fifth-birthday.html?"&gt;disbanding the IE team in 2004&lt;/a&gt;. &lt;/p&gt; &lt;p&gt;Find me at my new blog, &lt;a href="http://www.1011ltd.com/web/blog"&gt;www.1011ltd.com/web/blog&lt;/a&gt;&lt;/p&gt;&lt;span class="Apple-style-span"  style="color:#FF0000;"&gt;[Update: And as you can see, the Spam protection is woeful]&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;/p&gt;&lt;/div&gt;</description><link>http://21ccw.blogspot.com/2010/02/goodbye-blogger.html</link><author>noreply@blogger.com (Benjamin Nortier)</author><thr:total>23</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-7435412383881764254.post-3951992122970155328</guid><pubDate>Sun, 23 Aug 2009 15:40:00 +0000</pubDate><atom:updated>2009-08-23T09:30:42.253-07:00</atom:updated><category domain='http://www.blogger.com/atom/ns#'>Google Wave</category><category domain='http://www.blogger.com/atom/ns#'>Erlang</category><title>My Google Wave Client</title><description>Google released a &lt;a href="http://code.google.com/p/wave-protocol/"&gt;reference implementation&lt;/a&gt; of a Wave client and server in July, written in Java, that you can download and experiment with. When Mickaël Rémond wrote about &lt;a href="http://www.process-one.net/en/blogs/article/using_google_wave_reference_implementation_with_ejabberd/"&gt;using the reference implementation with ejabberd&lt;/a&gt;, I decided that it was time to get my hands dirty.&lt;br /&gt;&lt;br /&gt;I got the reference implementation working, and I could chat with multiple participants in the console clients. However, I wanted to know more about what was happening behind the scenes in the client, and more importantly, the server (and how operation tranforms work).&lt;br /&gt;&lt;br /&gt;So I decided to write my own client in Erlang that communicates with the reference server. This way I could investigate what was happening in the client and the server, and gain more knowledge about Wave. It required a bit of reverse engineering, and messing around with protocol buffers (the client communicates with the server using these), but it works nicely.&lt;br /&gt;&lt;br /&gt;I've created a video to demonstrate it. Happy watching!&lt;br /&gt;&lt;br /&gt;&lt;object width="400" height="300"&gt;&lt;param name="allowfullscreen" value="true" /&gt;&lt;param name="allowscriptaccess" value="always" /&gt;&lt;param name="movie" value="http://vimeo.com/moogaloop.swf?clip_id=6233461&amp;amp;server=vimeo.com&amp;amp;show_title=1&amp;amp;show_byline=1&amp;amp;show_portrait=0&amp;amp;color=&amp;amp;fullscreen=1" /&gt;&lt;embed src="http://vimeo.com/moogaloop.swf?clip_id=6233461&amp;amp;server=vimeo.com&amp;amp;show_title=1&amp;amp;show_byline=1&amp;amp;show_portrait=0&amp;amp;color=&amp;amp;fullscreen=1" type="application/x-shockwave-flash" allowfullscreen="true" allowscriptaccess="always" width="400" height="300"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;p&gt;&lt;a href="http://vimeo.com/6233461"&gt;Vimeo link&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;P.S. I used ngerakines's &lt;a href="http://github.com/ngerakines/erlang_protobuffs/tree/master"&gt;implementation&lt;/a&gt; of Erlang prototocol buffers, and the &lt;a href="http://nitrogenproject.com/"&gt;Nitrogen Web Framework.&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;P.P.S. I'm going on holiday for 2 weeks, so if I don't reply to comments, please be patient. I will do so when I get back...&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://nitrogenproject.com/"&gt; &lt;/a&gt;</description><link>http://21ccw.blogspot.com/2009/08/my-google-wave-client.html</link><author>noreply@blogger.com (Benjamin Nortier)</author><thr:total>11</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-7435412383881764254.post-6202057611761079672</guid><pubDate>Wed, 08 Jul 2009 09:04:00 +0000</pubDate><atom:updated>2009-07-08T02:22:09.355-07:00</atom:updated><category domain='http://www.blogger.com/atom/ns#'>HTML5</category><category domain='http://www.blogger.com/atom/ns#'>Google OS</category><category domain='http://www.blogger.com/atom/ns#'>Google Chrome</category><title>Google OS - I believe I was right</title><description>&lt;div&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;disclosure: I own Google shares&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;I'm not one for blowing my own trumpet, but sometimes it feels good to be right. A month ago I &lt;a href="http://21ccw.blogspot.com/2009/06/look-out-rias-html5-is-coming.html"&gt;blogged&lt;/a&gt; about HTML5, and I said the following:&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;"I believe it is part of Google's long term strategy to make the browser the de-facto application platform. To displace Windows as the dominant aplication platform. You can see it already with Chrome, how the tabs on the browser have become the new taskbar, and the integration of browser tabs as "applications" into the host OS. For example, features like local file storage and the canvas provide a path for Google Docs to become more like MS Office. You can be sure that some of the drive behind the HTML5 standard is coming from the Google docs team."&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;&lt;br /&gt;&lt;/i&gt;&lt;/div&gt;&lt;div&gt;Yesterday Google announced Google OS: &lt;a href="http://googleblog.blogspot.com/2009/07/introducing-google-chrome-os.html"&gt;http://googleblog.blogspot.com/2009/07/introducing-google-chrome-os.html&lt;/a&gt;. Google OS will be based on Chrome and a Linux kernel. &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;From the announcement:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;"For application developers, the web is the platform. All web-based applications will automatically work and new applications can be written using your favorite web technologies. And of course, these apps will run not only on Google Chrome OS, but on any standards-based browser on Windows, Mac and Linux thereby giving developers the largest user base of any platform."&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;&lt;br /&gt;&lt;/i&gt;&lt;/div&gt;&lt;div&gt;I will admit that I didn't think they would actually write an OS to support this strategy, but the strategy is clear. Unfortunately we'll have to wait at least a year to see it in action...&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;&lt;br /&gt;&lt;/i&gt;&lt;/div&gt;</description><link>http://21ccw.blogspot.com/2009/07/google-os-i-believe-i-was-right.html</link><author>noreply@blogger.com (Benjamin Nortier)</author><thr:total>0</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-7435412383881764254.post-5669561164847906526</guid><pubDate>Thu, 02 Jul 2009 20:32:00 +0000</pubDate><atom:updated>2009-07-05T02:27:32.029-07:00</atom:updated><category domain='http://www.blogger.com/atom/ns#'>Erlang</category><category domain='http://www.blogger.com/atom/ns#'>Erlang factory</category><title>Erlang Factory 2009 - New Kids on the Erlang Block</title><description>&lt;div style="text-align: left;"&gt;I went to Erlang Factory again this year thanks to my &lt;a href="http://www.zuhlke.co.uk/"&gt;employer&lt;/a&gt;, and it was even more exciting than &lt;a href="http://21ccw.blogspot.com/2008/07/notes-from-erlang-exchange.html"&gt;last year's&lt;/a&gt; event. There were some great speakers (Joe Armstrong - entertaining as ever, Simon Peyton Jones), I got to meet some interesting people (e.g. Rusty Klophaus, creator of the Nitrogen web framework) and I got to see some awesome projects. Although Erlang is experiencing a surge of interest, it is by no means "new". it has been used for years in robust and highly scalable applications. That is why I refer to the "new" - as opposed to the solid Erlang VM, tools, libraries and frameworks that have been around for at least a decade.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;So, wo are these the New Kids on the Block? These guys?&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;img src="http://1.bp.blogspot.com/_bXDgUoT7V7o/Sk4AeAT0K7I/AAAAAAAAANA/5lDV0iMS_V8/s400/newkids3.jpg" border="0" alt="" id="BLOGGER_PHOTO_ID_5354217522469284786" style="display: block; margin-top: 0px; margin-right: auto; margin-bottom: 10px; margin-left: auto; text-align: center; cursor: pointer; width: 320px; height: 320px; " /&gt;&lt;/div&gt;&lt;div style="text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="text-align: left;"&gt;Nice hair! No, not them. Here are some of the New Kids on the Erlang Block:&lt;/div&gt;&lt;div style="text-align: left;"&gt;&lt;div style="text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="text-align: left;"&gt;&lt;span class="Apple-style-span" style="color: rgb(0, 0, 238); -webkit-text-decorations-in-effect: underline; "&gt;&lt;img src="http://3.bp.blogspot.com/_bXDgUoT7V7o/Sk4EEAp6KfI/AAAAAAAAANo/dSLaROLb6oA/s400/collage.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5354221473931864562" style="display: block; margin-top: 0px; margin-right: auto; margin-bottom: 10px; margin-left: auto; text-align: center; cursor: pointer; width: 400px; height: 332px; " /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="text-align: center;"&gt;&lt;span class="Apple-style-span" style="color: rgb(0, 0, 238); -webkit-text-decorations-in-effect: underline; "&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="text-align: left;"&gt;&lt;a href="http://heroku.com/"&gt;Heroku&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;Heroku is a platform for deploying Ruby or Rails web apps. They've built their routing mesh in Erlang. This &lt;a href="http://heroku.com/how/architecture"&gt;routing mesh&lt;/a&gt; is used to load-balance and route requests to their application work units (dynos). This is a really cool piece of cloud infrastructure.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;a href="http://www.hypernumbers.com/"&gt;HyperNumbers&lt;/a&gt;&lt;/div&gt;&lt;div&gt;HyperNumbers is a startup that is "doing for numbers what hypertext did for text". Think GoogleDocs, but each cell can be a resource that's located on another spreadsheet. Coming soon.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;a href="http://couchdb.apache.org/"&gt;CouchDB&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;a href="http://couchdb.apache.org/"&gt;&lt;/a&gt;CouchDB is a non-relational document database written in Erlang, and it is going from strength to strength. Look at this &lt;a href="http://www.google.com/trends?q=couchdb"&gt;graph&lt;/a&gt; from Google Trends:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;img src="http://3.bp.blogspot.com/_bXDgUoT7V7o/Sk4BZxgLNvI/AAAAAAAAANQ/y_IRGWiCzsA/s400/couchdd_trend.png" style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 203px;" border="0" alt="" id="BLOGGER_PHOTO_ID_5354218549286745842" /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;If you don't know what CouchDB is, you should definitely have a look it. Even is it's just to make you challenge you preconceptions around databases, and relational ones in particular. There is also a &lt;a href="http://books.couchdb.org/relax/"&gt;book &lt;/a&gt;coming out soon. It's open source.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;a href="http://www.rabbitmq.com/"&gt;RabbitMQ&lt;/a&gt;&lt;/div&gt;&lt;div&gt;RabbitMQ is an open source, high-performance enterprise messaging system, and an implementation of the messaging standard AQMP. It is written in Erlang. I've heard rumors that ActiveMQ (Java) has 20 times more lines of code. (NB: If anyone can give me a reference or link to proof I would be grateful). &lt;span class="Apple-style-span"  style="color:#FF0000;"&gt;Update: See comments for line counts.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;a href="http://nitrogenproject.com/"&gt;Nitrogen Web Framework&lt;/a&gt;&lt;/div&gt;&lt;div&gt;This is my favourite web framework. It's a real Web 2.0 framework, and it's highly productive. Some of the &lt;a href="http://nitrogenproject.com/web/learn"&gt;main features&lt;/a&gt; are:&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;Event-Driven Development&lt;/li&gt;&lt;li&gt;Brainlessly Easy Ajax&lt;/li&gt;&lt;li&gt;Ridiculously Simple Comet&lt;/li&gt;&lt;li&gt;Complex Interfaces: Dragging, Dropping, and Sorting&lt;/li&gt;&lt;li&gt;Flexible Templating&lt;/li&gt;&lt;li&gt;Data Binding&lt;/li&gt;&lt;li&gt;Erlang Power&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;I've used this to create my own pet Erlang project, &lt;a href="http://www.opinion8r.com/"&gt;opinion8r.com&lt;/a&gt;. Check it out. It's also open source.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;a href="http://www.smarkets.com/"&gt;Smarkets&lt;/a&gt;&lt;/div&gt;&lt;div&gt;I didn't go to this talk, but I've been to one given last year at the Erlang London User group. Smarkets is an online betting exchange which allows users to bet on anything they wish. It is implemented in Erlang and close to launch.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;a href="http://campfirenow.com/"&gt;Campfire&lt;/a&gt;&lt;/div&gt;&lt;div&gt;Campfire is one of the applications from the guys at &lt;a href="http://www.37signals.com/"&gt;37Signals&lt;/a&gt;. If you don't know who 37 Signals are, they created Ruby on Rails. The functional C polling for Campfire service that was being used was replaced by an Erlang implementation:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(0, 0, 238); -webkit-text-decorations-in-effect: underline; "&gt;&lt;img src="http://1.bp.blogspot.com/_bXDgUoT7V7o/Sk4CNBqc-_I/AAAAAAAAANY/Phjz8_7XTfI/s400/campfire.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5354219429798149106" style="display: block; margin-top: 0px; margin-right: auto; margin-bottom: 10px; margin-left: auto; text-align: center; cursor: pointer; width: 400px; height: 155px; " /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The Erlang implementation is as fast as the C implementation, modular and extensible, and much easier to administer with 1 OS process instead of 80.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;a href="http://www.process-one.net/en/"&gt;ProcessOne&lt;/a&gt; - OneTeam Media Server&lt;/div&gt;&lt;div&gt;"OneTeam Media Server is a new project launched by ProcessOne which is a Media server for Flash clients implementing the FLV and RTMP protocol. The project is designed so that it can work hand in hand with ejabberd, ProcessOne Instant Messaging platform."&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;This one is very exciting. The media server has Flash in the front end, and Erlang in the back. It makes thing like writing video chat in the browser ridiculously easy. And it's open source! Look out for it in July.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;a href="http://code.google.com/p/webmachine/"&gt;Webmachine&lt;/a&gt;&lt;/div&gt;&lt;div&gt;Webmachine is the best way to implement a REST interface to your Erlang application that I've seen to date. I've &lt;a href="http://21ccw.blogspot.com/2008/06/migrating-native-erlang-interface-to.html"&gt;blogged&lt;/a&gt; about REST and Erlang in the past, but instead of rolling your own, you should look at this. Especially considering how it handles all the response codes correctly, does all the correct caching responses etc. Another open source project.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-size:large;"&gt;And the point is...?&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I think it's a very positive indicator for the future of Erlang that it is being used in these new and exciting areas and companies. It is performing very well in the field of high-scalability infrastructure. It is well suited to Cloud computing. But most importantly, &lt;i&gt;&lt;b&gt;very clever and innovative people are using it and loving i&lt;/b&gt;&lt;b&gt;t&lt;/b&gt;&lt;/i&gt;. That, above all else, means the future looks good for Erlang.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;P.S. I'm sure I missed some great talks again this year, but lucky for me and you, you can find the talks and videos &lt;a href="http://www.erlang-factory.com/conference/London2009/talks"&gt;here&lt;/a&gt;.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;</description><link>http://21ccw.blogspot.com/2009/07/erlang-factory-2009-new-kids-on-erlang.html</link><author>noreply@blogger.com (Benjamin Nortier)</author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_bXDgUoT7V7o/Sk4AeAT0K7I/AAAAAAAAANA/5lDV0iMS_V8/s72-c/newkids3.jpg' height='72' width='72'/><thr:total>4</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-7435412383881764254.post-8818275353199237827</guid><pubDate>Fri, 05 Jun 2009 22:50:00 +0000</pubDate><atom:updated>2009-06-05T16:02:29.895-07:00</atom:updated><category domain='http://www.blogger.com/atom/ns#'>Google Wave</category><category domain='http://www.blogger.com/atom/ns#'>RIA</category><category domain='http://www.blogger.com/atom/ns#'>HTML5</category><title>Look out RIAs, HTML5 is coming</title><description>&lt;span style="font-family:sans-serif;"&gt;Google showing off &lt;a href="http://googlesystem.blogspot.com/2009/05/google-wave.html"&gt;Wave&lt;/a&gt;, has [re]ignited some discussion around HTML5. I read an article on InfoQ, &lt;a href="http://www.infoq.com/news/2009/06/Wave-Silverlight"&gt;Is Google Wave Going to Have an Impact on RIA/Silverlight?&lt;/a&gt; Which struck me as a bit of an odd question. Tim Heuer&lt;/span&gt;&lt;span style="font-family:sans-serif;"&gt; (in the Silverlight camp) is correct in asking  "&lt;a href="http://timheuer.com/blog/archive/2009/05/30/google-wave-forces-out-silverlight-flash-ria-platforms.aspx"&gt;is HTML5 really what people are talking about here?&lt;/a&gt;" and towards the end of the article, the correct question is polled:&lt;br /&gt;&lt;br /&gt;&lt;i&gt;"What negative impact will HTML 5/Wave going to have on RIA/Silverlight?" [sic]&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;At the time the results of the poll were:&lt;br /&gt;&lt;br /&gt;Major: 66&lt;br /&gt;Significant: 115&lt;br /&gt;Small: 112&lt;br /&gt;None: 43&lt;br /&gt;Total votes: 336&lt;br /&gt;&lt;br /&gt;I was a bit surprised by the poll results. I voted "Major", and I find it hard to understand how someone could vote "None". None?! Really? I do realise that this is partly due to the fact that most people, including me, think they are above average (I just discovered that it is also called the &lt;a href="http://en.wikipedia.org/wiki/Lake_Wobegon_effect"&gt;Lake Wobegon&lt;/a&gt; effect. Cute). There is also some confusion about including Wave in the question, which has nothing to do with the issue. I'll try and convince you of the potential impact...&lt;br /&gt;&lt;span style="font-size:130%;"&gt;&lt;br /&gt;Why RIAs exist&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The existence of &lt;a href="http://en.wikipedia.org/wiki/Rich_Internet_application"&gt;Rich Internet Applications&lt;/a&gt; can be explained by the word "Rich" in the name. At some point in the past few years, people started to realise that HTML didn't keep up with the explosion in bandwidth and processing power available to users, and that the web experience could be "richer". This was especially obvious in areas such as not having the ability to to arbitrary on-screen animantions and rendering on a "canvas", the ability to store data locally on user's hard drives and drag-and-drop, amongst others. The lack of these features in HTML created a gap in the market, but only whilst browser technology continues to lag. This gap has been exploited by Flex, Silverlight and JavaFX.&lt;br /&gt;&lt;br /&gt;&lt;b style="font-style: italic;"&gt;This situation is changing.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Enter HTML5&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Some of the features in HTML5 will add some really exciting possibilities to applications running on the browser platform:&lt;br /&gt;&lt;span style="font-size:100%;"&gt;&lt;br /&gt;&lt;b&gt;Video&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Although I wouldn't categorise video as a major driver for RIA technologies, it is important to mention in relation to Adobe flash, which is the dominant technology for delivering internet video. YouTube is already delivering non-flash internet video in mobile devices. The introduction of the &lt;video&gt;&lt;/video&gt; tag will reduce the pressure of deploying flash on every platform, which could have an effect on the usage of Flex. Consider that Flash is not available on the iPhone: if you have a choice of developing your application on just one technology (HTML5) for both desktop and mobile, or choosing 2, which would cost less?&lt;br /&gt;&lt;span style="font-size:100%;"&gt;&lt;br /&gt;&lt;b&gt;Canvas drawing (immediate mode drawing)&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Canvas drawing will enable application developers to fill a gaping hole in native browser capabilities - gaming. The gaming industry is bigger than the movie industry, and the availability of a virtually universally available standard platform will not got unnoticed by game developers. Of course this capability doesn't only apply to gaming.&lt;br /&gt;&lt;div style="font-weight: bold;"&gt;&lt;span style="font-size:100%;"&gt;&lt;br /&gt;Local storage&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;span style="font-size:100%;"&gt;&lt;b&gt;&lt;span style="font-weight: normal;"&gt;As long as there's no capability for storing data offline, web applications continue to be considered the poor cousin of desktop applications. Google documents are not availble offline without &lt;a href="http://gears.google.com/"&gt;Google Gears&lt;/a&gt;. Having local storage available in a &lt;/span&gt;&lt;i&gt;&lt;span style="font-weight: normal;"&gt;standardised&lt;/span&gt;&lt;/i&gt;&lt;/b&gt;&lt;/span&gt;&lt;b&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-weight: normal;"&gt;&lt;span style="font-size:100%;"&gt; way is a big attraction for application developers. Gears might work for Gooogle, but not everyone is so keen on having a dependency on a non-standard technology.&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Some other disavantages to RIAs which could favour HTML5 applications&lt;/span&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;- RIAs are usually everything or nothing. They don't play well with embedding RIA functionality into existing web pages. HTML5 allows you to grow your application, creating a richer and richer experience as you go along.&lt;br /&gt;- Some RIAs have proprietary technologies.&lt;br /&gt;&lt;/div&gt;&lt;div&gt;- RIAs require an extra &lt;b&gt;deployment&lt;/b&gt; step on top of deploying the browser. Browsers implementing HTML5 will provide a more standardised way of deploying applications to users, also in the corporate environment where locked-down systems are common. Choosing a technology that doesn't require another installation step is a big attraction for corporate IT departments.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;In the long term&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;There another significant point to make.&lt;br /&gt;&lt;br /&gt;I believe it is part of Google's long term strategy to make the browser the de-facto application platform. &lt;b&gt;To displace Windows as the dominant aplication platform&lt;/b&gt;. You can see it already with Chrome, how the tabs on the browser have become the new taskbar, and the integration of browser tabs as "applications" into the host OS. For example, features like local file storage and the canvas provide a path for Google Docs to become more like MS Office. You can be sure that some of the drive behind the HTML5 standard is coming from the Google docs team. &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I'm not the only one thinking along these lines: &lt;a href="http://industry.bnet.com/technology/10002039/googles-long-shot-at-kicking-microsoft-off-the-desktop/"&gt;http://industry.bnet.com/technology/10002039/googles-long-shot-at-kicking-microsoft-off-the-desktop/&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;In closing&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;I'm not trying to say that HTML5 will eliminate RIA technologies, but I do believe that thinking that its impact on RIA will be "small" or "none" is absolutely ridiculous.&lt;br /&gt;&lt;br /&gt;There are some big players pushing the browser to become the dominant application platform. HTML5 is a big step in that direction.&lt;br /&gt;&lt;br /&gt;If you disagree, I look forward to your comments!&lt;br /&gt;&lt;/span&gt;</description><link>http://21ccw.blogspot.com/2009/06/look-out-rias-html5-is-coming.html</link><author>noreply@blogger.com (Benjamin Nortier)</author><thr:total>2</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-7435412383881764254.post-1553935460548581277</guid><pubDate>Wed, 20 May 2009 18:29:00 +0000</pubDate><atom:updated>2009-05-20T13:00:31.575-07:00</atom:updated><title>The Law of Conservation of Misery</title><description>At university I had a professor who would jokingly refer to his Law of Conservation of Misery:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;The total amount of misery in a system is constant&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;which implies that when you try and decrease the misery in one aspect of a system, you will increase the misery in some of the other aspects.&lt;br /&gt;&lt;br /&gt;There are many types of misery. In software engineering projects these could include non-maintainability, non-performance and budget constraints.&lt;br /&gt;&lt;br /&gt;Personally, I think people are too prone to think that there are &lt;a href="http://en.wikipedia.org/wiki/Zero-sum"&gt;zero-sum&lt;/a&gt; monsters lurking under each bed, so my own take on the "law" would be:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;The total amount of misery in a system is &gt; 0&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;&lt;/span&gt;&lt;/span&gt;Which implies that you &lt;span style="font-weight: bold;"&gt;can&lt;/span&gt; make tradeoffs that decrease the total amount of misery&lt;span style="font-style: italic;"&gt;, &lt;/span&gt;but you can never eliminate it completely. A bad implementation of a system will have a higher total amount of misery, and a good one will have less, but you will always have at least a little bit!</description><link>http://21ccw.blogspot.com/2009/05/law-of-conservation-of-misery.html</link><author>noreply@blogger.com (Benjamin Nortier)</author><thr:total>0</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-7435412383881764254.post-856792514420372731</guid><pubDate>Tue, 19 May 2009 18:19:00 +0000</pubDate><atom:updated>2009-05-19T11:42:07.050-07:00</atom:updated><category domain='http://www.blogger.com/atom/ns#'>smtp</category><category domain='http://www.blogger.com/atom/ns#'>Erlang</category><title>How to send email via Gmail using Erlang</title><description>One of my pet projects, &lt;a href="http://www.dayfindr.com"&gt;www.dayfindr.com&lt;/a&gt;, integrates with email to send notifications to users.&lt;br /&gt;&lt;br /&gt;I use &lt;a href="http://www.blogger.com/www.google.com/apps%20%20"&gt;Google Apps&lt;/a&gt; for email infrastructure, so you need an SMTP client that supports &lt;a href="http://en.wikipedia.org/wiki/Secure_Sockets_Layer"&gt;TLS&lt;/a&gt;. At the time, I couldn't find a simple Erlang SMTP client that could handle TLS, so I used a command-line SMTP client.&lt;br /&gt;&lt;br /&gt;For my new pet project, for want of a better name temporarily called  &lt;a href="http://www.theisabelleproject.com/"&gt;The Isabelle Project&lt;/a&gt;, I need to add some email functionality. This time I would prefer to use an Erlang solution with proper error handling and logging.&lt;br /&gt;&lt;br /&gt;I looked at the &lt;a href="http://en.wikipedia.org/wiki/Simple_Mail_Transfer_Protocol"&gt;SMTP&lt;/a&gt; protocol on Wikipedia, and it didn't seem to difficult. Erlang's built-in ssl module also seemed to support TLS. So, with a bit of trial and error, here's the result:&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;br /&gt;-module(smtp).&lt;br /&gt;-export([connect/0]).&lt;br /&gt;&lt;br /&gt;connect() -&gt;&lt;br /&gt;    {ok, Socket} = ssl:connect("smtp.gmail.com", 465, [{active, false}], 1000),&lt;br /&gt;    recv(Socket),&lt;br /&gt;    send(Socket, "HELO localhost"),&lt;br /&gt;    send(Socket, "AUTH LOGIN"),&lt;br /&gt;    send(Socket, binary_to_list(base64:encode("___@gmail.com"))),&lt;br /&gt;    send(Socket, binary_to_list(base64:encode("johngalt"))),&lt;br /&gt;    send(Socket, "MAIL FROM: &lt;___@gmail.com&gt;"),&lt;br /&gt;    send(Socket, "RCPT TO:&lt;___@gmail.com&gt;"),&lt;br /&gt;    send(Socket, "DATA"),&lt;br /&gt;    send_no_receive(Socket, "From: &lt;___@gmail.com&gt;"),&lt;br /&gt;    send_no_receive(Socket, "To: &lt;___@gmail.com&gt;"),&lt;br /&gt;    send_no_receive(Socket, "Date: Tue, 15 Jan 2008 16:02:43 +0000"),&lt;br /&gt;    send_no_receive(Socket, "Subject: Test message"),&lt;br /&gt;    send_no_receive(Socket, ""),&lt;br /&gt;    send_no_receive(Socket, "This is a test"),&lt;br /&gt;    send_no_receive(Socket, ""),&lt;br /&gt;    send(Socket, "."),&lt;br /&gt;    send(Socket, "QUIT"),&lt;br /&gt;    ssl:close(Socket).&lt;br /&gt;&lt;br /&gt;send_no_receive(Socket, Data) -&gt;&lt;br /&gt;    ssl:send(Socket, Data ++ "\r\n").&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;send(Socket, Data) -&gt;&lt;br /&gt;    ssl:send(Socket, Data ++ "\r\n"),&lt;br /&gt;    recv(Socket).&lt;br /&gt;&lt;br /&gt;recv(Socket) -&gt;&lt;br /&gt;    case ssl:recv(Socket, 0, 1000) of&lt;br /&gt; {ok, Return} -&gt; io:format("~p~n", [Return]);&lt;br /&gt; {error, Reason} -&gt; io:format("ERROR: ~p~n", [Reason])&lt;br /&gt;    end.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;And the output from the Erlang shell:&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;br /&gt;3&gt; application:start(ssl).&lt;br /&gt;ok&lt;br /&gt;4&gt; smtp:connect().&lt;br /&gt;"220 mx.google.com ESMTP y37sm613282mug.19\r\n"&lt;br /&gt;"250 mx.google.com at your service\r\n"&lt;br /&gt;"334 VXNlcm5hbWU6\r\n"&lt;br /&gt;"334 UGFzc3dvcmQ6\r\n"&lt;br /&gt;"235 2.7.0 Accepted\r\n"&lt;br /&gt;"250 2.1.0 OK y37sm613282mug.19\r\n"&lt;br /&gt;"250 2.1.5 OK y37sm613282mug.19\r\n"&lt;br /&gt;"354  Go ahead y37sm613282mug.19\r\n"&lt;br /&gt;"250 2.0.0 OK 1242683885 y37sm613282mug.19\r\n"&lt;br /&gt;"221 2.0.0 closing connection y37sm613282mug.19\r\n"&lt;br /&gt;ok&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The only tricky bit is that for the AUTO LOGIN, the received text and the username and password you send is base-64 encoded. By default the connect is active=false, which means the responses are send to the creating process directly. Using passive mode requires explicit receiving of the response using ssl:recv/2&lt;br /&gt;&lt;br /&gt;You'll have to handle errors better if you use this in production, but the basic protocol is pretty straightforward...</description><link>http://21ccw.blogspot.com/2009/05/how-to-send-email-via-gmail-using.html</link><author>noreply@blogger.com (Benjamin Nortier)</author><thr:total>5</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-7435412383881764254.post-7203639609804814726</guid><pubDate>Fri, 02 Jan 2009 11:31:00 +0000</pubDate><atom:updated>2009-01-06T04:16:50.140-08:00</atom:updated><category domain='http://www.blogger.com/atom/ns#'>Object Oriented Programming</category><title>Is a Square a Rectangle?</title><description>I pre-ordered the &lt;a href="http://clojure.org/"&gt;Clojure&lt;/a&gt; &lt;a href="http://www.pragprog.com/titles/shcloj/programming-clojure"&gt;book&lt;/a&gt; today, so I downloaded Clojure and had a peek at something that I know is interesting - "multimethods". I came across the following example statement:&lt;br /&gt;&lt;br /&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;(derive ::square ::rect)&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;i.e. a Square IS-A Rectangle in the Object-Oriented sense.&lt;span class="Apple-style-span" style="font-style: italic;"&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-style: italic;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;This rang a bell for me. Not a nice melodious one, but one that's a little off-key. A good teacher asked some colleagues and me the question "Is a Square a Rectangle?" a few years ago, and the answer wasn't all that clear ti us. But he showed us a realitively easy way to determine to some extent the validity of any IS-A relationship.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I will try and show you this principle by way of actually designing this hierarchy. First we design a simple Rectangle in Ruby:&lt;/div&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;br /&gt;class RectangleTest &amp;lt; Test::Unit::TestCase&lt;br /&gt;  def test_width_height&lt;br /&gt;&lt;br /&gt;    # Given&lt;br /&gt;    r = Rectangle.new(1,1)&lt;br /&gt;&lt;br /&gt;    # Then&lt;br /&gt;    assert_equal 1, r.width&lt;br /&gt;    assert_equal 1, r.height&lt;br /&gt;&lt;br /&gt;    # When&lt;br /&gt;    r.width = 5&lt;br /&gt;&lt;br /&gt;    # Then&lt;br /&gt;    assert_equal 5, r.width&lt;br /&gt;    assert_equal 1, r.height&lt;br /&gt;&lt;br /&gt;    # When&lt;br /&gt;    r.height = 2&lt;br /&gt;    &lt;br /&gt;    # Then&lt;br /&gt;    assert_equal 5, r.width&lt;br /&gt;    assert_equal 2, r.height&lt;br /&gt;&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;div&gt;If you think it's strange that I use a test as a design, I suggest you read &lt;a href="http://en.wikipedia.org/wiki/Test-driven_development"&gt;this&lt;/a&gt;. I'm not even going to show the implementation since it's so simple. Next we will design our Square class:&lt;/div&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;br /&gt;class SquareTest &amp;lt; Test::Unit::TestCase&lt;br /&gt;  def test_width_height&lt;br /&gt;&lt;br /&gt;    # Given&lt;br /&gt;    s = Square.new(3)&lt;br /&gt;&lt;br /&gt;    # Then&lt;br /&gt;    assert_equal 3, s.width&lt;br /&gt;    assert_equal 3, s.height&lt;br /&gt;&lt;br /&gt;    # When&lt;br /&gt;    s.width = 5&lt;br /&gt;&lt;br /&gt;    # Then&lt;br /&gt;    assert_equal 5, s.width&lt;br /&gt;    assert_equal 5, s.height&lt;br /&gt;&lt;br /&gt;    # When&lt;br /&gt;    s.height = 2&lt;br /&gt;    &lt;br /&gt;    # Then&lt;br /&gt;    assert_equal 2, s.width&lt;br /&gt;    assert_equal 2, s.height&lt;br /&gt;&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;div&gt;And you can see that we make sure that when either the width or height has been set, we get the same value for both the width and height respectively. The same goes for the single-parameter constructor. Again, the implementation is trivial. &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Now, you might think that this design is &lt;span class="Apple-style-span" style="font-style: italic;"&gt;OK&lt;/span&gt;, but it's not. We have to ensure that our inheritance doesn't break the &lt;a href="http://en.wikipedia.org/wiki/Liskov_substitution_principle"&gt;Liskov Substritution Principle.&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;From Wikipedia:&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-style: italic; "&gt;"Liskov's notion of 'subtype' is based on the notion of substitutability; that is, if S is a subtype of T, then objects of type T in a program may be replaced with objects of type S without altering any of the desirable properties of that program (e.g., correctness)."&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-style: italic;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;This means that for our designs, the unit test for the Rectangle should pass when I substitute the Rectangle instance with a Square instance. Will it? No it won't. Because in the rectangle test, we ensure that when the width is set, the height is not affected, and vice versa. Using a Square instance here will break the test.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Another way of looking at it is to think of the contract of the setters, and the pre and post conditions of those contracts. According to the Liskov princple, (again, from Wikipedia):&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;&lt;span class="Apple-style-span" style="font-style: italic;"&gt;Preconditions cannot be strengthened in a subclass.&lt;br /&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span class="Apple-style-span" style="font-style: italic;"&gt;Postconditions cannot be weakened in a subclass.&lt;/span&gt;&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;If we design the Square to force the width to be set when setting the height (and vice versa), we are &lt;span class="Apple-style-span" style="font-style: italic;"&gt;weakening&lt;/span&gt; the postconditions of the Rectangle methods. I.e. the post condition that states that either the width or height should &lt;span class="Apple-style-span" style="font-style: italic;"&gt;not&lt;/span&gt; be altered when setting the height or width respectively has been weakened.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;In conclusion, here we have a Square that is NOT a Rectangle. The lesson to be learnt is that (unfortunately for us), IS-A in the real world or other domains does not necessarily imply IS-A in the OO world! &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;P.S. If you remove the setters from the Rectangle, and only allow the fields to be set during construction, there is no violation of the Liskov Substitution Principle :)&lt;/div&gt;&lt;/div&gt;</description><link>http://21ccw.blogspot.com/2009/01/is-square-rectangle.html</link><author>noreply@blogger.com (Benjamin Nortier)</author><thr:total>14</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-7435412383881764254.post-8710392401372419187</guid><pubDate>Wed, 12 Nov 2008 09:07:00 +0000</pubDate><atom:updated>2008-11-18T14:00:19.438-08:00</atom:updated><category domain='http://www.blogger.com/atom/ns#'>Keynote</category><category domain='http://www.blogger.com/atom/ns#'>Presentations</category><category domain='http://www.blogger.com/atom/ns#'>Powerpoint</category><title>Does Robert Mugabe use Powerpoint?</title><description>I think most people will agree that &lt;a href="http://en.wikipedia.org/wiki/Robert_Mugabe"&gt;Robert Mugabe&lt;/a&gt; is a terrible dictator who has scant disregard for the people he is supposed to be serving. The same goes for the likes of Stalin, Hitler, Idi Amin etc. That being said, I think most people will also agree that one of the reasons of their rise to power was specifically their ability to influence people. In other words&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-style: italic;"&gt;People listen[ed] to them, and they're not using Powerpoint&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;My experience is that every time someone want to &lt;span class="Apple-style-span" style="font-style: italic;"&gt;say something&lt;/span&gt; to more than one person, there has to be a Powerpoint presentation to go along with it. And more often than not, this attempt at communication fails miserably, or succeeds only in some rudimentary fashion. &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Some of the causes of these failures are:&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;People are reading the slides, so they're not listening to the speaker.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;The presenter is reading the slides, so there is no connection with the audience.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;The bullet-pointed slides are so mind-numbingly &lt;span class="Apple-style-span" style="font-style: italic;"&gt;boring&lt;/span&gt; that the audience members have gone to their happy place or are thinking of what to cook/order/kill for dinner and of course, they're &lt;span class="Apple-style-span" style="font-style: italic;"&gt;not listening.&lt;/span&gt;&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;In contrast, I'll mention two very successful entrepeneurs and their presentation styles: &lt;a href="http://en.wikipedia.org/wiki/Steve_Jobs"&gt;Steve Jobs&lt;/a&gt; (Apple, Pixar, &lt;a href="http://www.nytimes.com/2008/11/12/opinion/12friedman.html?_r=1&amp;amp;partner=permalink&amp;amp;exprod=permalink&amp;amp;oref=slogin"&gt;General Motors?&lt;/a&gt;) and &lt;a href="http://en.wikipedia.org/wiki/Elon_Musk"&gt;Elon Musk&lt;/a&gt; (PayPal, Tesla Motors, SpaceX). In business it is absolutely essential that you can effectively communicate your ideas to people. And when you audience is holding the purse strings, and you can't convince them of your business ideas, you're doomed to become a (perhaps very successful) hobbyist.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I went to a talk by Elon Musk on his company &lt;a href="http://www.spacex.com/"&gt;SpaceX&lt;/a&gt; a few months ago. The projector had been set up and the presentation was ready to proceed. The first slide was already on the screen, with text along the lines of "Elon Musk, SpaceX" with very nice picture of a rocket (on a Pacific island if I remember correctly). &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Elon Musk came in and talked a bit. After a few minutes he proceeded to questions, and these went on for more than an hour. The first slide was the &lt;span class="Apple-style-span" style="font-style: italic;"&gt;only&lt;/span&gt; slide. Maybe the host created it when he realised there were no slides. &lt;span class="Apple-style-span" style="font-style: italic;"&gt;"Are you crazy? &lt;/span&gt;&lt;span class="Apple-style-span" style=""&gt;&lt;span class="Apple-style-span" style="font-style: italic;"&gt;No slides?! But how will you&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-style: italic;"&gt;...."&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The talk was very effective, he had communicated his points, and gave the audience exactly what they wanted, since they were asking him what they wanted to know!&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The second example is Steve Jobs. I remember watching the first iPhone Keynote speech and how I got swept up in the presentation. It was a highly effective communication (helped by a great product). Let's look at some of the slides he used:&lt;/div&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;img src="http://1.bp.blogspot.com/_bXDgUoT7V7o/SRws-5svzNI/AAAAAAAAAIM/WwqN3676wwM/s400/dsc_0236.jpg" style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 265px;" border="0" alt="" id="BLOGGER_PHOTO_ID_5268135123268652242" /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_bXDgUoT7V7o/SRwtIIPj6FI/AAAAAAAAAIU/2bn3R_0PMxo/s1600-h/dsc_0245.jpg"&gt;&lt;br /&gt;&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 265px;" src="http://2.bp.blogspot.com/_bXDgUoT7V7o/SRwtIIPj6FI/AAAAAAAAAIU/2bn3R_0PMxo/s400/dsc_0245.jpg" border="0" alt="" id="BLOGGER_PHOTO_ID_5268135281791592530" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;img src="http://1.bp.blogspot.com/_bXDgUoT7V7o/SRwtNIvaHWI/AAAAAAAAAIc/uURI3AxDKOE/s400/dsc_0175.jpg" style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 265px;" border="0" alt="" id="BLOGGER_PHOTO_ID_5268135367824514402" /&gt;&lt;br /&gt;Do you see lots of bullet points? The last slide has the most text on it, but that slide is in the minority, and the bullets are actually very short. The first two slides had simple images, and minimal text, and I can assure you that people were &lt;span class="Apple-style-span" style="font-style: italic;"&gt;listening&lt;/span&gt; to what he was saying!&lt;div&gt;&lt;br /&gt;&lt;/div&gt;Do you think the response would have been the same if his slides looked like this:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_bXDgUoT7V7o/SRwuDpzhL7I/AAAAAAAAAIk/aTBoI-Ic_Uc/s1600-h/boring.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 282px;" src="http://2.bp.blogspot.com/_bXDgUoT7V7o/SRwuDpzhL7I/AAAAAAAAAIk/aTBoI-Ic_Uc/s400/boring.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5268136304413061042" /&gt;&lt;/a&gt;&lt;div&gt;instead? I'm asleep already.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;My highly opinionated advice for presentations would be:&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;Don't use PowerPoint/Keynote/[insert slide software here] to write your presentation. Decide what you want to say and how you want it to flow and only prepare your slides once you know the content of your presentation. &lt;br /&gt;&lt;/li&gt;&lt;li&gt;Don't use your slides as notes, have seperate notes if you don't know the topic that well or need some backup information.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Don't read you slides.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Don't have lots of bullet points on each slide.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 0, 0);"&gt;You don't &lt;/span&gt;&lt;span class="Apple-style-span" style="font-style: italic; "&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 0, 0);"&gt;need&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 0, 0);"&gt; to use slides.&lt;/span&gt; Sometimes a white board is good enough, and in most cases it is probably better.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;What do you think?&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;</description><link>http://21ccw.blogspot.com/2008/11/does-robert-mugabe-use-powerpoint.html</link><author>noreply@blogger.com (Benjamin Nortier)</author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_bXDgUoT7V7o/SRws-5svzNI/AAAAAAAAAIM/WwqN3676wwM/s72-c/dsc_0236.jpg' height='72' width='72'/><thr:total>5</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-7435412383881764254.post-5384711924776737130</guid><pubDate>Mon, 08 Sep 2008 12:31:00 +0000</pubDate><atom:updated>2008-10-01T04:38:07.474-07:00</atom:updated><category domain='http://www.blogger.com/atom/ns#'>Software Development Process</category><category domain='http://www.blogger.com/atom/ns#'>Agile Development</category><category domain='http://www.blogger.com/atom/ns#'>Iterative Development</category><title>Iterative and Agile - the Venn Diagram of truth</title><description>&lt;div&gt;I'm seeing a bit of this lately:&lt;span class="Apple-tab-span" style="white-space:pre"&gt; &lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-style: italic;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-style: italic;"&gt;"Iterative software development is Agile software development."&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-style: italic;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;and I have to say that I disagree quite strongly. The Venn diagram actually looks more like this:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;img src="http://1.bp.blogspot.com/_bXDgUoT7V7o/SOI-BbG_ZmI/AAAAAAAAAIE/DfiQYsrWfI0/s400/agile_venn.png" style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" border="0" alt="" id="BLOGGER_PHOTO_ID_5251828309645289058" /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;This post has been prompted by two things. First, a few weeks ago a SCRUMmer made some comments along the following lines:&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;&lt;span class="Apple-style-span" style="font-style: italic; "&gt;"We don't allow changing the iteration plan once the iteration has begun" &lt;/span&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;span class="Apple-style-span" style="font-style: italic; "&gt;"All the requirements for the stories for the iteration must have been gathered before it starts."&lt;/span&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;span class="Apple-style-span" style="font-style: italic; "&gt;"If the customer want something else, they have to wait for the next iteration"&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;And some &lt;a href="http://www.infoq.com/news/2008/09/Short-Iterations-Mishkin-Berteig"&gt;articles&lt;/a&gt;/&lt;a href="http://agile-commentary.blogspot.com/2008/09/shorten-your-iteration.html"&gt;blogs&lt;/a&gt; about iteration lengths that jogged my memory about what I disagreed with. The root of my discomfort in equating iterative development to agile development is because&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-style: italic;"&gt;agile software development is a set of values, it is not a process. &lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;In fact, one of the core values of Agile development is to &lt;span class="Apple-style-span" style="font-style: italic;"&gt;not&lt;/span&gt; value you process too much. There are some processes that try to embody Agile values, and they have different interpretations and practicalities around those values, but these &lt;span class="Apple-style-span" style="font-style: italic;"&gt;processes&lt;/span&gt; are not what defines &lt;span class="Apple-style-span" style="font-style: italic;"&gt;Agile&lt;/span&gt; development. &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Here's a &lt;a href="http://agilemanifesto.org/"&gt;reminder&lt;/a&gt; of what it means to be agile:&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;&lt;span class="Apple-style-span" style="font-weight: bold;"&gt;Individuals and interactions&lt;/span&gt; over processes and tools &lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;span class="Apple-style-span" style="font-weight: bold;"&gt;Working software&lt;/span&gt; over comprehensive documentation &lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;span class="Apple-style-span" style="font-weight: bold;"&gt;Customer collaboration&lt;/span&gt; over contract negotiation &lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;span class="Apple-style-span" style="font-weight: bold;"&gt;Responding to change&lt;/span&gt; over following a plan &lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;It's very possible to do iterative development without embodying &lt;span class="Apple-style-span" style="font-style: italic;"&gt;any&lt;/span&gt; of these values. Treating each iteration like a waterfall with the usual waterfall suspects (requirements up front, little room for manoevering, opressive specification and documentation) is a case in point. And to me, valuing your plan over your customer by not responding to their needs is &lt;span class="Apple-style-span" style="font-style: italic;"&gt;not&lt;/span&gt; Agile.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;You are, of course, invited to disagree...&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;</description><link>http://21ccw.blogspot.com/2008/09/iterative-and-agile-venn-diagram-of.html</link><author>noreply@blogger.com (Benjamin Nortier)</author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_bXDgUoT7V7o/SOI-BbG_ZmI/AAAAAAAAAIE/DfiQYsrWfI0/s72-c/agile_venn.png' height='72' width='72'/><thr:total>1</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-7435412383881764254.post-4962382545561174872</guid><pubDate>Wed, 03 Sep 2008 16:29:00 +0000</pubDate><atom:updated>2008-09-08T05:08:51.419-07:00</atom:updated><category domain='http://www.blogger.com/atom/ns#'>Application Platform</category><category domain='http://www.blogger.com/atom/ns#'>DHTML</category><category domain='http://www.blogger.com/atom/ns#'>Google Chrome</category><title>Me and my dog on Google Chrome</title><description>Like every man and his dog I feel compelled to say something about &lt;a href="http://www.google.com/chrome"&gt;Chrome&lt;/a&gt;, Google's new browser. Most impressions I have read have been favourable, and my personal experience so far is very positive. Most reviews I have scanned have been thumbs-up, and have focussed on the usual suspects, i.e. rendering and javascript performance, stability, usability etc. etc.&lt;div style="text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;For me, there is something subtle and extremely powerful that comes with Chrome. It fields a combination of features with very powerful implications. A "whole being more that the sum of the parts" kind of situation.&lt;br /&gt;&lt;/div&gt;&lt;div style="text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;In the past year or two there has been a lot of focus in the "richer" web experience, and on ways of getting the web to feel more like the desktop. Newish technologies like Flex and Silverlight have received significant attention, and DHTML (Javascript+CSS+DOM+HTML) seems to have become the cousin that you are obliged to invite to your wedding party (or application party), but don't really want to. I believe that part of the reason for this has been Javascript performance, but there are several efforts underway (e.g. TraceMonkey and now Chrome's V8 Javascript engine) that will bring huge leaps in performance to Javascript. Also, I prefer to support non-proprietary technologies such as DHTML over vendor-based alternatives like Flex and Silverlight.&lt;/div&gt;&lt;div style="text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The second feature that's important in Chrome is process isolation for tabs. This enables each tab to be completely isolated from other processes. One tab going down doesn't take the whole browser with it (which is something that has plagued my Firefox quite a bit).&lt;/div&gt;&lt;div style="text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Thirdly, we have "application-like" OS integration in Chrome. Chrome has the ability to make a single tab look like an application window, with normal window decorations:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;img src="http://3.bp.blogspot.com/_bXDgUoT7V7o/SMUU8kz1zyI/AAAAAAAAAH8/B0hxkM8SvUQ/s400/titlebar.png" style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" border="0" alt="" id="BLOGGER_PHOTO_ID_5243620372048629538" /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;In your taskbar it looks like an application, there is  quick lauch icon for you app:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;img src="http://4.bp.blogspot.com/_bXDgUoT7V7o/SMUTlKE0ksI/AAAAAAAAAHs/P4OOCC6YjoI/s400/taskbar.png" style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" border="0" alt="" id="BLOGGER_PHOTO_ID_5243618870223475394" /&gt;There's also a Start Menu item (not shown) and a Desktop icon:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;img src="http://3.bp.blogspot.com/_bXDgUoT7V7o/SMUT16R0EhI/AAAAAAAAAH0/pMHvbHYvCzI/s400/shortcut.png" style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" border="0" alt="" id="BLOGGER_PHOTO_ID_5243619158040777234" /&gt;&lt;div&gt;&lt;br /&gt;The combination of these three is a significant step towards blurring the lines between the desktop and the web. Chrome is an application platform with (potentially) desktop-like application performance (DHTML + V8), process isolation (a key feaure of an application platform) and the web apps look like desktop apps (Operating system integration). And it's all standards based.&lt;br /&gt;&lt;br /&gt;There have been some rumours of a Google OS...&lt;br /&gt;&lt;br /&gt;You're looking at it.&lt;br /&gt;&lt;/div&gt;</description><link>http://21ccw.blogspot.com/2008/09/me-and-my-dog-on-google-chrome.html</link><author>noreply@blogger.com (Benjamin Nortier)</author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_bXDgUoT7V7o/SMUU8kz1zyI/AAAAAAAAAH8/B0hxkM8SvUQ/s72-c/titlebar.png' height='72' width='72'/><thr:total>0</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-7435412383881764254.post-1549559424813718640</guid><pubDate>Thu, 24 Jul 2008 11:46:00 +0000</pubDate><atom:updated>2008-07-24T05:02:34.724-07:00</atom:updated><category domain='http://www.blogger.com/atom/ns#'>Scalaris</category><category domain='http://www.blogger.com/atom/ns#'>Distributed databases</category><category domain='http://www.blogger.com/atom/ns#'>Erlang</category><title>Scalaris Released</title><description>If you were at the &lt;a href="http://www.erlang-exchange.com/"&gt;Erlang Exchange&lt;/a&gt; in London last month, you should know that one of the hottest talks was given by &lt;a href="http://erlang-exchange.com/alexander-reinefeld"&gt;Alexander Reinefeld&lt;/a&gt;, "&lt;a href="http://skillsmatter.com/podcast/erlang/building-a-transactional-distributed-data-store-with-erlang"&gt;Building a transactional distributed data store with Erlang&lt;/a&gt;"&lt;br /&gt;&lt;br /&gt;Joe Armstrong seems to like it too:&lt;br /&gt;&lt;br /&gt;"I might be wrong, but my gut feeling is that what Alexander Reinefeld showed us will be the first killer application in Erlang."&lt;br /&gt;&lt;br /&gt;"So my take on this is that this is one of the sexiest applications I've seen in many a year. I've been waiting for this to happen for a long while. The work is backed by quadzillion Ph.D's and is really good &lt;span style="font-style: italic;"&gt;believe me."&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;- http://armstrongonsoftware.blogspot.com/2008/06/itching-my-programming-nerve.html&lt;br /&gt;&lt;br /&gt;Well, seems like Scalaris has been released! The link went up on the website in the last 24-48 hours. I haven't had the time to look at it yet, but you can grab it &lt;a href="http://code.google.com/p/scalaris/"&gt;here&lt;/a&gt;.</description><link>http://21ccw.blogspot.com/2008/07/scalaris-released.html</link><author>noreply@blogger.com (Benjamin Nortier)</author><thr:total>1</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-7435412383881764254.post-2790488996638110334</guid><pubDate>Wed, 02 Jul 2008 11:09:00 +0000</pubDate><atom:updated>2008-07-02T05:31:19.996-07:00</atom:updated><category domain='http://www.blogger.com/atom/ns#'>OTP</category><category domain='http://www.blogger.com/atom/ns#'>erlang exchange</category><category domain='http://www.blogger.com/atom/ns#'>Erlang</category><title>Notes from Erlang Exchange</title><description>I was at the &lt;a href="http://www.erlang-exchange.com/"&gt;Erlang Exchange&lt;/a&gt; in London last week, which I really enjoyed. It was really great to see &lt;a href="http://erlang-exchange.com/joe-armstrong"&gt;Joe Armstrong&lt;/a&gt;, and to see the faces behind the projects, e.g. &lt;a href="http://erlang-exchange.com/klacke-wikstrom"&gt;Claes Wikström&lt;/a&gt; (Yaws, Bluetail, &lt;a href="http://www.kreditor.se/"&gt;Kreditor&lt;/a&gt;, &lt;a href="http://www.tail-f.com/"&gt;Tail-f&lt;/a&gt;), &lt;a href="http://erlang-exchange.com/steve-vinoski"&gt;Steve Vinoski&lt;/a&gt;,  &lt;a href="http://erlang-exchange.com/eric-merrit-and-martin-logan"&gt;Eric Merrit&lt;/a&gt; and &lt;a href="http://erlang-exchange.com/eric-merrit-and-martin-logan"&gt;Martin Logan&lt;/a&gt; (&lt;a href="http://www.erlware.org/"&gt;Erlware&lt;/a&gt;), &lt;a href="http://erlang-exchange.com/matthias-radestock-and-tony-garnock-jones"&gt;Matthias Radestock&lt;/a&gt; (&lt;a href="http://www.lshift.net/"&gt;LShift &lt;/a&gt;&amp;amp; &lt;a href="http://www.rabbitmq.com/"&gt;RabbitMQ&lt;/a&gt;), &lt;a href="http://www.erlang-exchange.com/mickael-remond"&gt;Mickaël Rémond&lt;/a&gt; (&lt;a href="http://www.ejabberd.im/"&gt;ejabberd&lt;/a&gt;) etc. etc.&lt;br /&gt;&lt;br /&gt;Something that Joe said reminded me of a comment that &lt;a href="http://peripateticaxiom.blogspot.com/"&gt;Keith&lt;/a&gt; made about 6 months ago. I think this is something that gets lost in the hype around Erlang and multicore computing:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;Erlang was designed to program fault tolerant systems. &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;It was not designed to program multicore computers. It was designed 15-20 years ago when multicore-everywhere was not even on the horizon. As a side-effect, it is a good language for multicore systems, since the only way to actually &lt;span style="font-style: italic;"&gt;achieve&lt;/span&gt; fault tolerance is to have &lt;span style="font-style: italic;"&gt;independant&lt;/span&gt; processes (read &lt;a href="http://ml.osdir.com/lang.erlang.general/2003-04/msg00350.html"&gt;here&lt;/a&gt; for some more on this).&lt;br /&gt;&lt;br /&gt;I think it's important for us as to remember to advocate this, and instead of marketing Erlang/OTP as "the application system for multicore", we should be marketing it as "the application system for fault tolerance". Oh, and by the way "this is one of the few systems that will actually use those cores that are coming your way".&lt;br /&gt;&lt;br /&gt;The second point that I have been pondering, was brought on my &lt;a href="http://erlang-exchange.com/gordon-guthrie"&gt;Gordon Guthrie&lt;/a&gt;'s talk about Erlang/OTP vs Google Apps as an application engine. At this moment, there aren't many application systems to choose from, Erlang/OTP, Google Apps and Amazon EC2 come to mind. In the context of the talk, an Application System (A/S) would be something that takes care of a lot of the non-functional requirements of you system, e.g. reliability, scalability (+distribution) etc.&lt;br /&gt;&lt;br /&gt;For some businesses, using Google Apps or EC2 would be a good fit, but then you're tied into the value chain of that business, and in some instances you just cannot (for legal and other reasons) put your data on servers you don't own. If you go the OTP route you have full control over your application(s), but there's a lot of extra infrastructure that you will have to supply yourself.&lt;br /&gt;&lt;br /&gt;If you start thinking of Erlang/OTP in this way, you realise that the terms should be reversed and it should actually be OTP/Erlang. The platform is actually the important thing. The &lt;span style="font-style: italic;"&gt;platform&lt;/span&gt; is the thing that gives you the reliability, scalability, distribution and hot code swapping etc. that you require. Erlang becomes this awesome language that you use to build things on the &lt;span style="font-style: italic;"&gt;platform&lt;/span&gt;. Looking at things from a different perspective can lead to some interesting insights.&lt;br /&gt;&lt;br /&gt;If I'm standing at this vantage point, heated discussions like the recent meme-storm in the blogosphere of "&lt;a href="http://www.sauria.com/blog/2008/05/22/the-scala-vs-erlang-whirlwind/"&gt;Erlang&lt;/a&gt;&lt;a href="http://www.sauria.com/blog/2008/05/22/the-scala-vs-erlang-whirlwind/"&gt; vs &lt;/a&gt;&lt;a href="http://www.sauria.com/blog/2008/05/22/the-scala-vs-erlang-whirlwind/"&gt;Scala&lt;/a&gt;" become much less relevant. Is there a platform that gives you reliability, scalability, distribution, hot code replacement that uses Scala as a language? No? Oh. What a pity...</description><link>http://21ccw.blogspot.com/2008/07/notes-from-erlang-exchange.html</link><author>noreply@blogger.com (Benjamin Nortier)</author><thr:total>1</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-7435412383881764254.post-8901001959287642946</guid><pubDate>Tue, 17 Jun 2008 13:07:00 +0000</pubDate><atom:updated>2008-06-25T01:47:45.188-07:00</atom:updated><category domain='http://www.blogger.com/atom/ns#'>Mochiweb</category><category domain='http://www.blogger.com/atom/ns#'>REST</category><category domain='http://www.blogger.com/atom/ns#'>Erlang</category><category domain='http://www.blogger.com/atom/ns#'>TDD</category><title>Migrating a native Erlang interface to RESTful Mochiweb (with a bit of TDD)</title><description>The title is a bit of a mouthful, but it does contain in essence what I will show you:&lt;br /&gt;&lt;br /&gt;1. I will convert an existing native erlang &lt;a href="http://en.wikipedia.org/wiki/Create,_read,_update_and_delete"&gt;CRUD&lt;/a&gt; interface (a simple client-server) to use a HTTP layer.&lt;br /&gt;2. The HTTP calls will be &lt;a href="http://en.wikipedia.org/wiki/Representational_State_Transfer"&gt;REST&lt;/a&gt;ful.&lt;br /&gt;3. I will be using &lt;a href="http://code.google.com/p/mochiweb/"&gt;Mochiweb&lt;/a&gt;.&lt;br /&gt;4. I will show you how to do a bit of Test-Driven-Development (TDD) for this exercise  using &lt;a href="https://support.process-one.net/doc/display/CONTRIBS/EUnit"&gt;EUnit&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;As an introduction, I suggest you read &lt;a href="http://steve.vinoski.net/blog/"&gt;Steve Vinoski&lt;/a&gt;'s "&lt;a href="http://www.infoq.com/articles/vinoski-erlang-rest"&gt;RESTful Services with Erlang and Yaws&lt;/a&gt;", and "&lt;a href="http://blog.socklabs.com/2008/04/a_restful_web_service_demo_in/"&gt;A RESTful web service demo in yaws&lt;/a&gt;" by &lt;a href="http://blog.socklabs.com/"&gt;Nick Gerakines.&lt;/a&gt; They are both excellent posts, and Nick's post has a lot of interesting details, including some OTP goodness. Also check out &lt;a href="http://darynholmes.wordpress.com/2008/03/15/beginners-tutorial-routing-in-rails-20-with-rest-part-1-of-n/"&gt;Daryn&lt;/a&gt;'s posts for a Rails twist on this topic.&lt;br /&gt;&lt;br /&gt;I will NOT be showing much error handling, simply returning a 501 response if the REST Url has&lt;br /&gt;some mistake in it.&lt;br /&gt;&lt;br /&gt;This is the plan:&lt;br /&gt;1. We will start with a working set of tests for a native CRUD interface. I will &lt;span style="font-style: italic;"&gt;not&lt;/span&gt; show you the actual implementation, but it is simple enough to do yourself using a process dictionary.&lt;br /&gt;2. I will show a very simple REST interface with Mochiweb, that just returns the request method type as a plaintext string. This will be tested using Inets, the native Erlang OTP internet module.&lt;br /&gt;3. We will write a translators to and from plaintext and native erlang terms (again, unit tested).&lt;br /&gt;4. We will combine all these into the REST interface, tested using Inets again.&lt;br /&gt;&lt;br /&gt;Right, here is the unit tests for the CRUD interface:&lt;pre class="code"&gt;crud_test_() -&gt;   &lt;br /&gt;  {setup,&lt;br /&gt;   fun() -&gt; start() end,&lt;br /&gt;   fun(_) -&gt; stop() end,&lt;br /&gt;   fun(_) -&gt;&lt;br /&gt;           [&lt;br /&gt;            ?_assert(ok == create(#person{id = 1})),&lt;br /&gt;            ?_assert(already_exists == create(#person{id=1})),&lt;br /&gt;            ?_assert(#person{id=1} == retrieve(1)),&lt;br /&gt;            ?_assert(ok == update(#person{id = 1, name="Ben"})),&lt;br /&gt;            ?_assert(#person{id=1, name="Ben"} == retrieve(1)),&lt;br /&gt;            ?_assert(ok == delete(1)),&lt;br /&gt;            ?_assert(undefined == delete(2)),&lt;br /&gt;            ?_assert(undefined == update(#person{id = 2}))&lt;br /&gt;           ]&lt;br /&gt;   end}.&lt;/pre&gt;The CRUD interface is container in a module "crud", and start() and stop() are methods to start and stop the CRUD server. For EUnit, you can define a test with a setup, teardown and tests, and this is the form that I've used here. There are many forms of tests for EUnit, and I suggest you consult the EUnit documentation (contained in the EUnit distribution).&lt;br /&gt;&lt;br /&gt;EUnit automatically creates a test() function on the module when you include "eunit.hrl", so let's run the crud tests:&lt;pre class="console"&gt;&lt;br /&gt;75&gt; crud:test().&lt;br /&gt;All 8 tests successful.&lt;br /&gt;ok&lt;br /&gt;&lt;/pre&gt;We know that are CRUD interface is working OK.&lt;br /&gt;&lt;br /&gt;Onto Step 2! If you're not familiar with Mochiweb, it's a lightweight web server developed by the guys at &lt;a href="http://www.mochimedia.com/"&gt;Mochi Media&lt;/a&gt;. It's very easy to integrate into you application, and has very little configuration.&lt;br /&gt;&lt;br /&gt;Here are the tests for the simple Mochiweb demo:&lt;pre class="code"&gt;&lt;br /&gt;-define(URL, "http://127.0.0.1:8888").&lt;br /&gt;&lt;br /&gt;rest_server_test_() -&gt;&lt;br /&gt;    {setup,&lt;br /&gt;     fun() -&gt; inets:start(), start_simple() end,&lt;br /&gt;     fun(_) -&gt; inets:stop(), stop_simple() end,&lt;br /&gt;     fun(_) -&gt;&lt;br /&gt;             [&lt;br /&gt;              ?_assert(http_result('GET') =:= "GET"),&lt;br /&gt;              ?_assert(http_result('PUT') =:= "PUT"),&lt;br /&gt;              ?_assert(http_result('POST') =:= "POST"),&lt;br /&gt;              ?_assert(http_result('DELETE') =:= "DELETE")&lt;br /&gt;             ]&lt;br /&gt;     end}.&lt;br /&gt;&lt;br /&gt;parse_result(Result) -&gt;&lt;br /&gt;    {ok, {{_Version, 200, _ReasonPhrase}, _Headers, ResultBody}} = Result,&lt;br /&gt;    ResultBody.&lt;br /&gt;&lt;br /&gt;rest_server_test_() -&gt;&lt;br /&gt;    {setup,&lt;br /&gt;     fun() -&gt; inets:start(), start_simple() end,&lt;br /&gt;     fun(_) -&gt; inets:stop(), stop_simple() end,&lt;br /&gt;     fun(_) -&gt;&lt;br /&gt;             [&lt;br /&gt;              ?_assert(http_result('GET') =:= "GET"),&lt;br /&gt;              ?_assert(http_result('PUT') =:= "PUT"),&lt;br /&gt;              ?_assert(http_result('POST') =:= "POST"),&lt;br /&gt;              ?_assert(http_result('DELETE') =:= "DELETE")&lt;br /&gt;             ]&lt;br /&gt;     end}.&lt;br /&gt;&lt;/pre&gt;start_simple() starts the simple version of the server and stop_simple() stops it. We're just using the base URL, and the server just returns a plain-text string of the request method. The http:request() functions are part of Inets (documentation &lt;a href="http://www.erlang.org/doc/apps/inets/index.html"&gt;here&lt;/a&gt;). You will notice that the put and post functions have extra parameters (which we will use later for the request body).&lt;br /&gt;&lt;br /&gt;Here's the solution:&lt;pre class="code"&gt;&lt;br /&gt;start_simple() -&gt;&lt;br /&gt;    mochiweb_http:start(&lt;br /&gt;      [{ip, "127.0.0.1"},&lt;br /&gt;       {loop, {?MODULE, simple_response}}]).&lt;br /&gt;stop_simple() -&gt;&lt;br /&gt;    mochiweb_http:stop().&lt;br /&gt;    &lt;br /&gt;&lt;br /&gt;simple_response(Req, Method) -&gt;&lt;br /&gt;    Req:ok({"text/plain", atom_to_list(Method)}).&lt;br /&gt;simple_response(Req) -&gt;&lt;br /&gt;    simple_response(Req, Req:get(method)).&lt;br /&gt;&lt;/pre&gt;We've started Mochiweb, and told it that the "simple_response" function in the current module should be used to handle requests. It takes one parameter, which contains the request data. The data can be queried with different methods to extract data from it, e.g. Req:get(method), gives you the method used.&lt;br /&gt;&lt;br /&gt;The Req:ok() function is used to respond to a request, and we simply return plain text (with the text/plain MIME type).&lt;br /&gt;&lt;br /&gt;And let's run the tests:&lt;pre class="console"&gt;&lt;br /&gt;77&gt; rest_server:test().&lt;br /&gt;...&lt;br /&gt;All 4 tests successful.&lt;br /&gt;ok&lt;br /&gt;&lt;/pre&gt;Nice. The next bit of code we need is to translate Erlang terms to and from plain text. We'll use Base64 encoding for this. Erlang also has very hand term to binary and binary to term functions that we can use to achieve the translation.&lt;br /&gt;&lt;br /&gt;The tests for the translation:&lt;br /&gt;&lt;pre class="code"&gt;&lt;br /&gt;term_to_plaintext_test_() -&gt;&lt;br /&gt;    A = anatom,&lt;br /&gt;    B = {a, 23, "abc"},&lt;br /&gt;    C = {props, [{a,3},{b,4}]},&lt;br /&gt;    [&lt;br /&gt;     ?_assert(A == ptt(ttp(A))),&lt;br /&gt;     ?_assert(B == ptt(ttp(B))),&lt;br /&gt;     ?_assert(C == ptt(ttp(C)))&lt;br /&gt;    ].&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Where "ttp" would be "term_to_plaintext" and "ptt" is&lt;br /&gt;"plaintext_to_term". Here's the implementation:&lt;br /&gt;&lt;pre class="code"&gt;&lt;br /&gt;ttp(Term) -&gt;&lt;br /&gt;    base64:encode_to_string(term_to_binary(Term)).&lt;br /&gt;&lt;br /&gt;ptt(PlainText) -&gt;&lt;br /&gt;    binary_to_term(base64:decode(PlainText)).&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;And the test result:&lt;pre class="console"&gt;&lt;br /&gt;77&gt; rest_server:test().&lt;br /&gt;...&lt;br /&gt;All 7 tests successful.&lt;br /&gt;ok&lt;br /&gt;&lt;/pre&gt;(7 tests, since we have the first 4 and now the extra 3). Now things are getting a bit more complicated. We now have to ensure that the response to the request is converted from the native Erlang terms to plaintext, this result is then returned, and we also need a function to convert the request result into the native form.&lt;br /&gt;&lt;br /&gt;Here we go:&lt;pre class="code"&gt;&lt;br /&gt;result_to_terms(Result) -&gt;&lt;br /&gt;  {ok, {{_Version, 200, _ReasonPhrase}, _Headers, ResultBody}} = Result,&lt;br /&gt;  ptt(ResultBody).&lt;br /&gt;&lt;br /&gt;rest_api('GET', Path) -&gt;&lt;br /&gt;    Result = http:request(get, {?URL ++ Path, []}, [], []),&lt;br /&gt;    result_to_terms(Result);&lt;br /&gt;rest_api('DELETE', Path) -&gt;&lt;br /&gt;    Result = http:request(delete, {?URL ++ Path, []}, [], []),&lt;br /&gt;    result_to_terms(Result).&lt;br /&gt;&lt;br /&gt;rest_api('POST', Path, Data) -&gt;&lt;br /&gt;    Result = http:request(post, {?URL ++ Path, [], [], ttp(Data)}, [], []),&lt;br /&gt;    result_to_terms(Result);&lt;br /&gt;rest_api('PUT', Path, Data) -&gt;&lt;br /&gt;    Result = http:request(put, {?URL ++ Path, [], [], ttp(Data)}, [], []),&lt;br /&gt;    result_to_terms(Result).&lt;br /&gt;&lt;br /&gt;crud_server_test_() -&gt;&lt;br /&gt;    {setup,&lt;br /&gt;     fun() -&gt; crud:start(), inets:start(), start() end,&lt;br /&gt;     fun(_) -&gt; stop(), inets:stop(), crud:stop() end,&lt;br /&gt;     fun(_) -&gt;&lt;br /&gt;      [&lt;br /&gt;       ?_assert(ok == rest_api('POST', "/person", #person{id = 1})),&lt;br /&gt;       ?_assert(already_exists == rest_api('POST', "/person", #person{id = 1})),&lt;br /&gt;       ?_assert(#person{id=1} == rest_api('GET', "/person/1")),&lt;br /&gt;       ?_assert(ok == rest_api('PUT', "/person/1", #person{name="Ben"})),&lt;br /&gt;       ?_assert(#person{id=1, name="Ben"} == rest_api('GET', "/person/1")),&lt;br /&gt;       ?_assert(ok == rest_api('DELETE', "/person/1")),&lt;br /&gt;       ?_assert(undefined == rest_api('DELETE', "/person/1")),&lt;br /&gt;       ?_assert(undefined == rest_api('PUT', "/person/1", #person{id = 2}))&lt;br /&gt;      ]&lt;br /&gt;     end}.&lt;br /&gt;&lt;/pre&gt;There is a bit of duplication here, but for illustration I've kept the functions seperate. You will notice that the data is converted to plaintext for the PUT and POST requests. result_to_terms() converts the HTTP result from the plain text to the native Erlang terms.&lt;br /&gt;&lt;br /&gt;You can scroll back to the original CRUD tests, and notice that the tests do exactly the same thing as on the CRUD interface, but we would have to do a "GET" + "/person/1" to get person 1, instead of doing a crud:retrieve(1).&lt;br /&gt;&lt;br /&gt;The mapping between the CRUD and REST method are as follows:&lt;br /&gt;GET &lt;=&gt; RETRIEVE&lt;br /&gt;POST &lt;=&gt; CREATE&lt;br /&gt;PUT &lt;=&gt; UPDATE&lt;br /&gt;DELETE &lt;=&gt;DELETE&lt;br /&gt;&lt;br /&gt;Here's the final product:&lt;br /&gt;&lt;pre class="code"&gt;&lt;br /&gt;start() -&gt;&lt;br /&gt;    mochiweb_http:start(&lt;br /&gt;      [{ip, "127.0.0.1"},&lt;br /&gt;       {loop, {?MODULE, crud_response}}]).&lt;br /&gt;stop() -&gt;&lt;br /&gt;    mochiweb_http:stop().&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;crud_response(Req, 'GET', "/person/" ++ IdString) -&gt;&lt;br /&gt;    Id = list_to_integer(IdString),&lt;br /&gt;    Response = crud:retrieve(Id),&lt;br /&gt;    Req:ok({"text/plain", ttp(Response)});&lt;br /&gt;&lt;br /&gt;crud_response(Req, 'DELETE', "/person/" ++ IdString) -&gt;&lt;br /&gt;    Id = list_to_integer(IdString),&lt;br /&gt;    Response = crud:delete(Id),&lt;br /&gt;    Req:ok({"text/plain", ttp(Response)});&lt;br /&gt;&lt;br /&gt;crud_response(Req, 'POST', "/person") -&gt;&lt;br /&gt;    Body = Req:recv_body(),&lt;br /&gt;    Person = ptt(Body),&lt;br /&gt;    Response = crud:create(Person),&lt;br /&gt;    Req:ok({"text/plain", ttp(Response)});&lt;br /&gt;&lt;br /&gt;crud_response(Req, 'PUT', "/person/" ++ IdString) -&gt;&lt;br /&gt;    Id = list_to_integer(IdString),&lt;br /&gt;    Body = Req:recv_body(),&lt;br /&gt;    PersonWithNewValues = ptt(Body),&lt;br /&gt;    UpdatedPerson = #person{id = Id, &lt;br /&gt;                            name = PersonWithNewValues#person.name, &lt;br /&gt;                            email_address = PersonWithNewValues#person.email_address},&lt;br /&gt;    Response = crud:update(UpdatedPerson),&lt;br /&gt;    Req:ok({"text/plain", ttp(Response)});&lt;br /&gt;        &lt;br /&gt;crud_response(Req, _Method, Path) -&gt;&lt;br /&gt;    Req:respond({501, [], Path}).&lt;br /&gt;&lt;br /&gt;crud_response(Req) -&gt;&lt;br /&gt;    crud_response(Req, Req:get(method), Req:get(path)).&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Once again, there is some duplication, but it's easier to grasp when we split the functions. The GET and DELETE requests are simple to handle, since we just convert the Id string to an integer (which could throw an exception, you would have to handle that in some way), and call the CRUD interface.&lt;br /&gt;&lt;br /&gt;POST is not much more complicated, we just get the body of the request using Req:recv_body().&lt;br /&gt;&lt;br /&gt;The PUT function has a problem. There is duplication of the Id of the person, in both the URL "/person/1", and the actual term, #person{id=1,...}. I'm not sure how to handle this, comments are welcome. Perhaps you can generate an error response if the Ids don't match. Or you can make sure the posted record does NOT contain an Id field, and return an error if it does.&lt;br /&gt;&lt;br /&gt;The solution as I've given it, uses the fields in the record, and ignores the Id of the record, using the Id in the URL instead.&lt;br /&gt;&lt;br /&gt;The last function generates an error if the request could not be matched.&lt;br /&gt;&lt;br /&gt;And the moment of truth:&lt;pre class="console"&gt;81&gt; rest_server:test().&lt;br /&gt;...&lt;br /&gt;All 15 tests successful.&lt;br /&gt;ok&lt;/pre&gt;Looks so simple now, but I can asure you that it took some effort to get all the tests to pass!&lt;br /&gt;&lt;br /&gt;Well I hope I've shown you something that you didn't know before, or even encouraged you to learn some Erlang. &lt;br /&gt;&lt;br /&gt;Comments and criticisms are most welcome :)</description><link>http://21ccw.blogspot.com/2008/06/migrating-native-erlang-interface-to.html</link><author>noreply@blogger.com (Benjamin Nortier)</author><thr:total>2</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-7435412383881764254.post-2111907712393486859</guid><pubDate>Sun, 08 Jun 2008 21:19:00 +0000</pubDate><atom:updated>2008-06-25T01:48:11.531-07:00</atom:updated><category domain='http://www.blogger.com/atom/ns#'>REST</category><category domain='http://www.blogger.com/atom/ns#'>Charts</category><title>Restful Charts</title><description>What would you normally expect of a graph-generation library? I'm talking about the pie charts, bar charts etc. You would normally expect an output to some file format, it being PNG or SVG if you're lucky. BUT, &lt;a href="http://www.infoq.com/articles/bass-google-charts-gchartrb;jsessionid=C2188665B07E95A7BDFC4CD39A1C78D3"&gt;gchartrb&lt;/a&gt; doesn't do this, it creates a URL that you would use to GET the graph from the Google charts API.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;So what? &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Well, it's interesting because it means that &lt;span style="font-style: italic;"&gt;some&lt;/span&gt; people at least consider that generating the graph using a REST API is as reliable as disk, and as convenient as a local library. I've used Google charts before, but I've never thought about integrating it into another program as a service, and I think it's pretty neat. And &lt;span style="font-style: italic;"&gt;easy&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;I'm placing my chips on REST.</description><link>http://21ccw.blogspot.com/2008/06/restful-charts.html</link><author>noreply@blogger.com (Benjamin Nortier)</author><thr:total>0</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-7435412383881764254.post-6668255548224137560</guid><pubDate>Wed, 07 May 2008 12:14:00 +0000</pubDate><atom:updated>2009-02-25T01:54:02.621-08:00</atom:updated><category domain='http://www.blogger.com/atom/ns#'>Concurrency</category><category domain='http://www.blogger.com/atom/ns#'>Quicksort</category><category domain='http://www.blogger.com/atom/ns#'>Erlang</category><title>Parallel Quicksort in Erlang - Part II</title><description>In my &lt;a href="http://21ccw.blogspot.com/2008/04/parallel-quicksort-in-erlang.html"&gt;previous post&lt;/a&gt;, &lt;a href="http://jaksa.wordpress.com/"&gt;Jaksa&lt;/a&gt; pointed out that there's a problem, in that although there may be multiple processes, they will be blocked and waiting (sequencially) for the first peval to complete, then the second peval, etc. Which will result in a pseudo-parallel depth-first quicksort, instead of a breadth-first quicksort. This is the offending line:&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;br /&gt;peval(fun pqsort/1, Left) ++ [Pivot] ++ peval(fun pqsort/1, Right).&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I was hoping Erlang would be clever enough to execute the two functions in parallel, but that's a fairly unreasonable (and completely incorrect) assumption! &lt;br /&gt;&lt;br /&gt;But how to fix it? There is a parallel implementation of the map function in &lt;a href="http://www.amazon.com/dp/193435600X?tag=21scencodwor-20&amp;camp=14573&amp;creative=327641&amp;linkCode=as1&amp;creativeASIN=193435600X&amp;adid=0S65GZ2QK77VXZX40RD5&amp;"&gt;Joe's book&lt;/a&gt;, and &lt;a href="http://montsamu.blogspot.com/2007/02/erlang-parallel-map-and-parallel.html"&gt;Montsamu&lt;/a&gt; has a variation of it on his blog:&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;br /&gt;-module(plists).&lt;br /&gt;-export([pmap/2]).&lt;br /&gt;&lt;br /&gt;pmap(F, L) -&gt;&lt;br /&gt;    S = self(),&lt;br /&gt;    Pids = lists:map(fun(I) -&gt; spawn(fun() -&gt; pmap_f(S, F, I) end) end, L),&lt;br /&gt;    pmap_gather(Pids).&lt;br /&gt;&lt;br /&gt;pmap_gather([H|T]) -&gt;&lt;br /&gt;    receive&lt;br /&gt;        {H, Ret} -&gt; [Ret|pmap_gather(T)]&lt;br /&gt;    end;&lt;br /&gt;pmap_gather([]) -&gt;&lt;br /&gt;    [].&lt;br /&gt;&lt;br /&gt;pmap_f(Parent, F, I) -&gt;&lt;br /&gt;    Parent ! {self(), (catch F(I))}.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The pmap_gather function is the interesting one. It will traverse the list of Pids, and wait for each to send back its result. Using pmap, we update pqsort as follows:&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;br /&gt;pqsort([]) -&gt; [];&lt;br /&gt;pqsort([Pivot]) -&gt; [Pivot];&lt;br /&gt;pqsort([Pivot|Rest]) -&gt;&lt;br /&gt;    io:format("+", []),&lt;br /&gt;    Left = [X || X &lt;- Rest, X &lt; Pivot],&lt;br /&gt;    Right = [Y || Y &lt;- Rest, Y &gt;= Pivot],&lt;br /&gt;    [SortedLeft, SortedRight] = plists:pmap(fun pqsort/1, [Left, Right]),&lt;br /&gt;    io:format("-", []),&lt;br /&gt;    SortedLeft ++ [Pivot] ++ SortedRight.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And with a perfectly unbalanced (for quicksort) list to sort, we expect 1 process, then 3 as the first spawns 2 new ones, then down again to zero:&lt;br /&gt;&lt;br /&gt;&lt;pre class="console"&gt;&lt;br /&gt;(emacs@21ccw.blogspot.com)47&gt; pqsort:pqsort([4,2,1,3,6,5,7]).&lt;br /&gt;+++---[1,2,3,4,5,6,7]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;For the same example we had last time, sorting 100 random numbers, the number of concurrent process graph during execution looks like this:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_bXDgUoT7V7o/SCGdnRJGWeI/AAAAAAAAAHM/3_fhZemNZPs/s1600-h/pqsort.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://3.bp.blogspot.com/_bXDgUoT7V7o/SCGdnRJGWeI/AAAAAAAAAHM/3_fhZemNZPs/s400/pqsort.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5197608742904289762" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Much better!</description><link>http://21ccw.blogspot.com/2008/05/parallel-quicksort-in-erlang-part-ii.html</link><author>noreply@blogger.com (Benjamin Nortier)</author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_bXDgUoT7V7o/SCGdnRJGWeI/AAAAAAAAAHM/3_fhZemNZPs/s72-c/pqsort.png' height='72' width='72'/><thr:total>1</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-7435412383881764254.post-6885360353804589417</guid><pubDate>Thu, 24 Apr 2008 12:36:00 +0000</pubDate><atom:updated>2009-02-25T01:52:18.340-08:00</atom:updated><category domain='http://www.blogger.com/atom/ns#'>Concurrency</category><category domain='http://www.blogger.com/atom/ns#'>Quicksort</category><category domain='http://www.blogger.com/atom/ns#'>Erlang</category><title>Parallel Quicksort in Erlang</title><description>&lt;a href="http://jaksa.wordpress.com/2008/04/22/graph-algorithms-with-forkjoin/"&gt;Jaksa&lt;/a&gt; is doing some posts on parallel graph algorithm in Java using fork/join, and he mentions &lt;a href="http://en.wikipedia.org/wiki/Quicksort"&gt;quicksort&lt;/a&gt;:&lt;br /&gt;&lt;br /&gt;&lt;i&gt;    "The equivalent of the hello world for parallel languages is the quicksort algorithm"&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;Now I'm tempted to say something like "Oh yea? You know how many lines of code quicksort is in Erlang? 3!" Yes &lt;a href="http://en.wikipedia.org/wiki/Erlang_(programming_language)"&gt;really&lt;/a&gt;:&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;br /&gt;qsort([]) -&gt; [];&lt;br /&gt;qsort([Pivot|Rest]) -&gt;&lt;br /&gt;    qsort([ X || X &lt;- Rest, X &lt; Pivot]) ++ [Pivot] ++ qsort([ Y || Y &lt;- Rest, Y &gt;= Pivot]).&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;But this would be wrong, as the terseness of the quicksort implementation says more about the power of &lt;a href="http://egarson.blogspot.com/2008/03/erlang-list-comprehensions-by-necessity.html"&gt;list comprehensions&lt;/a&gt; than about Erlang's concurrency-supporting language features.&lt;br /&gt;&lt;br /&gt;So then let's see how you could implement a concurrent quicksort in Erlang, which &lt;i&gt;will&lt;/i&gt; say something about the way in which we can implement concurrency in Erlang. We'll start with the original Wikipedia quicksort from above, and alter it a little bit to make it more obvious what I'm going to do next:&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;br /&gt;qsort([]) -&gt; [];&lt;br /&gt;qsort([Pivot|Rest]) -&gt;&lt;br /&gt;    Left = [X || X &lt;- Rest, X &lt; Pivot],&lt;br /&gt;    Right = [Y || Y &lt;- Rest, Y &gt;= Pivot],&lt;br /&gt;    qsort(Left) ++ [Pivot] ++ qsort(Right).&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Note the two calls to qsort at the end. Since there is no shared state between these two functions, they can be executed in parallel, and the results can be concatenated to yield the sorted result. Depending on the distribution of the input, each of these calls could be split up into 2 more qsort calls etc. etc. Note that you could do a hybrid recursive-tail-recursive solution which sorts the smallest partition using normal recursion and the largest using tail-recusrion, but that is not the aim here.&lt;br /&gt;&lt;br /&gt;Let's assume that we already have a function that executes any input function in a separate Erlang process called "peval". (Note Erlang processes are extremely light-weight and take very little time to construct. They're are &lt;i&gt;not&lt;/i&gt; modelled as operating system processes). peval takes two arguments, a function to execute, and the arguments to the function. When called, it looks like a normal function call:&lt;br /&gt;&lt;br /&gt;&lt;pre class="console"&gt;&lt;br /&gt;(emacs@21ccw.blogspot.com)38&gt; pqsort:peval(fun(X) -&gt; X*X end, 13).&lt;br /&gt;169&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;But, in the background, it spawns a process and the function is evaluated on this new process. The result is then returned to the calling process. Using peval, we now have a parallel quicksort:&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;br /&gt;pqsort([]) -&gt; [];&lt;br /&gt;pqsort([Pivot|Rest]) -&gt;&lt;br /&gt;    Left = [X || X &lt;- Rest, X &lt; Pivot],&lt;br /&gt;    Right = [Y || Y &lt;- Rest, Y &gt;= Pivot],&lt;br /&gt;    peval(fun pqsort/1, Left) ++ [Pivot] ++ peval(fun pqsort/1, Right).&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Now there is a significant observation to make. The concurrent and non-concurrent versions look remarkably similar, we only had to substitute the original function evaluations with parallel versions. This is one of the reasons why I think functional languages are better suited to concurrency that OO ones.&lt;br /&gt;&lt;br /&gt;Let's look at peval. peval creates a new process using spawn_link. spawn_link() is used instead of spawn(), so that the creator process receives exception when the new child process throws an exception.&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;br /&gt;peval(Fun, Args) -&gt;&lt;br /&gt;    Pid = spawn_link(fun() -&gt; wait() end),&lt;br /&gt;    Pid ! {self(), Fun, Args},&lt;br /&gt;    receive &lt;br /&gt;        {Pid, R} -&gt; R&lt;br /&gt;    end.&lt;br /&gt;&lt;br /&gt;wait() -&gt;&lt;br /&gt;    receive&lt;br /&gt;        {From,Fun,Args} -&gt; &lt;br /&gt;            From ! {self(), Fun(Args)}&lt;br /&gt;    end.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;That's it! Parallel quicksort in about &lt;i&gt;15&lt;/i&gt; lines of Erlang code. If I add a "+" output to console on every entry to peval, and "-" on every exit from peval, you get the following output:&lt;br /&gt;&lt;br /&gt;&lt;pre class="console"&gt;&lt;br /&gt;(emacs@21ccw.blogspot.com)18&gt; L1 = lists:map(fun(X) -&gt; trunc(random:uniform()*100) end, lists:seq(1,100)).&lt;br /&gt;[57,33,2,85,59,65,61,71,83,75,94,94,19,5,79,53,31,54,39,61,&lt;br /&gt; 61,79,55,6,37,35,86,31,88|...]&lt;br /&gt;&lt;br /&gt;(emacs@21ccw.blogspot.com&gt; pqsort:pqsort(L1).&lt;br /&gt;+++-++++-+--++-+++++-+--+--+--+++-+--++-+------+++&lt;br /&gt;+-++-+---+++-+--+---++-+-----+++++-+--++-+---+++++&lt;br /&gt;-+--+--+++-+--+---++-+----++-++-++-+------++++-+--&lt;br /&gt;++++-+--++-+---++++-+--++-+---++++-+--+++-++-+---+&lt;br /&gt;++-+--+----+-----+++-+++-+--+++-++-+---+----++-+--&lt;br /&gt;--&lt;br /&gt;[2,4,5,6,7,9,11,13,14,17,18,19,20,21,22,24,27,31,32,33,35,&lt;br /&gt; 37,38,39,40,42,44,47,48|...]&lt;br /&gt;&lt;br /&gt;(emacs@21ccw.blogspot.com)5&gt; pqsort:integrate_output("+++-++++-+--++-+++++-+--+--+--+++-+--++-+------+++").&lt;br /&gt;0123234565654565678910910989878767898987898987654345...&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;You can see that for the first few pevals, by integrating over the output, that number of parallel processes goes up to around 8-10. This will depend on the distribution, and the relative times it takes to initialise processes to doing the actual computation etc. &lt;br /&gt;&lt;br /&gt;Now that we've got pqsort going, I'm looking forward to doing the parallel graph algorithms of Jaksa's fork/join graph algorithms. There will be an obstacle to ensure that no nodes are visited more than once, but this could (possibly) be solved by partitioning the graph into subgraphs first where no children have multiple parents. We'll see...&lt;br /&gt;&lt;br /&gt;&lt;span class="updates"&gt;Update (7 May 2008): This implementation is wrong! (see comments). There's a follow-up post here: &lt;/span&gt;&lt;a href="http://21ccw.blogspot.com/2008/05/parallel-quicksort-in-erlang-part-ii.html"&gt;Parallel Quicksort in Erlang - Part II&lt;/a&gt;</description><link>http://21ccw.blogspot.com/2008/04/parallel-quicksort-in-erlang.html</link><author>noreply@blogger.com (Benjamin Nortier)</author><thr:total>4</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-7435412383881764254.post-3297377195562942494</guid><pubDate>Fri, 18 Apr 2008 17:13:00 +0000</pubDate><atom:updated>2008-04-21T06:06:42.810-07:00</atom:updated><category domain='http://www.blogger.com/atom/ns#'>XPlanner</category><category domain='http://www.blogger.com/atom/ns#'>Internet Explorer</category><category domain='http://www.blogger.com/atom/ns#'>IE</category><title>IE7 Page Zoom Broken</title><description>We use &lt;a href="http://www.xplanner.org/"&gt;XPlanner&lt;/a&gt; at work for some projects, and we noticed that there seems to be a problem with the hyperlinks in Internet Explorer (version 7.0.5730.11) when the pages are zoomed in (you can zoom pages in IE with Ctrl +). The horizontal areas for the links were not aligned to the actual text, which means that some links seem &lt;span style="font-style: italic;"&gt;can't be clicked correctly&lt;/span&gt;, but you &lt;span style="font-style: italic;"&gt;can&lt;/span&gt; click white space (look carefully at the first image, you can see the normal I-beam cursor):&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_bXDgUoT7V7o/SAjcyclT7uI/AAAAAAAAAG8/uXDb8m13Ddo/s1600-h/one.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://3.bp.blogspot.com/_bXDgUoT7V7o/SAjcyclT7uI/AAAAAAAAAG8/uXDb8m13Ddo/s400/one.png" alt="" id="BLOGGER_PHOTO_ID_5190641329768754914" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_bXDgUoT7V7o/SAjcyslT7vI/AAAAAAAAAHE/r-iBAhrJyJU/s1600-h/two.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://4.bp.blogspot.com/_bXDgUoT7V7o/SAjcyslT7vI/AAAAAAAAAHE/r-iBAhrJyJU/s400/two.png" alt="" id="BLOGGER_PHOTO_ID_5190641334063722226" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;I shouldn't be surprised, since it does &lt;a href="http://21ccw.blogspot.com/2008/03/acid3-test-released.html"&gt;so badly in the Acid3 test&lt;/a&gt;. I couldn't find any other reports of this issue, and will be happy to link to them if you post a comment.&lt;br /&gt;&lt;br /&gt;P.S. Firefox, Opera and Safari don't have this problem.</description><link>http://21ccw.blogspot.com/2008/04/ie7-page-zoom-broken.html</link><author>noreply@blogger.com (Benjamin Nortier)</author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_bXDgUoT7V7o/SAjcyclT7uI/AAAAAAAAAG8/uXDb8m13Ddo/s72-c/one.png' height='72' width='72'/><thr:total>0</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-7435412383881764254.post-7636161167002905275</guid><pubDate>Thu, 10 Apr 2008 13:35:00 +0000</pubDate><atom:updated>2008-04-11T01:42:48.223-07:00</atom:updated><category domain='http://www.blogger.com/atom/ns#'>Mnesia</category><category domain='http://www.blogger.com/atom/ns#'>Distributed databases</category><category domain='http://www.blogger.com/atom/ns#'>Erlang</category><title>Getting started with distributed Erlang - Mnesia table relocation</title><description>&lt;a href="http://www.erlang.org/doc/apps/mnesia/index.html"&gt;Mnesia&lt;/a&gt; is a distributed database that forms part of the &lt;a href="http://www.erlang.org/"&gt;Erlang&lt;/a&gt; release. One of the features that I think is potentially powerful, is &lt;span style="font-style: italic;"&gt;transparent&lt;/span&gt; table relocation across machines. With Mnesia, you can replicate tables to any nodes you wish in your network, and Mnesiatakes care of all the back end bits for you. With "transparent", I mean that you don't need to do anything in your clients to make them "aware" of the new tables. Reads that were taking place from a table on one machine, will now be distributed across multiple nodes (where the nodes reside on single or multiple machines).&lt;br /&gt;&lt;br /&gt;I wanted to see how difficult it is to achieve this. For the setup, I installed two virtual Ubuntu 7.10 machines using &lt;a href="http://www.vmware.com/products/player/"&gt;VMware Player&lt;/a&gt;. You can get images for most Ubuntu distros at &lt;a href="http://isv-image.ubuntu.com/vmware/"&gt;http://isv-image.ubuntu.com/vmware/&lt;/a&gt;. FYI, the username and password for these images is ubuntu:ubuntu. I named the two nodes&lt;br /&gt;&lt;br /&gt;node1.21ccw.blogspot.com and&lt;br /&gt;node2.21ccw.blogspot.com&lt;br /&gt;&lt;br /&gt;You'll need to edit the network configurations with the IP addresses if you want to reproduce this experiment. If you need some help, post a question as comment :)&lt;br /&gt;&lt;br /&gt;I now had two machines that could ping each other using the full names, and a warm and fuzzy feeling inside:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_bXDgUoT7V7o/R_4bNw2zkfI/AAAAAAAAAG0/by5WJ18M21A/s1600-h/vms.jpg"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://3.bp.blogspot.com/_bXDgUoT7V7o/R_4bNw2zkfI/AAAAAAAAAG0/by5WJ18M21A/s400/vms.jpg" alt="" id="BLOGGER_PHOTO_ID_5187613744044413426" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;The next step was to start up an Erlang node on each machine. There's a catch here though. I got some problems using erl -sname, probably because of the way I set up the hostnames of the machines. So, &lt;span style="font-style: italic;"&gt;I had to specify the fully qualified names manually&lt;/span&gt;:&lt;br /&gt;&lt;br /&gt;&lt;pre class="consoleA"&gt;&lt;br /&gt;ubuntu@node1:~/node1$ erl -name 'node1@node1.21ccw.blogspot.com'&lt;br /&gt;Erlang (BEAM) emulator version 5.5.5 [source] [async-threads:0] [kernel-poll:false]&lt;br /&gt;&lt;br /&gt;Eshell V5.5.5  (abort with ^G)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="consoleB"&gt;&lt;br /&gt;ubuntu@node2:~/node2$ erl -name 'node2@node2.21ccw.blogspot.com'&lt;br /&gt;Erlang (BEAM) emulator version 5.5.5 [source] [async-threads:0] [kernel-poll:false]&lt;br /&gt;&lt;br /&gt;Eshell V5.5.5  (abort with ^G)&lt;br /&gt;(node1@node1.21ccw.blogspot.com)1&gt; nodes().&lt;br /&gt;[]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Notice the output of the &lt;span style="font-style: italic;"&gt;nodes() &lt;/span&gt;command. This will return a list of &lt;span style="font-style: italic;"&gt;other&lt;/span&gt; Erlang nodes that this node is aware of. Initially there's no awareness. To let a node know of another node, you can use net_adm:ping/1 to ping the other node. Both nodes will then become be aware of each other:&lt;br /&gt;&lt;br /&gt;&lt;pre class="consoleA"&gt;&lt;br /&gt;(node1@node1.21ccw.blogspot.com)4&gt; net_adm:ping('node2@node2.21ccw.blogspot.com').&lt;br /&gt;pong&lt;br /&gt;&lt;br /&gt;(node1@node1.21ccw.blogspot.com)5&gt; nodes().&lt;br /&gt;['node1@node1.21ccw.blogspot.com']&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="consoleB"&gt;&lt;br /&gt;(node2@node2.21ccw.blogspot.com)1&gt; nodes().&lt;br /&gt;['node1@node1.21ccw.blogspot.com']&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Cool. Now the nodes know of each other. To get Mnesia started, you have to create a schema on each node. A schema is located on the file system, in the same location where the actual disc-copies of tables will reside. [node()|nodes()] creates a list of the current node and all the other connected  nodes. ls() shows the directory that Mnesia has created for the database.&lt;br /&gt;&lt;br /&gt;&lt;pre class="consoleA"&gt;&lt;br /&gt;(node1@node1.21ccw.blogspot.com)5&gt; mnesia:create_schema([node()|nodes()]).&lt;br /&gt;ok&lt;br /&gt;&lt;br /&gt;(node1@node1.21ccw.blogspot.com)6&gt; ls().&lt;br /&gt;Mnesia.node1@node1.21ccw.blogspot.com&lt;br /&gt;ok&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="consoleB"&gt;&lt;br /&gt;(node2@node2.21ccw.blogspot.com)2&gt; ls().&lt;br /&gt;Mnesia.node2@node2.21ccw.blogspot.com&lt;br /&gt;ok&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Now we have to start Mnesia on both nodes. You will notice that when we do an mnesia:info on node2 at this point, that it shows both nodes as being running database nodes.&lt;br /&gt;&lt;br /&gt;&lt;pre class="consoleA"&gt;&lt;br /&gt;&lt;br /&gt;(node1@node1.21ccw.blogspot.com)8&gt; mnesia:start().&lt;br /&gt;ok&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="consoleB"&gt;&lt;br /&gt;(node2@node2.21ccw.blogspot.com)3&gt; mnesia:start().&lt;br /&gt;ok&lt;br /&gt;&lt;br /&gt;(node2@node2.21ccw.blogspot.com)4&gt; mnesia:info().&lt;br /&gt;...&lt;br /&gt;running db nodes   = ['node1@node1.21ccw.blogspot.com','node2@node2.21ccw.blogspot.com']&lt;br /&gt;...&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Next we'll create an actual database table, and populate it with some data. We define a record using rd(), then create a table on node1 (by default, this table will reside in RAM and have a disc copy), write a record to it and then read the record again. The primary key of the table is the first field of the record, i.e. the name.&lt;br /&gt;&lt;br /&gt;&lt;pre class="consoleA"&gt;&lt;br /&gt;(node1@node1.21ccw.blogspot.com)9&gt; rd(person, {name, email_address}).&lt;br /&gt;person&lt;br /&gt;&lt;br /&gt;(node1@node1.21ccw.blogspot.com)10&gt; mnesia:create_table(person, [{attributes, record_info(fields, person)}, {disc_copies, [node()]}]).&lt;br /&gt;{atomic,ok}&lt;br /&gt;&lt;br /&gt;(node1@node1.21ccw.blogspot.com)11&gt; mnesia:transaction(fun() -&gt; mnesia:write(#person{name = "John", email_address = "john@21ccw.blogspot.com"}) end).&lt;br /&gt;{atomic,ok}&lt;br /&gt;&lt;br /&gt;(node1@node1.21ccw.blogspot.com)14&gt; mnesia:transaction(fun() -&gt; mnesia:read({person, "John"}) end).&lt;br /&gt;{atomic,[#person{name = "John",email_address = "john@21ccw.blogspot.com"}]}&lt;br /&gt;&lt;br /&gt;(node1@node1.21ccw.blogspot.com)15&gt; mnesia:info().                                     ...&lt;br /&gt;...&lt;br /&gt;[{'node1@node1.21ccw.blogspot.com',disc_copies}] = [person]&lt;br /&gt;...&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;What happens when we do the same read on node2? Remember that node has access to the person table &lt;span style="font-style: italic;"&gt;only&lt;/span&gt; via the network, since it resides in RAM and on disc on node1.&lt;br /&gt;&lt;pre class="consoleB"&gt;&lt;br /&gt;node2@node2.21ccw.blogspot.com)5&gt; mnesia:transaction(fun() -&gt; mnesia:read({person, "John"}) end).&lt;br /&gt;{atomic,[{person,"John","john@21ccw.blogspot.com"}]}&lt;br /&gt;&lt;/pre&gt;Nice. Mnesia has transparently read the record from a table that's on another machine :)&lt;br /&gt;&lt;br /&gt;Now we decide to copy the table to node2. This requires a single command. Mnesia does the copying of the actual data for you to the other machine, and when you look at the file system on node2, there will now be "person.DCD" file, which is the disc copy of the table.&lt;br /&gt;&lt;br /&gt;&lt;pre class="consoleA"&gt;&lt;br /&gt;(node1@node1.21ccw.blogspot.com)15&gt; mnesia:add_table_copy(person, 'node2@node2.21ccw.blogspot.com', disc_copies).&lt;br /&gt;{atomic,ok}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="consoleB"&gt;&lt;br /&gt;(node2@node2.21ccw.blogspot.com)9&gt; ls("Mnesia.node2@node2.21ccw.blogspot.com").&lt;br /&gt;DECISION_TAB.LOG     LATEST.LOG           person.DCD&lt;br /&gt;schema.DAT&lt;br /&gt;ok&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;At this point, when you do a query on the person table, the actual data can come from  either node. I'm not sure how Mnesia decides how to distribute the data, that's something to investigate further.&lt;br /&gt;&lt;br /&gt;Since the table is resident on both nodes, we can actually delete it from node1, and doing a read on node1 will now read the table over the network from node2:&lt;br /&gt;&lt;br /&gt;&lt;pre class="consoleA"&gt;&lt;br /&gt;(node1@node1.21ccw.blogspot.com)23&gt; mnesia:del_table_copy(person, node()).&lt;br /&gt;{atomic,ok}&lt;br /&gt;&lt;br /&gt;(node1@node1.21ccw.blogspot.com)19&gt; mnesia:info().&lt;br /&gt;...&lt;br /&gt;[{'node2@node2.21ccw.blogspot.com',disc_copies}] = [person]&lt;br /&gt;...&lt;br /&gt;&lt;br /&gt;(node1@node1.21ccw.blogspot.com)18&gt; mnesia:transaction(fun() -&gt; mnesia:read({person, "John"}) end).&lt;br /&gt;{atomic,[#person{name = "John",email_address = "john@21ccw.blogspot.com"}]}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Cool.&lt;br /&gt;&lt;br /&gt;What I've show is how to start up an Erlang/Mnesia node on two machines that are networked together, create tables on either node, and move the tables to other nodes by copying and then deleting them. Mnesia has the ability to configure tables to be RAM only, RAM and disc and disc only, which gives you lots of power for optimisation. Couple this with the fact that you can change your configuration dynamically and you have powerful, dynamically configurable distributed database!</description><link>http://21ccw.blogspot.com/2008/04/getting-started-with-distributed-erlang.html</link><author>noreply@blogger.com (Benjamin Nortier)</author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_bXDgUoT7V7o/R_4bNw2zkfI/AAAAAAAAAG0/by5WJ18M21A/s72-c/vms.jpg' height='72' width='72'/><thr:total>12</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-7435412383881764254.post-7476475743087456600</guid><pubDate>Wed, 02 Apr 2008 13:37:00 +0000</pubDate><atom:updated>2008-04-02T06:40:51.935-07:00</atom:updated><category domain='http://www.blogger.com/atom/ns#'>Unit Testing</category><category domain='http://www.blogger.com/atom/ns#'>OTP</category><category domain='http://www.blogger.com/atom/ns#'>Rakefile</category><category domain='http://www.blogger.com/atom/ns#'>Rake</category><category domain='http://www.blogger.com/atom/ns#'>Make</category><category domain='http://www.blogger.com/atom/ns#'>Erlang</category><title>Using Rake for Erlang Unit Testing</title><description>My previous Erlang/Yaws project, &lt;a href="http://www.dayfindr.com/"&gt;dayfindr.com&lt;/a&gt;, is ticking along nicely:&lt;br /&gt;&lt;pre class="console"&gt;$ yaws --status --id dayfindr&lt;br /&gt;Uptime: 33 Days, 13 Hours, 38 Minutes&lt;br /&gt;&lt;/pre&gt;The feedback has been mostly positive, although I've had a "That's a very irritating site." comment. Well, you can't please everyone!&lt;br /&gt;&lt;br /&gt;Following on the experience gained with dayfindr, I'm starting a new Lyme project which will be using some of the OTP design principles (Think of OTP as Erlang application design patterns). Using OTP will give me hot upgrades, which dayfindr lacks at the moment. The first step that I encountered towards this goal was getting my directory structure to conform to the OTP application structure:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;/project      [The project root]&lt;br /&gt;   /ebin      [The compiled .beam files]&lt;br /&gt;   /src       [The source .erl files]&lt;br /&gt;   /include   [Include files like record definitions]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I've been using the Makefile that's provided in the &lt;a href="http://www.amazon.com/gp/product/193435600X?ie=UTF8&amp;amp;tag=21scencodwor-20&amp;amp;linkCode=as2&amp;amp;camp=1789&amp;amp;creative=9325&amp;amp;creativeASIN=193435600X"&gt;Erlang book&lt;/a&gt;&lt;img src="http://www.assoc-amazon.com/e/ir?t=21scencodwor-20&amp;amp;l=as2&amp;amp;o=1&amp;amp;a=193435600X" alt="" style="border: medium none  ! important; margin: 0px ! important;" border="0" height="1" width="1" /&gt;, which is quite simple and compiles the beam files to the same directory as the source folder, which doesn't comply to the required directory structure. Now, I could just update the Makefile, but Makefiles have a cryptic syntax that I don't really want to spend time learning. Plus, they're difficult to debug in my experience. So I Googled around a bit for erlang Makefiles with little to disappointing success. Then I saw an interesting link:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://seangeo.blogspot.com/2007/09/building-erlang-with-rake.html"&gt;Building Erlang with Rake&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;When they make a movie of my life and this moment, you will hear an orchestra and a choir of baritones singing "Eureka" when I click on the link.&lt;br /&gt;&lt;br /&gt;Rake is an Ruby equivalent of Make, and more. It took some effort to get it working, since I had rake 0.7.1 on my machine, but trying to find the problem taught me a bit of Ruby in the process. Upgrading to 0.7.3 solved the problem. Sean's Rakefile compiles your src files into the ebin directory very nicely! After tinkering around with Rake, I realised that it's a &lt;span style="font-style: italic;"&gt;really &lt;/span&gt;nice tool:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;It has a nice mix of declarative and imperative code. You can define rules (e.g. always compile .erl to .beam), or tasks (which can be imperative, e.g. running unit tests).&lt;/li&gt;&lt;li&gt;You can use the full power of Ruby, and don't need to learn Make.&lt;/li&gt;&lt;li&gt;It has syntax that is very close to the domain (i.e. it's a good DSL)&lt;/li&gt;&lt;li&gt;It's easy to debug, since you can use the normal Ruby puts functions etc.&lt;/li&gt;&lt;li&gt;How you set up you dependencies is completely up to you, e.g. you can have different rules for files that conform to different regular expressions.&lt;/li&gt;&lt;/ul&gt;After I got this working, inspired with confidence, I decided to integrate my unit testing into the Rakefile. I think it's important to note at this point that I have less than a day's Ruby experience, and it was &lt;span style="font-style: italic;"&gt;easy &lt;/span&gt;to get this working. Hacking a Makefile would probably have taken me hours and hours. The idea is that the test task is dependent on the compile task, so that if you do a "rake test", it will compile anything that's new and run the unit tests. You can just compile with "rake compile".&lt;br /&gt;&lt;br /&gt;In order to get this going I created two Erlang files, foo.erl and bar.erl. Here's bar.erl, which contains two functions, and a test (&lt;a href="http://https//support.process-one.net/doc/display/CONTRIBS/EUnit"&gt;EUnit&lt;/a&gt;)for each function. One of the tests will fail:&lt;br /&gt;&lt;pre class="code"&gt;&lt;br /&gt;&lt;br /&gt;-module(bar).&lt;br /&gt;-export([bar1/0, bar2/0]).&lt;br /&gt;&lt;br /&gt;-ifdef(EUNIT).&lt;br /&gt;-include_lib("eunit/include/eunit.hrl").&lt;br /&gt;-endif.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;bar1() -&gt;&lt;br /&gt;  ok.&lt;br /&gt;&lt;br /&gt;bar2() -&gt;&lt;br /&gt;  okk.&lt;br /&gt;&lt;br /&gt;-ifdef(EUNIT).&lt;br /&gt;bar1_test_() -&gt;&lt;br /&gt;  [&lt;br /&gt;    ?_assert(ok == bar1())&lt;br /&gt;  ].&lt;br /&gt;-endif.&lt;br /&gt;&lt;br /&gt;-ifdef(EUNIT).&lt;br /&gt;bar2_test_() -&gt;&lt;br /&gt;  [&lt;br /&gt;    ?_assert(ok == bar2())&lt;br /&gt;  ].&lt;br /&gt;-endif.&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;Notice that the unit test code is only compiled into the beam file when the EUNIT flag is set. You can set this in the Rakefile. The unit tests are in the same source file, so we can also test non-exported functions.&lt;br /&gt;&lt;br /&gt;Now let's look at the Rakefile:&lt;br /&gt;&lt;pre class="code"&gt;&lt;br /&gt;&lt;br /&gt;require 'rake/clean'&lt;br /&gt;&lt;br /&gt;INCLUDE = "include"&lt;br /&gt;&lt;br /&gt;ERLC_FLAGS = "-I#{INCLUDE} +warn_unused_vars +warn_unused_import"&lt;br /&gt;&lt;br /&gt;SRC = FileList['src/*.erl']&lt;br /&gt;OBJ = SRC.pathmap("%{src,ebin}X.beam")&lt;br /&gt;&lt;br /&gt;CLEAN.include("ebin/*.beam")&lt;br /&gt;&lt;br /&gt;directory 'ebin'&lt;br /&gt;&lt;br /&gt;rule ".beam" =&gt; ["%{ebin,src}X.erl"] do |t|&lt;br /&gt;  sh "erlc -D EUNIT -pa ebin -W #{ERLC_FLAGS} -o ebin #{t.source}"&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;task :compile =&gt; ['ebin'] + OBJ&lt;br /&gt;&lt;br /&gt;task :default =&gt; :compile&lt;br /&gt;&lt;br /&gt;task :run_tests =&gt; [:compile] do&lt;br /&gt;  puts "Modules under test:"&lt;br /&gt;  OBJ.each do |obj|&lt;br /&gt;    obj[%r{.*/(.*).beam}]&lt;br /&gt;    mod = $1&lt;br /&gt;    test_output = `erl -pa ebin -run #{mod} test -run init stop`&lt;br /&gt;&lt;br /&gt;    if /\*failed\*/ =~ test_output&lt;br /&gt;      test_output[/(Failed.*Aborted.*Skipped.*Succeeded.*$)/]&lt;br /&gt;    else&lt;br /&gt;      test_output[/1&gt;\s*(.*)\n/]&lt;br /&gt;    end&lt;br /&gt;&lt;br /&gt;    puts "#{mod}: #{$1}"&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;The juicy bits are the rule and the "run_tests" task. The rule states "for each file ending in .erl, compile the beam file using erlc and put the beam file in ebin". The run_tests task starts up an erlang runtime for each module, and calls test() for that module. The test output is captured and parsed using regular expressions. I know the Ruby code can be improved, so comments are most welcome.&lt;br /&gt;&lt;br /&gt;Here's what happens when I compile and run the tests:&lt;br /&gt;&lt;br /&gt;&lt;pre class="console"&gt;&lt;br /&gt;$ rake clean&lt;br /&gt;(in /home/bjnortier/development/project1)&lt;br /&gt;&lt;br /&gt;$ rake&lt;br /&gt;(in /home/bjnortier/development/project1)&lt;br /&gt;erlc -D EUNIT -pa ebin -W -Iinclude +warn_unused_vars +warn_unused_import -o ebin src/foo.erl&lt;br /&gt;erlc -D EUNIT -pa ebin -W -Iinclude +warn_unused_vars +warn_unused_import -o ebin src/bar.erl&lt;br /&gt;&lt;br /&gt;$rake run_tests&lt;br /&gt;(in /home/bjnortier/development/project1)&lt;br /&gt;Modules under test:&lt;br /&gt;foo: All 2 tests successful.&lt;br /&gt;bar: Failed: 1.  Aborted: 0.  Skipped: 0.  Succeeded: 1.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;At this points I have cleaning, compiling and running the tests for each module. Nice. I'm quite pleased with how it works at the moment, but there's still quite a  bit of work to be done:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Concatenating the running of the unit tests into one no-shell erlang execution. Actually running the tests are very fast, but the shell termination takes about a second or so. Thus, for each module, there is a approximately a second of extra time. Running all the tests in the same erlang session will require some more text parsing, but it's do-able.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Displaying the failed tests. Seeing "Failed: 1" is not very useful for determining what went wrong, so I'll update the parsing to include the failures and errors.&lt;/li&gt;&lt;li&gt;Probably change the "run_tests" task to just "test"&lt;/li&gt;&lt;li&gt;Continuous integration that hooks into version control.&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;If there is significant progress I'll post the results. I hope you can use some of it :)</description><link>http://21ccw.blogspot.com/2008/04/using-rake-for-erlang-unit-testing.html</link><author>noreply@blogger.com (Benjamin Nortier)</author><thr:total>2</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-7435412383881764254.post-308992736105091102</guid><pubDate>Tue, 01 Apr 2008 11:25:00 +0000</pubDate><atom:updated>2008-04-02T03:16:46.309-07:00</atom:updated><category domain='http://www.blogger.com/atom/ns#'>Big Mac Index</category><category domain='http://www.blogger.com/atom/ns#'>Apple</category><category domain='http://www.blogger.com/atom/ns#'>Macbook Pro</category><title>The MacbookPro Index</title><description>I'm looking into prices for a Macbook Pro, so that I can &lt;a href="http://21ccw.blogspot.com/2008/03/mac-is-back.html"&gt;bolster my own prediction&lt;/a&gt;. I'm aware of the fact that for some reason, they are a bit more expensive in the UK than in the US (even without sales tax/VAT). I'm from South Africa, so I can organise for someone to bring me one, but I would &lt;span style="font-style: italic;"&gt;never have guessed&lt;/span&gt; that it would be cheaper to buy it there than in the UK, and even &lt;span style="font-style: italic;"&gt;cheaper than the states&lt;/span&gt; if you ignore sales tax/VAT.&lt;br /&gt;&lt;br /&gt;Here's a summary of the prices for the 3 models in Us Dollars (USD), Canadian Dollars (CAD), Punds Sterling (GBP), South African Rand (ZAR) and Hong Kong Dollars (HKD). Prices were taken from the official apple websites, except in South African where the &lt;span style="text-decoration: underline;"&gt;&lt;/span&gt;&lt;a href="http://www.zastore.co.za/"&gt;ZA Store&lt;/a&gt; is one of the official retailers.&lt;br /&gt;&lt;br /&gt;&lt;table class="nobr"&gt;&lt;br /&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;br /&gt;&lt;/td&gt;&lt;th&gt;15" 2.4GHz&lt;/th&gt;&lt;th&gt;15"2.5 GHz&lt;/th&gt;&lt;th&gt;17" 2.5GHz&lt;/th&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td&gt;US&lt;/td&gt;&lt;td&gt;1999&lt;/td&gt;&lt;td&gt;2499&lt;/td&gt;&lt;td&gt;2799&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td&gt;CAD&lt;/td&gt;&lt;td&gt;2099&lt;/td&gt;&lt;td&gt;2599&lt;/td&gt;&lt;td&gt;2899&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td&gt;GBP&lt;/td&gt;&lt;td&gt;1299&lt;/td&gt;&lt;td&gt;1599&lt;/td&gt;&lt;td&gt;1799&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td&gt;ZAR&lt;/td&gt;&lt;td&gt;18499&lt;/td&gt;&lt;td&gt;22999&lt;/td&gt;&lt;td&gt;25999&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td&gt;HKD&lt;/td&gt;&lt;td&gt;15400&lt;/td&gt;&lt;td&gt;19200&lt;/td&gt;&lt;td&gt;21500&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;Using the following VAT and exchange rates (as on 31/3/2008 and 1/4/2008. A quick investigation revealed Hong Kong at 0%. I could be wrong):&lt;br /&gt;&lt;br /&gt;&lt;table class="nobr"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;th&gt;Country&lt;/th&gt;&lt;th&gt;VAT&lt;br /&gt;&lt;/th&gt;&lt;th&gt;&lt;a href="http://www.xe.com/"&gt;Exchange Rate&lt;/a&gt;&lt;/th&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td&gt;USD&lt;/td&gt;&lt;td&gt;0&lt;br /&gt;&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td&gt;CAD&lt;/td&gt;&lt;td&gt;0&lt;br /&gt;&lt;/td&gt;&lt;td&gt;1.02&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td&gt;GBP&lt;/td&gt;&lt;td&gt;18&lt;br /&gt;&lt;/td&gt;&lt;td&gt;0.50&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td&gt;ZAR&lt;/td&gt;&lt;td&gt;14&lt;br /&gt;&lt;/td&gt;&lt;td&gt;8.12&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td&gt;HKD&lt;/td&gt;&lt;td&gt;0&lt;br /&gt;&lt;/td&gt;&lt;td&gt;7.79&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;we get the pre-tax cost in US Dollars:&lt;br /&gt;&lt;br /&gt;&lt;table class="nobr"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;br /&gt;&lt;/td&gt;&lt;th&gt;15" 2.4GHz&lt;/th&gt;&lt;th&gt;15"2.5 GHz&lt;/th&gt;&lt;th&gt;17" 2.5GHz&lt;/th&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td&gt;US&lt;/td&gt;&lt;td&gt;1999&lt;/td&gt;&lt;td&gt;2499&lt;/td&gt;&lt;td&gt;2799&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td&gt;CAD&lt;/td&gt;&lt;td&gt;2049.8&lt;/td&gt;&lt;td&gt;2538.09&lt;/td&gt;&lt;td&gt;2813.05&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td&gt;GBP&lt;/td&gt;&lt;td&gt;2197.88&lt;/td&gt;&lt;td&gt;2705.47&lt;/td&gt;&lt;td&gt;3043.86&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td&gt;ZAR&lt;/td&gt;&lt;td&gt;1997.93&lt;/td&gt;&lt;td&gt;2483.94&lt;/td&gt;&lt;td&gt;2807.95&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td&gt;HKD&lt;/td&gt;&lt;td&gt;1977.64&lt;/td&gt;&lt;td&gt;2465.63&lt;/td&gt;&lt;td&gt;2760.99&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;At a glance, the UK price is consistently higher than any others, and remember this is &lt;span style="font-style: italic;"&gt;without VAT&lt;/span&gt;. UK VAT is at 17.5%, the highest of the 5, which will make the post-tax prices &lt;span style="font-style: italic;"&gt;even higher&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;And if you prefer a pretty picture (&lt;a href="http://code.google.com/apis/chart/"&gt;using Google Charts API&lt;/a&gt;):&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_bXDgUoT7V7o/R_IlQ3pEvwI/AAAAAAAAAGc/Sp-6Rq1HaZQ/s1600-h/MacDiff.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://4.bp.blogspot.com/_bXDgUoT7V7o/R_IlQ3pEvwI/AAAAAAAAAGc/Sp-6Rq1HaZQ/s400/MacDiff.png" alt="" id="BLOGGER_PHOTO_ID_5184247092801224450" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;And now onto the MacbookPro index. If you haven't heard about the &lt;a href="http://en.wikipedia.org/wiki/Big_Mac_Index"&gt;Big Mac&lt;/a&gt; index, it is an informal way of measuring purchasing power parity between countries. It can be used to estimate if a currency is over or undervalued, since a Big Mac is pretty much the same in every country. It is calculated by dividing the price of a Big Mac in one country, by that it another country, and comparing this value to the actual exchange rate.&lt;br /&gt;&lt;br /&gt;For the MacbookPro index, I've used an average price (non-weighted for simplicity), and here are the results:&lt;br /&gt;&lt;br /&gt;&lt;table class="nobr"&gt;&lt;br /&gt;&lt;tbody&gt;&lt;tr&gt;&lt;th&gt;Currency&lt;/th&gt;&lt;th&gt;Under valuation [%]&lt;/th&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td&gt;CAD&lt;/td&gt;&lt;td&gt;0.02&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td&gt;GBP&lt;/td&gt;&lt;td&gt;8.91&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td&gt;ZAR&lt;/td&gt;&lt;td&gt;-0.1&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td&gt;HKD&lt;/td&gt;&lt;td&gt;-1.27&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;and the graph:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_bXDgUoT7V7o/R_IwtnpEvyI/AAAAAAAAAGs/frmR3f3kojI/s1600-h/MacbookProIndex.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://3.bp.blogspot.com/_bXDgUoT7V7o/R_IwtnpEvyI/AAAAAAAAAGs/frmR3f3kojI/s400/MacbookProIndex.png" alt="" id="BLOGGER_PHOTO_ID_5184259681350369058" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;You could conclude that the pound is undervalued by about 9%. I wouldn't. I would conclude that Macs are overpriced in the UK. I think I'll get mine elsewhere...</description><link>http://21ccw.blogspot.com/2008/04/macbookpro-index.html</link><author>noreply@blogger.com (Benjamin Nortier)</author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_bXDgUoT7V7o/R_IlQ3pEvwI/AAAAAAAAAGc/Sp-6Rq1HaZQ/s72-c/MacDiff.png' height='72' width='72'/><thr:total>2</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-7435412383881764254.post-6230491151259415119</guid><pubDate>Wed, 19 Mar 2008 14:35:00 +0000</pubDate><atom:updated>2008-04-01T06:01:09.108-07:00</atom:updated><category domain='http://www.blogger.com/atom/ns#'>Apple</category><category domain='http://www.blogger.com/atom/ns#'>Macbook Pro</category><title>The Mac is Back*</title><description>* No, not &lt;a href="http://www.economist.com/world/na/displaystory.cfm?story_id=10498767"&gt;John McCain&lt;/a&gt;, I'm talking about the Apple Mac.&lt;br /&gt;&lt;br /&gt;I'm going to make a wild prediction:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;The Apple Mac will increase it's market share by more than 50% over the next two years from 4.8% to at least 7.5 %&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;I'm basing my speculation on some figures:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://www.macworld.com/article/51932/2006/07/marketshare.html"&gt;Mac market share went up 16% year-on-year&lt;/a&gt; (Feb 2008. From 4.4 to 4.8% )&lt;/li&gt;&lt;li&gt;&lt;a href="http://arstechnica.com/journals/apple.ars/2008/03/18/apple-spanks-rest-of-computer-industry-in-february-sales"&gt;Macs represented 14 percent of sales last month&lt;/a&gt; (Feb 2008)&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.intomobile.com/2008/02/05/apple-iphone-keeps-getting-stronger-in-smartphone-segment.html"&gt;Solid iPhone market share figures in the US&lt;/a&gt; (28% in the US for Q4 2007)&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;And some opinion:&lt;br /&gt;&lt;br /&gt;First, &lt;a href="http://en.wikipedia.org/wiki/Halo_effect"&gt;the Halo effect&lt;/a&gt;. Apple now has a solid (well integrated) product offering with the Mac line, iPods, iPhones and iTunes (music and movies). Consumers who are exposed to any of these products have (mostly) good experiences, which means they get warm and fuzzy inside and buy more Apple products. (&lt;a href="http://arstechnica.com/journals/apple.ars/2008/01/28/steve-jobs-reassures-investors-employees-over-stock-drop"&gt;but NOT shares.&lt;/a&gt; Hmmmm). Why are their product doing so well despite their higher prices? My first axiom:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;People are always willing to pay more for a better quality product&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Secondly, some perception. I work in technology (creating software), and I would hope that we have a better idea of where the market is heading that the average consumer. I would also like to think that we have some influence over how people will be using their computers in the future, especially though the products and services that we create. I'm seeing more and more Macs being used by the creators of software, &lt;span style="font-style: italic;"&gt;especially by those who are more discerning&lt;/span&gt;,  and my perception is that there is an increasingly positive view of the Mac and Mac OS X in the amongst software creators. Couple that with the buzz that has been created by the iPhone and the iPhone SDK (which delivers a platform for writing software for the iPhone by 3rd party developers), and the future looks rosy.&lt;br /&gt;&lt;br /&gt;Lastly, Windows Vista. No matter what Microsoft says, it hasn't been a big success. All the problems associated with it's (much delayed) launch, the quality of the product, the whole "&lt;a href="http://seattlepi.nwsource.com/business/352442_vista23.html"&gt;Vista Capable&lt;/a&gt;" fiasco etc. has created a unique opportunity for Mac OS X.&lt;br /&gt;&lt;br /&gt;Let's wait and see...</description><link>http://21ccw.blogspot.com/2008/03/mac-is-back.html</link><author>noreply@blogger.com (Benjamin Nortier)</author><thr:total>1</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-7435412383881764254.post-1712284182946334591</guid><pubDate>Thu, 06 Mar 2008 14:44:00 +0000</pubDate><atom:updated>2008-04-01T06:01:38.417-07:00</atom:updated><category domain='http://www.blogger.com/atom/ns#'>IE</category><category domain='http://www.blogger.com/atom/ns#'>Safari</category><category domain='http://www.blogger.com/atom/ns#'>Opera</category><category domain='http://www.blogger.com/atom/ns#'>Acid3</category><category domain='http://www.blogger.com/atom/ns#'>Firefox</category><title>Acid3 Test released</title><description>What is &lt;a href="http://acid3.acidtests.org/"&gt;Acid3&lt;/a&gt;? From &lt;a href="http://en.wikipedia.org/wiki/Acid3"&gt;Wikipedia&lt;/a&gt;: "&lt;b&gt;Acid3&lt;/b&gt; is a test suite that checks how well a &lt;a href="http://en.wikipedia.org/wiki/Web_browser" title="Web browser"&gt;web browser&lt;/a&gt; follows certain &lt;a href="http://en.wikipedia.org/wiki/Web_standards" title="Web standards"&gt;web standards&lt;/a&gt;, especially relating to the &lt;a href="http://en.wikipedia.org/wiki/Document_Object_Model" title="Document Object Model"&gt;DOM&lt;/a&gt; and &lt;a href="http://en.wikipedia.org/wiki/JavaScript" title="JavaScript"&gt;JavaScript&lt;/a&gt;."&lt;br /&gt;&lt;br /&gt;There's some synopsis &lt;a href="http://www.drunkenfist.com/temp/acid3.html"&gt;here&lt;/a&gt; and on the wikipedia page&lt;br /&gt;&lt;br /&gt;Here are the results of the browsers that I have on my work machine (Windows XP). Note that this is a score out of 100:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_bXDgUoT7V7o/R9AKP4NB88I/AAAAAAAAAGE/9RLsdzhhoQc/s1600-h/Acid3.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://3.bp.blogspot.com/_bXDgUoT7V7o/R9AKP4NB88I/AAAAAAAAAGE/9RLsdzhhoQc/s400/Acid3.png" alt="" id="BLOGGER_PHOTO_ID_5174647239750054850" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;No surprise that IE, the bastion of web standards, fails so miserably. I'm actually surprised that Firefox is doing better than Opera, since my subjective opinion at the moment is that Opera renders most pages better than Firefox (except the Google ones, which is not much of a surprise since Google &lt;a href="http://blogs.zdnet.com/BTL/?p=6715"&gt;funds a large chunk of Firefox development&lt;/a&gt;). Also, the first time I tried the test in Opera 9.26, it crashed the browser. And it just crashed again now with Acid3 open.&lt;br /&gt;&lt;br /&gt;All these 4 browsers have big releases coming up, so it remains to be seen who become the new king of the Acid hill. Webkit based browsers like Safari seems to be in the early lead with scores of 90/100 floating around...</description><link>http://21ccw.blogspot.com/2008/03/acid3-test-released.html</link><author>noreply@blogger.com (Benjamin Nortier)</author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_bXDgUoT7V7o/R9AKP4NB88I/AAAAAAAAAGE/9RLsdzhhoQc/s72-c/Acid3.png' height='72' width='72'/><thr:total>5</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-7435412383881764254.post-1614764708926264525</guid><pubDate>Wed, 05 Mar 2008 09:58:00 +0000</pubDate><atom:updated>2008-03-05T06:05:29.573-08:00</atom:updated><category domain='http://www.blogger.com/atom/ns#'>Yaws</category><category domain='http://www.blogger.com/atom/ns#'>Erlang</category><category domain='http://www.blogger.com/atom/ns#'>Apache</category><category domain='http://www.blogger.com/atom/ns#'>FUSE</category><title>Apache + Erlang for dynamic web content</title><description>It dawned on me this morning that there is a way of using &lt;a href="http://www.apache.org/"&gt;Apache&lt;/a&gt; (or any other web server for that matter) with &lt;a href="http://www.erlang.org/"&gt;Erlang&lt;/a&gt; to create dynamic web content...&lt;br /&gt;&lt;br /&gt;How? Use &lt;a href="http://fuse.sourceforge.net/"&gt;FUSE&lt;/a&gt;. There is an implementation of FUSE for Erlang by the &lt;a href="http://dukesoferl.blogspot.com/"&gt;Dukes of Erl&lt;/a&gt;, called &lt;a href="http://code.google.com/p/fuserl/"&gt;fuserl&lt;/a&gt;. If you haven't heard of FUSE, it's a way to make anything you want look like a file system. By using fuserl, you can make an Mnesia database  (just one example) look like a file system. You can map queries to directories, and the files that are "listed" can contain the rows of your tables. But you can structure your filesystem in any way you want!&lt;br /&gt;&lt;br /&gt;Take for example &lt;a href="http://code.google.com/p/youtubefs/"&gt;YoutubeFS&lt;/a&gt;: "&lt;span style="font-style: italic;"&gt;YoutubeFS enables you to browse your favorite Youtube videos locally on your desktop without going to the youtube website. Just create a youtube account and add videos to your playlists, favorites list or subscribe to different channels. YoutubeFS then enables you to automatically load these videos to a local folder on your desktop. You can then view these videos (using a browser) as if they are local files.&lt;/span&gt;"&lt;br /&gt;&lt;br /&gt;If you extend this idea, you can imagine that you can point your Apache server to a FUSE filesystem location which generates the "contents" of the file system dynamically. To Apache it looks like it's serving normal files, but behind the scenes you can have a distributed Erlang system generating content for you...&lt;br /&gt;&lt;br /&gt;This wouldn't work so well if you actually want to use parameters in your http queries, but then I would use &lt;a href="http://yaws.hyber.org/"&gt;Yaws &lt;/a&gt;with an appmod if that's what you need.&lt;br /&gt;&lt;br /&gt;P.S. You don't have to use Erlang either, you can write FUSE file systems in many other languages...</description><link>http://21ccw.blogspot.com/2008/03/apache-erlang-for-dynamic-web-content.html</link><author>noreply@blogger.com (Benjamin Nortier)</author><thr:total>0</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-7435412383881764254.post-8714167263367784337</guid><pubDate>Mon, 25 Feb 2008 18:25:00 +0000</pubDate><atom:updated>2008-02-27T01:48:39.540-08:00</atom:updated><category domain='http://www.blogger.com/atom/ns#'>Yaws</category><category domain='http://www.blogger.com/atom/ns#'>Dayfindr.com</category><category domain='http://www.blogger.com/atom/ns#'>Erlang</category><title>The time has come</title><description>I've been working on an Erlang project at home for a while, and it has reached the first public version! Here it is &lt;a href="http://www.dayfindr.com/"&gt;http://www.dayfindr.com.&lt;/a&gt; It's a simple utility that can make your life easier.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_bXDgUoT7V7o/R8QFTtqHpxI/AAAAAAAAAFU/6aVjiEqdGZU/s1600-h/dayfindr.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://4.bp.blogspot.com/_bXDgUoT7V7o/R8QFTtqHpxI/AAAAAAAAAFU/6aVjiEqdGZU/s400/dayfindr.png" alt="" id="BLOGGER_PHOTO_ID_5171264108360083218" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;It ticks some of the boxes that Paul Graham related recently in &lt;a href="http://www.paulgraham.com/newthings.html"&gt;Six Principles for Making things:&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;(a) simple solutions &lt;span style="color: rgb(0, 153, 0); font-style: italic;"&gt;I hope so. This was one of the big design principles - to make it as simple as possible. That's why, for example, it leverages email instead of using some fancy social networking/address book features. There's also no login, since your email address already identifies you. &lt;a href="http://www.tripit.com/"&gt;TripIt &lt;/a&gt;uses the same idea&lt;/span&gt;&lt;br /&gt;(b) to overlooked problems &lt;span style="font-style: italic; color: rgb(0, 153, 0);"&gt;nothing that I know of addresses it&lt;br /&gt;&lt;/span&gt;(c) that actually need to be solved, and &lt;span style="font-style: italic; color: rgb(0, 153, 0);"&gt;This tries to help with  a real problem that I've encountered myself. Feedback on the idea has been quite positive, so I know it's something that people will find useful! &lt;/span&gt;&lt;br /&gt;(d) deliver them as informally as possible &lt;span style="font-style: italic; color: rgb(0, 153, 0);"&gt;Hope so&lt;/span&gt;&lt;br /&gt;(e) starting with a very crude version 1, then &lt;span style="font-style: italic; color: rgb(0, 153, 0);"&gt;Relatively crude&lt;/span&gt;&lt;br /&gt;(f) iterating rapidly. &lt;span style="font-style: italic; color: rgb(0, 153, 0);"&gt;We'll see!&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;It's implemented on a Lyme stack (Linux, &lt;a href="http://yaws.hyber.org/"&gt;Yaws&lt;/a&gt;, Mnesia and &lt;a href="http://www.erlang.org/"&gt;Erlang&lt;/a&gt;). This project has generated some interesting questions, that I will relate in a series of posts. For example:&lt;br /&gt;&lt;br /&gt;Why &lt;a href="http://www.erlang.org/"&gt;Erlang&lt;/a&gt;?&lt;br /&gt;Why not &lt;a href="http://erlyweb.org/"&gt;ErlyWeb&lt;/a&gt;?&lt;br /&gt;Why not &lt;a href="http://www.rubyonrails.org/"&gt;Rails&lt;/a&gt;?&lt;br /&gt;Why did I abandon the fully functional &lt;a href="http://en.wikipedia.org/wiki/Common_Lisp"&gt;Lisp&lt;/a&gt; version?&lt;br /&gt;&lt;br /&gt;So, use it, it's free. If you like it (or more importantly if you don't), lemme know at &lt;a href="mailto:feedback@dayfindr.com"&gt;feedback@dayfindr.com&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;P.S. To the Internet Explorer users: You will notice that there's a javascript error on the page, because some function is "not implemented", which means you'll see check boxes and not coloured boxes in the calendar. The current versions of &lt;a href="http://www.opera.com/"&gt;Opera&lt;/a&gt;, &lt;a href="http://www.mozilla.com/firefox/"&gt;Firefox&lt;/a&gt; and &lt;a href="http://www.apple.com/safari/"&gt;Safari&lt;/a&gt; have no problem with it. Maybe you should consider an upgrade...</description><link>http://21ccw.blogspot.com/2008/02/time-has-come.html</link><author>noreply@blogger.com (Benjamin Nortier)</author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_bXDgUoT7V7o/R8QFTtqHpxI/AAAAAAAAAFU/6aVjiEqdGZU/s72-c/dayfindr.png' height='72' width='72'/><thr:total>9</thr:total></item></channel></rss>