<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-5297772973487593985</id><updated>2012-01-27T17:49:48.843-08:00</updated><category term='processes'/><category term='make'/><category term='version control system'/><category term='java'/><category term='erlang'/><category term='Dream'/><category term='bitcoin'/><category term='vala'/><category term='software'/><category term='Family'/><category term='haskell'/><category term='programming'/><category term='issue tracking'/><category term='review'/><category term='restructuredtext'/><category term='mercurial'/><category term='golang'/><category term='rust'/><category term='markup'/><category term='scotch'/><category term='subversion'/><title type='text'>Coruscating Lucubrations</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://blog.ser1.net/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://blog.ser1.net/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>SER</name><uri>http://www.blogger.com/profile/11674189468540726402</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>56</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-5297772973487593985.post-6181162695791243145</id><published>2012-01-26T05:31:00.000-08:00</published><updated>2012-01-26T18:51:29.852-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='software'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='golang'/><category scheme='http://www.blogger.com/atom/ns#' term='rust'/><title type='text'>Mozilla's Rust Language</title><content type='html'>Mozilla released version 0.1 of it's programming language offering called &lt;a href="http://www.rust-lang.org/"&gt;Rust&lt;/a&gt;. &amp;nbsp;There are a number of things about Rust which are nice; it uses LLVM, which means it gets tail-call-optimization, which Go doesn't have; it has isolated, lightweight tasks, and channels much like Go; I've come across discussions about Erlang-style supervisors, which is encouraging; it has a Ruby-like syntax for closures; it has some limited type inferrence (very similar in scope to what Go can do); and it has pattern matching a-la OCaml, which Go doesn't. &amp;nbsp;On top of all of this, the executables it produces for small applications are two orders of magnitude smaller than those produced by Go (Go's "Hello World" is 1.2MB; Rust's is 14KB). &amp;nbsp;I don't have any performance comparisons yet, but I wouldn't be surprised if Rust was (currently) faster than Go. Go's compiler is faster, the value of which increases proportional to the size of the project on which you're working.&lt;br /&gt;&lt;br /&gt;Despite all of this, I haven't switched over to Rust yet; it's a touchy-feel-ey reason, more than anything concrete. &amp;nbsp;Go feels more mature, despite Rust being older. &amp;nbsp;The Go library documentation is much better, and the developer tools feel more complete. &amp;nbsp;And it's just easier to code in; Rust is a bit more wordy in tiny little ways. &amp;nbsp;For example, to do something multithreaded, in Rust you'll end up doing something like this:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;  let in = comm::port::int&lt;int&gt;();&lt;br /&gt;  let out = comm::chan::int&lt;int&gt;(in);&lt;br /&gt;  task::spawn { ||&lt;br /&gt;    some_func(out);&lt;br /&gt;  }&lt;br /&gt;  let res = comm::recv(in);&lt;br /&gt;&lt;/int&gt;&lt;/int&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;ports are for writing to, and chans are for reading from, and never the twain shall meet. &amp;nbsp;In Go, the equivalent would be:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;  var inout = make(chan int)&lt;br /&gt;  go some_func(inout)&lt;br /&gt;  res := &amp;lt;-inout&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;There in't a huge difference, but the Rust version is more wordy, and not any more clear. &amp;nbsp;I just get a tiny, nagging feeling that, over time, the verbosity of Rust would start to wear. &amp;nbsp;Especially with the whole port/chan thing, which smells an awful lot like boiler-plate.&lt;br /&gt;&lt;br /&gt;The other thing is that I keep having trouble writing threaded apps in Rust. &amp;nbsp;At some point in every attempt, something or other blocks me and I can't get around it. &amp;nbsp;Most recently, it had to do with the fact that you can't send a port to a function if that function is in another task. &amp;nbsp;This means that you can't share a port between tasks. &amp;nbsp;You can't do this:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;   let in = comm::port::int&lt;int&gt;();&lt;br /&gt;   task::spawn { || comm::recv(in); }&lt;br /&gt;&lt;/int&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The Rust compiler raises this as an error (unsendable value). &amp;nbsp;I will readily admit that this is almost certainly a failure on my part to understand the idiomatic&amp;nbsp;idiosyncrasies&amp;nbsp;of &amp;nbsp;Rust, and that there may be a way to accomplish this, but my question remains: if you can't share ports between tasks, then how do you implement a single-producer / multiple-consumer multi-threaded application? &amp;nbsp;I didn't find any code in any of the examples in src/test (in the Rust repository) which demonstrated something like this, and when I asked on #rust IRC, my question was met with silence.&lt;br /&gt;&lt;br /&gt;What it all boils down to is that, at least for me, Rust is being a PITA to get started with, whereas Go wasn't. &amp;nbsp;And in some ways, they're really very similar languages; they both have the "arg TYPE" ordering of type declarations; they both have "type SOMETHING {" structure for records; they have similar threading models and channel communications... but Google, in my mind, at least, has been much more successful at making Go accessible than Mozilla has with Rust. &amp;nbsp;So for now, I'm going to continue working with Go, and keep an eye on developments in Rust.&lt;br /&gt;&lt;br /&gt;P.S., I can't properly annotate the Rust examples because blogger.com doesn't handle less-than / greater-than signs.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5297772973487593985-6181162695791243145?l=blog.ser1.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ser1.net/feeds/6181162695791243145/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5297772973487593985&amp;postID=6181162695791243145' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/6181162695791243145'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/6181162695791243145'/><link rel='alternate' type='text/html' href='http://blog.ser1.net/2012/01/mozillas-rust-language.html' title='Mozilla&apos;s Rust Language'/><author><name>SER</name><uri>http://www.blogger.com/profile/11674189468540726402</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5297772973487593985.post-7865738030998703430</id><published>2011-12-08T18:29:00.001-08:00</published><updated>2011-12-08T18:39:28.122-08:00</updated><title type='text'>Droid Incredible 2</title><content type='html'>I have a new company phone; I got it yesterday, and have been spending the usual amount of time getting it set up and playing with it.&amp;nbsp; I'm pleasantly surprised by the battery life, actually; at the time of this writing, the phone has been on for 31 hours and has about 40% battery left; 39% has been used by the display.&amp;nbsp; I don't know how much time I've spent in calls, but I've made or received 12 of them in that 31 hours.&amp;nbsp; Not too shabby.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5297772973487593985-7865738030998703430?l=blog.ser1.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ser1.net/feeds/7865738030998703430/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5297772973487593985&amp;postID=7865738030998703430' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/7865738030998703430'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/7865738030998703430'/><link rel='alternate' type='text/html' href='http://blog.ser1.net/2011/12/droid-incredible-2.html' title='Droid Incredible 2'/><author><name>SER</name><uri>http://www.blogger.com/profile/11674189468540726402</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5297772973487593985.post-3019535848069900141</id><published>2011-10-27T03:09:00.000-07:00</published><updated>2011-10-27T03:09:06.599-07:00</updated><title type='text'>The cult of technology personality</title><content type='html'>Recently, I've come to the conclusion that products are irrelevant; popularity is all in branding and marketing.  Us developers (of hardware and software) like to kid ourselves into thinking that we're the ones who do the "real work,"but really, it's the sales and marketing people who are the backbone.  Apple didn't "invent" the smartphone, any more than they invented the MP3 player (they were three years late on that), or the laptop, or the slate PC (again,late by several years), or any of the other stuff they've been successful with in the past ten years..  They've just been able to corner the "sexy" market, throughgood advertising and branding.  I think that since Jobs returned to thecompany, they also payed more attention to quality and product polish, and werewilling to sacrifice volume in the increased costs that often incurred; he had the same attention to detail at NeXT, although he failed to identify the right market for that platform.  But Ireally think what makes a successful product is the cult of personality.&lt;br /&gt;&lt;br /&gt;Other examples:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Microsoft. There is almost always been a better competing product towhatever Microsoft is selling, but Microsoft managed to capture the Business sector with its early and intimate association with IBM.  Even OS/2, an arguably better OS, couldn't wrestle that crown away, and that's because they didn't have Bill Gates, not because it was a technically inferior product.&lt;/li&gt;&lt;li&gt;Linux.  Minix predates Linux, and had the potential to be as successful as Linux, and can be argued to have a better architecture, but Tanenbaum haddifferent priorities and isn't, I dare suggest, the personality that Linus is.  Or, if you don't like microkernels, BSD.  Same thing: they don't lack technology, they lack Linus.&lt;/li&gt;&lt;li&gt;Java.  There are many at &lt;i&gt;least&lt;/i&gt; equivalent languages out there, even if you restrict yourself to the OO space, but none of them had Sun behindit.&amp;nbsp; Sun pushed Java aggressively.   I'm not going to credit McNeally or Goslingdirectly for that; I don't think there was a personality behind that one, justaggressive and persistent marketing.&lt;/li&gt;&lt;/ul&gt;History is littered with the detritus of better products that lost to inferior products, simply because of better marketing. &lt;br /&gt;&lt;ul&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5297772973487593985-3019535848069900141?l=blog.ser1.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ser1.net/feeds/3019535848069900141/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5297772973487593985&amp;postID=3019535848069900141' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/3019535848069900141'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/3019535848069900141'/><link rel='alternate' type='text/html' href='http://blog.ser1.net/2011/10/cult-of-technology-personality.html' title='The cult of technology personality'/><author><name>SER</name><uri>http://www.blogger.com/profile/11674189468540726402</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5297772973487593985.post-955605013472368363</id><published>2011-10-06T17:15:00.000-07:00</published><updated>2011-10-06T17:15:30.206-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='software'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='golang'/><title type='text'>Universal truths</title><content type='html'>The fact that channel communications (whatever their implementation details) take much, much longer than function calls seems to be a constant truth no matter what the programming language.&amp;nbsp; I don't have the benchmarks for Erlang offhand, but here are ones that I just recently ran for Go:&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;main.BenchmarkChannel-2&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 500000&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 6106 ns/op&lt;br /&gt;main.BenchmarkFunction-2&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 100000000&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 10.2 ns/op&lt;br /&gt;main.BenchmarkAnonymous-2&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 100000000&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 11.5 ns/op&lt;/div&gt;&lt;br /&gt;Here's the source code in case you want to pick holes in my benchmark:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;package main&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;import "testing"&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;func BenchmarkChannel(b *testing.B) {&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; c := make(chan int, 1000)&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; accum := 0&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; go func() { for { accum += &amp;lt;-c } }()&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; for i := 0; i &amp;lt; b.N; i++ {&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; c &amp;lt;- i&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;}&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;func BenchmarkFunction(b *testing.B) {&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; accum := 0&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; for i := 0; i &amp;lt; b.N; i++ {&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; handle(i, &amp;amp;accum)&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;}&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;func handle(v int, accum *int) {&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; *accum += v&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;}&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;func BenchmarkAnonymous(b *testing.B) {&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; accum := 0&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; f := func(i int) { accum += i }&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; for i := 0; i &amp;lt; b.N; i++ {&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; f(i)&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;This was run with:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;gotest -run -cpu=2 -bench='.*'&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Changing GOMAXPROCS didn't make any difference. I was running it on a Core 2 Duo.&amp;nbsp; It's not hugely surprising, but still.&amp;nbsp; It makes me wonder how the ratio of performance compares between languages.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5297772973487593985-955605013472368363?l=blog.ser1.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ser1.net/feeds/955605013472368363/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5297772973487593985&amp;postID=955605013472368363' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/955605013472368363'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/955605013472368363'/><link rel='alternate' type='text/html' href='http://blog.ser1.net/2011/10/universal-truths.html' title='Universal truths'/><author><name>SER</name><uri>http://www.blogger.com/profile/11674189468540726402</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5297772973487593985.post-1364301632132989945</id><published>2011-09-21T05:22:00.000-07:00</published><updated>2011-09-21T05:22:44.429-07:00</updated><title type='text'>Just a rant</title><content type='html'>I need to get this out of my system.&amp;nbsp; It's probably better if you just go ahead and skip this rant; there's nothing constructive in it.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size: large;"&gt;Windows&lt;/span&gt;&lt;br /&gt;Windows sucks.&amp;nbsp; There's really nothing more to say about it; it's a horrible operating system, and I don't know why anybody -- especially anybody in a management position -- would think that it's suitable for running a business on.&amp;nbsp; It's buggy, slow, bloated, and obtuse.&amp;nbsp; It's &lt;b&gt;so &lt;/b&gt;bad at being a server, I don't even know where to begin criticizing it.&amp;nbsp; And as bad as it sucks being a server, it really sucks at being a desktop computer, too. There really is nothing redeeming about it.&amp;nbsp; Using it is a painful chore; it's my personal belief that anybody who thinks otherwise has simply been conditioned to believe so.&amp;nbsp; It's like, if you hit somebody frequently and often enough, they get used to being beaten.&amp;nbsp; That's Windows.&amp;nbsp; It is like being beaten by an entire company.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size: large;"&gt;Dell&lt;/span&gt;Dell hardware sucks.&amp;nbsp; I have both a (personal) two year old MacBook Pro and a one-year-old (company) Dell Latitude E6410.&amp;nbsp; If I put the Dell to sleep (not hibernate), the battery gives out within 48 hours.&amp;nbsp; If I put the Mac to sleep, the battery gives out after... well, I don't know how long, because&amp;nbsp; I've never had the battery give out.&amp;nbsp; It lasts at least a week, though; I went backpacking once and was gone for about a week, and the Mac still woke up after I got back.&amp;nbsp; Granted, the Dell does have more memory than the Mac (8GB vs 4GB), and that's going to affect the battery drain.&amp;nbsp; That's still pretty pathetic.&amp;nbsp; And the battery on the Mac lasts a lot longer; I can get a full 6 hours out of it, with constant use (if not heavy load)... the Dell gives out in about 3.&amp;nbsp;&amp;nbsp; The CPUs in both have two cores each; Windows claims the Dell's CPU is running at 2.4GHz, and OSX claims it's running at 2.5GHz.&amp;nbsp; The Mac's display is larger (15" vs. 14"), and that's a big battery drain.&amp;nbsp; Of course, the Dell &lt;b&gt;is&lt;/b&gt; running Windows, so you have suckage upon suckage.&amp;nbsp; Windows is probably doing more to kill the battery life than the Dell hardware.&amp;nbsp; I've heard that the Mac will hibernate itself &lt;i&gt;while it is asleep&lt;/i&gt; if it detects that the battery is going to give out.&amp;nbsp; Again, I wouldn't know, because I've never had the battery drain entirely while the Mac was asleep, and because OSX does so much of this smart stuff behind the scenes.&amp;nbsp; &lt;br /&gt; &lt;br /&gt;If I close the lid on my Mac, it goes to sleep.&amp;nbsp; If I open the lid, it wakes up.&amp;nbsp; All of the Dells in our company have this feature turned off because if you sleep a Windows laptop by closing the lid, odds are good that the laptop will never wake up, or won't be able to find a wireless connection after it resumes, or something.&amp;nbsp; That's why you see people walking the halls with their laptop lids only partially closed.&amp;nbsp; Every time you see that, just remember: Windows sucks.&lt;br /&gt;&lt;br /&gt; &lt;br /&gt;All of the managers above a certain level at my company get Macs.&amp;nbsp; All of the peons get Dells.&amp;nbsp; Honestly, the Macs aren't much more expensive.&amp;nbsp; A new 15" Pro starts at $1800 for a quad-core 2GHz 4GB RAM 500GB HD; a new 14" E6410 dual-core 2.8GHz 4GB RAM 380GB HD starts at $1400.&amp;nbsp; Not much difference in price there -- and you're getting better, newer hardware in the Mac.&lt;br /&gt;&lt;br /&gt;It baffles me why people are still running their businesses on Windows, and on Dell hardware.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5297772973487593985-1364301632132989945?l=blog.ser1.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ser1.net/feeds/1364301632132989945/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5297772973487593985&amp;postID=1364301632132989945' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/1364301632132989945'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/1364301632132989945'/><link rel='alternate' type='text/html' href='http://blog.ser1.net/2011/09/just-rant.html' title='Just a rant'/><author><name>SER</name><uri>http://www.blogger.com/profile/11674189468540726402</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5297772973487593985.post-254994283082393729</id><published>2011-09-07T05:54:00.000-07:00</published><updated>2011-09-07T05:55:00.827-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Incremental backups with btrfs</title><content type='html'>btrfs is teh win.&amp;nbsp; No, seriously.&amp;nbsp; It's not the only file system that can do this, but it's the first one I've had installed, and it's beautiful, man.&lt;br /&gt;&lt;br /&gt;I got myself a little 2TB external USB 3.0 hard drive ($99!&amp;nbsp; Past Sean, be very jealous) and wrote a backup script; my first version had all of this complex Towers of Hanoi rotation scheme, but then I realized I didn't need any of it if I used btrfs's snapshots.&amp;nbsp; Now, my backup script consists mainly of:&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;sg_start --start /dev/sdh &amp;nbsp; # Tell the drive to spin up&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;mount /dev/sdh1 /mnt/backups&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;btrfsctl -s /mnt/backups/backup-$todays_date \&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; /mnt/backups/backup-$yesterdays_date&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;rsync -va --numeric-ids --delete-before --ignore-errors \&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; --partial --inplace $backup_paths \&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; /mnt/backups/backup-$todays_date&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;sync&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;umount /mnt/backups&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;sg_start --stop /dev/sdh&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-family: Times,&amp;quot;Times New Roman&amp;quot;,serif;"&gt;It does a little more than that (error checking, logging, etc), but not &lt;i&gt;much&lt;/i&gt; more.&amp;nbsp; After the first backup, the disk usage was 15GB; ten days later, I have ten incremental backups, and it's &lt;i&gt;still&lt;/i&gt; 15GB.&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-family: Times,&amp;quot;Times New Roman&amp;quot;,serif;"&gt;Of course, the problem with this is that snapshots are, essentially, COW hard links; this means that if there's a corruption on the disk for a file, it'll affect all child snapshots.&amp;nbsp; My mitigation is to add another backup disk: they're only $99 (cripes, I can't get over that price), and each backup is taking about 8 minutes to run (most of that time is spent in rsync, detecting changes) -- I can easily affordable to run two backups a night.&amp;nbsp; It's not as safe as a rotation backup, but it's safe enough.&lt;/span&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5297772973487593985-254994283082393729?l=blog.ser1.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ser1.net/feeds/254994283082393729/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5297772973487593985&amp;postID=254994283082393729' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/254994283082393729'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/254994283082393729'/><link rel='alternate' type='text/html' href='http://blog.ser1.net/2011/09/incremental-backups-with-btrfs.html' title='Incremental backups with btrfs'/><author><name>SER</name><uri>http://www.blogger.com/profile/11674189468540726402</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5297772973487593985.post-3457335343296600911</id><published>2011-09-01T11:11:00.000-07:00</published><updated>2011-09-01T11:11:34.607-07:00</updated><title type='text'>tmux magic</title><content type='html'>I recently switched from GNU screen to &lt;a href="http://tmux.sourceforge.net/"&gt;tmux&lt;/a&gt;, so I'm discovering all of these awesome little features. &amp;nbsp;This morning, I found out (accidentally) that you can remotely script sessions, and here's how it came about:&lt;br /&gt;&lt;br /&gt;I added a new back-up disk and back-up scheme (more on that later), and decided to do a little cleaning while I was at it. &amp;nbsp;I've got 10 years of digital photography in one drive, much of which isn't very well organized. &amp;nbsp;There's a lot of duplication resulting from copying and moving things over the years, so I ran &lt;a href="http://code.google.com/p/fdupes/"&gt;fdupes&lt;/a&gt; on that directory and it came back with more than 1,000 duplicate sets. &amp;nbsp;These files are fairly large, and there are a few home videos mixed in there which are even larger, and so fdupes itself took a while to run; when it was done, I had found myself in a situation where I didn't want to press "1&lt;cr&gt;" a thousand times, but I also didn't want to re-run fdupes just to pipe &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;yes n&lt;/span&gt; to it. Luckily, I remembered coming across a &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;send-key&lt;/span&gt; command in the tmux man page, and that proved to be the solution.&lt;/cr&gt;&lt;br /&gt;&lt;br /&gt;It turns out that you can send any tmux command to any tmux session using the the command line, and this allows you to script any interaction you might want to have with tmux or any process that is running within tmux. &amp;nbsp;You can take a (text) snapshot of a window buffer, and you can also send input events to any window. &amp;nbsp;In my particular case, it ended up being the little shell script:&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; &amp;nbsp;for x in `seq 1 1000`; do&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; tmux send-key -t 0:3 1 C-m&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; &amp;nbsp;done&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The tmux command says, "send, to session #0, window #3, the key '1' followed by Ctrl-M". &amp;nbsp;This is really powerful; you can completely script any tmux session, including (for example) a start-up tmux script that runs processes, creates windows, and splits windows into panes.&lt;br /&gt;&lt;br /&gt;tmux still lacks the ability to attach different clients to different windows within a session (or, if it allows that, I haven't yet discovered how to do it), which makes running tmux in an environment such as &lt;a href="http://xmonad.org/"&gt;XMonad&lt;/a&gt; a little less pleasant than screen; however, this ability to do emergency, post-hoc scripting is a killer feature, and has sold me on tmux.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5297772973487593985-3457335343296600911?l=blog.ser1.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ser1.net/feeds/3457335343296600911/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5297772973487593985&amp;postID=3457335343296600911' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/3457335343296600911'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/3457335343296600911'/><link rel='alternate' type='text/html' href='http://blog.ser1.net/2011/09/tmux-magic.html' title='tmux magic'/><author><name>SER</name><uri>http://www.blogger.com/profile/11674189468540726402</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5297772973487593985.post-1901312790126396356</id><published>2011-07-05T20:13:00.000-07:00</published><updated>2011-07-05T20:13:19.277-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='markup'/><title type='text'>RML: Readable Markup Language</title><content type='html'>In answer to &lt;a href="http://goo.gl/P9OmX"&gt;my own post&lt;/a&gt;, I spent last weekend implementing my own markup language. &amp;nbsp;You can find the results &lt;a href="http://goo.gl/5zat7"&gt;here&lt;/a&gt; (and the source &lt;a href="http://goo.gl/sCdtg"&gt;here&lt;/a&gt;). &amp;nbsp;FWIW. &amp;nbsp;It's version 1.0, which means it works, but it doesn't have all of the features I want. &amp;nbsp;A summary can be found &lt;a href="http://goo.gl/OFizF"&gt;here&lt;/a&gt;. &amp;nbsp;I'm pretty pleased with it so far.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5297772973487593985-1901312790126396356?l=blog.ser1.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ser1.net/feeds/1901312790126396356/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5297772973487593985&amp;postID=1901312790126396356' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/1901312790126396356'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/1901312790126396356'/><link rel='alternate' type='text/html' href='http://blog.ser1.net/2011/07/rml-readable-markup-language.html' title='RML: Readable Markup Language'/><author><name>SER</name><uri>http://www.blogger.com/profile/11674189468540726402</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5297772973487593985.post-3545545583727295233</id><published>2011-06-29T05:14:00.000-07:00</published><updated>2011-06-29T05:14:49.574-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='restructuredtext'/><category scheme='http://www.blogger.com/atom/ns#' term='markup'/><title type='text'>Never satisfied</title><content type='html'>Every once in a while I find myself wasting a couple of hours re-trolling through the Wikipedia entry for &lt;a href="http://goo.gl/qFbF"&gt;lightweight markup languages&lt;/a&gt;, searching for the perfect markup system. &amp;nbsp;They are all deficient in some way, and it's becoming increasingly evident that I'm either going to have to pick the least deficient one and write additional software for transforming it (ugh), or come up with my own (yeauck!).&lt;br /&gt;&lt;br /&gt;As a preface, I'll just say that &lt;a href="http://goo.gl/eIDK"&gt;TeX&lt;/a&gt; is a non-starter; any tool that requires TeX (or LaTeX, or any derivation thereof) is not an option. I've said it before: all TeX packages require about a gigabyte of disk space, and TeX doesn't do anything significantly better than what &lt;a href="http://goo.gl/7NFdH"&gt;Lout&lt;/a&gt; does in 11MB. &amp;nbsp;TeX is bloated, and I simply don't want to have anything to do with it; it was fine when I was in college, when the only alternative was MS Word, but there are better alternatives out there, now.&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://goo.gl/ra558"&gt;RestructuredText&lt;/a&gt; has a huge amount of software for converting it into HTML, DocBook, PDF, Slidy, S5, and others, and it has very few dependencies. &amp;nbsp;In particular, rst2pdf can generate PDF using only a small number of Python libraries. &amp;nbsp;It has an awesome table syntax. &amp;nbsp;It is easily extended to support dot diagrams, aafigure, and a host of other embedded images. &amp;nbsp;You can use SVG images in an RST document, and both rst2pdf and rst2html will do the right thing. &amp;nbsp;However, the markup is unintuitive and verbose, and sometimes awkward, and it lacks primitive support for basic things like underscore, superscript, subscript, and strike-through. &amp;nbsp;While you &lt;i&gt;can&lt;/i&gt;&amp;nbsp;do these mark-ups with roles, they all require verbose syntax and both rst2pdf and rst2html don't handle whitespace in roles correctly (e.g., :strike:`- `)&lt;/li&gt;&lt;li&gt;&lt;a href="http://goo.gl/VYf5G"&gt;Asciidoc&lt;/a&gt; has better syntax, although it too has no primitive support for underscore and strike-out. &amp;nbsp;It's main failing is that writing asciidoc is a lot like programming: the conversion tools are extremely strict about syntax, and will utterly fail on any minimal mistake (whereas the RST tools will normally emit a warning and continue on, trying their best). &amp;nbsp;Asciidoc converts everything through DocBook, which isn't itself so bad, but getting something into PDF requires either LaTeX (recommended) or FOP, and the FOP stylesheet needs tweaking to get it to work correctly. &amp;nbsp;Altogether, Asciidoc is too fussy.&amp;nbsp;&lt;/li&gt;&lt;li&gt;Markdown has OK syntax, although they stopped too short on the really useful heading syntax (only two levels with the underline style), and it requires extensions to get anything like table and fenced block syntax. &amp;nbsp;Most damning, there's no way to convert Markdown to PDF without going through LaTeX.&lt;/li&gt;&lt;li&gt;&lt;a href="http://goo.gl/8pmNh"&gt;Creole&lt;/a&gt;&amp;nbsp;and&amp;nbsp;&lt;a href="http://goo.gl/fvXbY"&gt;Textile&lt;/a&gt; have dumb heading syntax -- they're pretty obviously designed to make editing wiki pages easier, not to produce plain ASCII documents that can also be converted into something else. &amp;nbsp;Creole also has ugly list syntax. &amp;nbsp;Textile's heading and blockquote markup is useless for reading the documents in plain-text. &amp;nbsp;In particular, Textile's inline markup is almost perfect; unfortunately, the only conversion tool is to HTML, and the heading and blockquote markup really is unusable.&lt;/li&gt;&lt;ul&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;_emphasis_*strong*??citation??-deleted text-+inserted text+^superscript^~subscript~%span%&lt;/span&gt;&lt;/ul&gt;&lt;ul&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;lt; right&amp;gt; left= center&amp;lt;&amp;gt; justify&lt;/span&gt;&lt;/ul&gt;&lt;ul&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;ADM(Acronym Definition Markup)&lt;/span&gt;&lt;/ul&gt;&lt;li&gt;&lt;a href="http://goo.gl/ek0zn"&gt;Setext&lt;/a&gt; has interesting markup, but is pretty spartan. &amp;nbsp;No table support, strike-through, image embedding; and there are two converters: one for converting to HTML, and one for converting to LaTeX. &amp;nbsp;So, really only one usable (IMO) converter. &amp;nbsp;It might be a good basis for extension into a more robust markup.&lt;/li&gt;&lt;li&gt;The Wikipedia page on &lt;a href="http://goo.gl/8SJNf"&gt;Texy&lt;/a&gt; claims that it's the "most complex" markup language, but it doesn't support strike-out or underline. &amp;nbsp;Otherwise, its syntax is quite interesting, as it its table support. &amp;nbsp;However, there's no software for converting to either PDF or Slidy. &amp;nbsp;The markup for adding style to text is tidy: &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;.{color:red}&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://goo.gl/4yOVJ"&gt;txt2tags&lt;/a&gt;, like RST, can be converted to a wide variety of output, and the syntax is interesting. &amp;nbsp;It has support for underscore and strike-out, but not super or subscript, and the table syntax is not as interesting as Texy's. &amp;nbsp;Actually, as I re-review it, it may be the most interesting of the bunch; I'm a bit concerned about the implied dependency on tabs, which you can't use in text fields on web pages, which would make it inappropriate for wiki. &amp;nbsp;I don't care for the heading markup, but I think I could live with that.&lt;/li&gt;&lt;li&gt;Not mentioned on the Wikipedia entry is &lt;a href="http://goo.gl/7slHa"&gt;AFT&lt;/a&gt; (Almost Free Text), which is one of the original markup languages, and has been around for years. &amp;nbsp;The source documents don't read quite as cleanly as other markups; it's pretty obvious that you're dealing with a markup language. &amp;nbsp;There's no support for strike-out, and it requires LaTeX to generate PDF.&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;I just want something that reads like something you'd have composed in an email prior to HTML multipart-mime messages; simple syntax covering the basic cases (i.e., if I could do it with an old electric typewriter, the markup should be able to handle it as well. &amp;nbsp;The whole &lt;b&gt;purpose&lt;/b&gt;&amp;nbsp;of the underscore key was to underscore words; it baffles me how a modern mark-up language could leave that out.). &amp;nbsp;A rich, but easy, table syntax is a bonus, and being able to create PDF without installing a gigabyte of software (LaTeX) a must. &amp;nbsp;HTML is required, but they all generate HTML; Slidy output is more or less required, too. &amp;nbsp;Additional points are given to support for domain-specific markup, like aafigure (an awesome package for very simple diagrams) and dot -- an easy plug-in for passing blocks of text to an external program, and then being able to embed that output (which will probably be SVG, or some bit image format like PNG) into the resulting document, wins special mention.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;No existing markup package does everything that I want, though. &amp;nbsp;Dare I implement a new one?&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5297772973487593985-3545545583727295233?l=blog.ser1.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ser1.net/feeds/3545545583727295233/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5297772973487593985&amp;postID=3545545583727295233' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/3545545583727295233'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/3545545583727295233'/><link rel='alternate' type='text/html' href='http://blog.ser1.net/2011/06/never-satisfied.html' title='Never satisfied'/><author><name>SER</name><uri>http://www.blogger.com/profile/11674189468540726402</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5297772973487593985.post-5371209928634880244</id><published>2011-06-27T06:12:00.000-07:00</published><updated>2011-06-27T06:12:28.381-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='golang'/><title type='text'>Now we get to the annoying aspects of Go</title><content type='html'>I've spent another ten hours or so writing extensive functional tests for my code, and fixing bugs, so I have some further thoughts about Go.&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: large;"&gt;POLA&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;gotest&lt;/span&gt; continues to be easy to use and is&amp;nbsp;versatile, even if you do have to implement a bunch of basic functionality yourself (there are no convenient "assert" functions, there's no setup or teardown functionality, and nothing like TestNG's &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;DataProvider&lt;/span&gt; mechanism). &amp;nbsp;Even functional testing of a server process is easier than in other languages, although I think most of that is again due to Go helping me separate my code into modules. &amp;nbsp;I also found my first non-contrived reason to &lt;a href="http://goo.gl/XCxCs"&gt;use goroutines&lt;/a&gt; for the first time. &amp;nbsp;I really like goroutines, but that's probably because I really like &lt;a href="http://goo.gl/7u49C"&gt;Erlang's process&lt;/a&gt;&amp;nbsp;paradigm.&lt;br /&gt;&lt;br /&gt;What really bugs me are the inconsistencies. &amp;nbsp;Go is self-consistent, which seems to be the Go-team's answer to any criticism of consistency in the language, which is itself annoying, but it isn't consistent in the &lt;a href="http://goo.gl/DCfAL"&gt;Principle of Least Astonishment&lt;/a&gt; way. &amp;nbsp;In fact, Go is really pretty bad when it comes to POLA. &amp;nbsp;The thing that kept biting me this weekend -- and, admittedly, it was my fault resulting from a lack of experience with Go -- was the following case:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; package main&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; type Foo map[string]string&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; type Bar struct {&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; &amp;nbsp; thingOne string&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; &amp;nbsp; thingTwo int&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; }&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; func main() {&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; &amp;nbsp; x := new(Foo)&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; &amp;nbsp; y := new(Bar)&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; &amp;nbsp; (*y).thingOne = "hello"&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; &amp;nbsp; (*y).thingTwo = 1&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; &amp;nbsp; (*x)["x"] = "goodbye"&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; &amp;nbsp; (*x)["y"] = "world"&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; }&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;Go requires that you know details about the implementation of the types defined above. &amp;nbsp;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;x&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt; is not really a new object; it's a pointer to something that doesn't exist, while &lt;/span&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;y&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt; is an actually allocated object. &amp;nbsp;If you try to use &lt;/span&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;x&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;, you'll get a runtime fault and your program will crash. &amp;nbsp;Instead, you have to do this:&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; &amp;nbsp; x = make(Foo)&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;However, if you try to do the same thing with Bar:&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; &amp;nbsp; y = make(Bar)&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;you get a compiler error. &amp;nbsp;This bothers me, because it's one of those submarine bugs -- if you make a mistake in how you allocate an instance, you won't find out unless your tests actually hit that code. &amp;nbsp;I had hoped we'd have gotten away from that by now. &amp;nbsp;I'd rather Go just made &lt;/span&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;make()&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt; work on structures, and then you could eschew &lt;/span&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;new()&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt; entirely (unless you really needed it for a specific purpose), and this type of error would become exceedingly rare. &amp;nbsp;It's a gotcha; it's an inconsistency in the syntax.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;I've mentioned before that I like catching errors as early in the development process as possible, and this is the reason why I prefer strongly typed languages. The sort of problem that I've just mentioned weakens Go a bit in this area; for comparison, &lt;a href="http://goo.gl/CHmpN"&gt;Haskell&lt;/a&gt; eliminates this sort of submarine bug -- if you can get your program to compile, it &lt;b&gt;will&lt;/b&gt; run, and if it fails, it'll be entirely due to a &lt;i&gt;logical&lt;/i&gt;, algorithmic error, not a type-usage one. &amp;nbsp;There's no compiler yet that will check your logic for you, so Haskell is pretty close to perfect, in that respect.&lt;br /&gt;&lt;br /&gt;Before anybody comments on my syntax use above, I'll point out that the dereferencing of the &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;y&lt;/span&gt; pointer is unnecessary: Go does that for you. &amp;nbsp;I did it for illustration purposes.&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: large;"&gt;Pointers&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;It's been a long time since I've had to use pointers, and during my development and testing I was floundering around trying to determine when and when not to use them. &amp;nbsp;At one point, I was getting annoyed and was thinking that this was another case of POLA in Go, but it turns out I didn't have to worry about it. &amp;nbsp;All you have to do is to ensure that any function that needs to change a structure (or variable) gets passed a pointer, and Go happily figures out the rest. &amp;nbsp;Another example:&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; func (b *Bar) foo() { &amp;nbsp;b.thing = 1&amp;nbsp;}&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; func (b Bar) write() string { return&amp;nbsp;fmt.Sprint(b)&amp;nbsp;}&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; func tralaTrala() {&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; var b Bar&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; b.foo()&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; fmt.Println(b.write())&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; a := new(Bar)&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; a.foo()&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; fmt.Println(a.write())&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; }&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;tralaTrala()&lt;/span&gt; does not need to know whether &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;a&lt;/span&gt; and &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;b&lt;/span&gt; are pointers or not; Go will "do the right thing" here, and the syntax is the same despite the fact that the types of the two variables are different (one's a pointer, the other isn't). &amp;nbsp;The only real issue is that, when writing a function, the programmer &lt;i&gt;does&lt;/i&gt; need to know whether &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;foo()&lt;/span&gt; and &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;write()&lt;/span&gt; modify the state of the Bar they belong to, because s/he needs to know whether to request a pointer or not:&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; func doSomething( b Bar ) {&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; b.foo()&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; }&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;will probably end up being a runtime logic error, and Go does nothing to help you with these. &amp;nbsp;This makes me think that the safest way to use Go is to pretend that it's entirely functional, and write all of your code that way, until you need to optimize it. &amp;nbsp;This would mean that the signature for &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;foo()&lt;/span&gt; would be:&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; &amp;nbsp; foo() Bar&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;and it'd be used like this:&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; func doSomething( b Bar ) Bar {&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; rv := b.foo()&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; return rv&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; }&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;This is another thing that I'm not entirely happy about, but it may be a reasonable trade-off to provide a powerful optimization path.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5297772973487593985-5371209928634880244?l=blog.ser1.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ser1.net/feeds/5371209928634880244/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5297772973487593985&amp;postID=5371209928634880244' title='25 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/5371209928634880244'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/5371209928634880244'/><link rel='alternate' type='text/html' href='http://blog.ser1.net/2011/06/now-we-get-to-annoying-aspects-of-go.html' title='Now we get to the annoying aspects of Go'/><author><name>SER</name><uri>http://www.blogger.com/profile/11674189468540726402</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>25</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5297772973487593985.post-3621795789042401938</id><published>2011-06-25T06:37:00.000-07:00</published><updated>2011-06-25T06:37:45.753-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='golang'/><title type='text'>More Golang adventures</title><content type='html'>I recently wrote a little application called &lt;a href="http://goo.gl/BSLlO"&gt;ConfigServer&lt;/a&gt; (although the project will probably be renamed). It is intended to be a tool to assist release managers and software deployments by providing a central server to hold configurations, but also allow configurations to be version controlled. ConfigServer provides some inheritance capability, the idea being that you can have a Cluster configuration, a Server configuration that inherits properties from the Cluster, and an Application configuration that inherits from the Server -- ConfigServer doesn't care about the schema, actually, but that's an implementation detail.&lt;br /&gt;&lt;br /&gt;The original version of this application was written in Ruby, a language that I was once extremely well-versed in, but which I haven't used for anything but one-off command-line scripts in a few years. It took me about 6 hours to write the thing.&lt;br /&gt;&lt;br /&gt;Out of curiosity, and since the project was so small, I decided to re-implement it in &lt;a href="http://golang.org/"&gt;Go&lt;/a&gt;. When I started the rewrite, it was the second time I'd looked at Go syntax; the first was to fix a small benchmark program that was comparing Go to &lt;a href="http://live.gnome.org/Vala"&gt;Vala&lt;/a&gt; (&lt;a href="http://goo.gl/cKTCR"&gt;commentary on that is here&lt;/a&gt;), so it really was the first time I'd written any Go; I was utterly unfamiliar with the packages.  The Go version took me about 20 hours.  That time includes unit tests and a couple of refactorings to improve segregation of responsibility; I guesstimate that if I performed the same refactoring and writing unit tests for the Ruby code, I could probably spend another 6-10 hours; on the one hand, I know the shape of the modules/packages, but on the other hand, writing unit tests always takes a lot of time.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://goo.gl/hFe6"&gt;Cloc&lt;/a&gt; tells us this:&lt;br /&gt;&lt;br /&gt;&lt;table cellpadding="5px"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;th&gt;Language&lt;/th&gt;&lt;th&gt;files&lt;/th&gt;&lt;th&gt;blank&lt;/th&gt;&lt;th&gt;comment&lt;/th&gt;&lt;th&gt;code&lt;/th&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Go&lt;/td&gt;&lt;td&gt;7&lt;/td&gt;&lt;td&gt;104&lt;/td&gt;&lt;td&gt;43&lt;/td&gt;&lt;td&gt;482&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Ruby&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;59&lt;/td&gt;&lt;td&gt;55&lt;/td&gt;&lt;td&gt;326&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;This excludes unit tests.  So here are my observations:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;I'm frankly amazed that the Go LOC is so close to the Ruby LOC (33% larger). If I refactored the Ruby code to be more well-structured and encapsulated (and unit-testable), that LOC delta would shrink even more.&lt;/li&gt;&lt;li&gt;The Ruby program is a single, executable-and-library file.  This was intentional.  I was able to break up the Go code into more files because the end artifact would also be a single executable.&lt;/li&gt;&lt;li&gt;The Go compiler is wicked, stupid fast.  &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;gomake&lt;/span&gt; is nice, in that it uses standard Makefiles but provides includes to greatly simplify the Makefiles.&lt;/li&gt;&lt;li&gt;The Go unit test package is anemic, providing only the bare minimum of functionality.&lt;/li&gt;&lt;li&gt;Go has such potential for mixing in, but the language spec disallows it (you can only define functions on structures that are within your own package).&lt;/li&gt;&lt;li&gt;Even if I were as knowledgeable about Go as I am about Ruby, I'd still have been able to whip the Ruby version out faster.  On the other hand, the Go version is much safer, and I trust that program a &lt;i&gt;lot&lt;/i&gt; more -- it is, after all, type-checked.  I guarantee that no matter how much I look at the Ruby code, if it runs for any length of time I'll eventually encounter a typing error because of a bug in the code.&lt;/li&gt;&lt;li&gt;Go has an amazingly useful set of built-in libraries, without being package and class bloated (like, say, Java).  Although, the crypto package is curiously huge.&lt;/li&gt;&lt;li&gt;&lt;a href="http://golang.org/pkg"&gt;The Go package documentation&lt;/a&gt;, FTW!&lt;/li&gt;&lt;li&gt;Doing system exec stuff is easier in Ruby, but not by a lot.&lt;/li&gt;&lt;/ul&gt;All in all, Go surprises me; it's concise, easy, encourages good separation of concerns, is easy to read, and much more functional in feel than you'd expect from looking at the tutorial.  At the same time, it has some unfortunate quirks: it's (currently) relatively slow for a compiled language, and builds large executables; some language constructs are just baffling in their constraints, such as &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;range&lt;/span&gt; only being applicable to a small set of built-ins, rather than working on some well-defined interface; tail-call optimization is extremely limited, which is such a shame for a language as close to being a functional language as Go is; and the lack of ability to mix in to extra-package types is limiting.&lt;br /&gt;&lt;br /&gt;To the first point in my list of Go quirks, I have no doubt that this will change rapidly, and Go will catch up; it's a very young language, after all.  As to the last point, the explanation put forth by the Go developers is, frankly, weak.  It's the same argument Gosling made about not supporting multiple inheritance in Java, which basically boils down to "programmers are stupid, and this feature will confuse them." I don't buy it; I'd rather have extra-package mix-ins with limitations based on technical merit than none at all.&lt;br /&gt;&lt;br /&gt;I've learned that I vastly prefer the cost of slower development time to gain reliability and safety, as long as the cost isn't too high.  Haskell is even safer, but it pushes the boundaries of how much pain I'm willing to endure trying to get code to pass through the type checker.&lt;br /&gt;&lt;br /&gt;Right now, I'm thinking Go's a keeper.  It is more productive than C, more reliable than Ruby, easier than Haskell, is superior to Erlang in a whole host of ways I won't enumerate here (although, I do like the features that the OTP brings to the table, many of which are "killer" features unavailable in any other language, which keeps Erlang highly relevant), and is far better than Java in terms of simplicity, syntax, typing, threading, native compilation, and memory use.  Each of these languages surpasses Go in their own ways, but I believe that Google has successfully found that sweet spot in a practical language that minimizes the annoying attributes and boiler-plate, and provides strong typing, resulting in a highly "writeable" language that compiles to native executables. We'll see how I feel with a few gross more hours under my belt.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5297772973487593985-3621795789042401938?l=blog.ser1.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ser1.net/feeds/3621795789042401938/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5297772973487593985&amp;postID=3621795789042401938' title='9 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/3621795789042401938'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/3621795789042401938'/><link rel='alternate' type='text/html' href='http://blog.ser1.net/2011/06/more-golang-adventures.html' title='More Golang adventures'/><author><name>SER</name><uri>http://www.blogger.com/profile/11674189468540726402</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>9</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5297772973487593985.post-3334834408311945113</id><published>2011-06-17T15:25:00.000-07:00</published><updated>2011-06-17T15:26:44.450-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='restructuredtext'/><category scheme='http://www.blogger.com/atom/ns#' term='markup'/><title type='text'>Restructuredtext vs Asciidoc</title><content type='html'>This will be brief: &lt;a href="http://docutils.sourceforge.net/rst.html"&gt;RST&lt;/a&gt; beats &lt;a href="http://www.methods.co.nz/asciidoc/"&gt;Asciidoc&lt;/a&gt;. &amp;nbsp;I actually prefer Asciidoc's syntax; it uses markup inherited from the markup people have been using in emails for more than a dozen years, while RST is hacky, obtuse, and contrived. &amp;nbsp;I never will understand why people ignore defacto, organic standards in favor of inventing new standards which are worse. &amp;nbsp;In any case, Asciidoc's syntax is better -- but the parse rules are just too involved. &amp;nbsp;It can be difficult to construct a document from scratch; the parser is extremely picky, and there are too many dependencies on unstable libraries. &amp;nbsp;This is supposed to be human-composable, human-readable text; the syntax rules should be more relaxed, and the parser more forgiving. &amp;nbsp;So, for now, I'm sticking with RST, even though it has serious flaws. [1] &amp;nbsp;It does have unparalleled table syntax, although unfortunately without any support for text alignment in cells.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;[1] Such as:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;No easy syntax for underscore, strike-through, superscript, or subscript&lt;/li&gt;&lt;li&gt;Bizarre, unintuitive choices for markup, like *xyz* for italics and **xyz** for bold&lt;/li&gt;&lt;li&gt;Unnecessarily verbose syntax for footnotes&lt;/li&gt;&lt;li&gt;No mechanism for continuing lines in lists after inline blocks&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5297772973487593985-3334834408311945113?l=blog.ser1.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ser1.net/feeds/3334834408311945113/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5297772973487593985&amp;postID=3334834408311945113' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/3334834408311945113'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/3334834408311945113'/><link rel='alternate' type='text/html' href='http://blog.ser1.net/2011/06/restructuredtext-vs-asciidoc.html' title='Restructuredtext vs Asciidoc'/><author><name>SER</name><uri>http://www.blogger.com/profile/11674189468540726402</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5297772973487593985.post-6990857241832817778</id><published>2011-06-10T06:58:00.000-07:00</published><updated>2011-06-13T07:14:51.142-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='haskell'/><category scheme='http://www.blogger.com/atom/ns#' term='erlang'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='golang'/><title type='text'>JVM hate</title><content type='html'>I came across this paper, published by Google, comparing benchmarks in Scala, C++, Java, and Go. &amp;nbsp;Scala comes out on top (well, under C++) in performance, by a decent margin. &amp;nbsp;This surprised me a bit, but makes me happy -- Scala is a functional language, and I do think functional languages are the (medium) future of software development. &amp;nbsp;My main beef with Scala is the JVM.&lt;br /&gt;&lt;br /&gt;There are (in my mind) several problems with the JVM. &amp;nbsp;First, Java is now owned by Oracle, who's &lt;a href="http://mashable.com/2010/08/12/oracle-google-android-lawsuit/"&gt;begun suing companies&lt;/a&gt; using Java technology. &amp;nbsp;This isn't like Sun's lawsuit against Microsoft way-back-when, which was about maintaining compatibility in the JVMs so as not to fragment Java; no, this time, it's about money: Oracle wants a piece of the Android pie. &amp;nbsp;The lawsuit makes good business sense for Oracle, but it puts an indelible stain on Java and the JVM, and one that I can't abide. &amp;nbsp;Now that Oracle has proven its willingness to go after companies using Java technology for financial gain, no company is safe, and every company should seriously consider whether using Java (or a JVM) is worth the financial risk.&lt;br /&gt;&lt;br /&gt;Second, the JVM is a bloated hog. &amp;nbsp;Yes, it is fast. It's amazingly fast. The scientists at Sun have done a great job with JIT optimizations, bringing Java performance up into the &lt;a href="http://shootout.alioth.debian.org/u64q/which-language-is-best.php?gpp=on&amp;amp;java=on&amp;amp;sbcl=on&amp;amp;ghc=on&amp;amp;csharp=on&amp;amp;hipe=on&amp;amp;python3=on&amp;amp;yarv=on&amp;amp;xfullcpu=1&amp;amp;xmem=0&amp;amp;xloc=0&amp;amp;nbody=1&amp;amp;fannkuchredux=1&amp;amp;meteor=1&amp;amp;fasta=1&amp;amp;spectralnorm=1&amp;amp;revcomp=1&amp;amp;mandelbrot=1&amp;amp;knucleotide=1&amp;amp;regexdna=1&amp;amp;pidigits=1&amp;amp;chameneosredux=1&amp;amp;threadring=1&amp;amp;binarytrees=1&amp;amp;calc=chart"&gt;top tier of languages&lt;/a&gt;&amp;nbsp;(grain of salt, but real-world reports of Java performance are pretty consistent). &amp;nbsp;However, it's a memory hog, and it can be difficult to tune the garbage collection so that unexpected spikes don't occur, or applications don't crash with out-of-memory exceptions, and so on. &amp;nbsp;Its start-up time is also a problem, and there's on old&amp;nbsp;adage&amp;nbsp;of "write once, debug everywhere" that's still relevant.&lt;br /&gt;&lt;br /&gt;I'm not a big VM fan in general, any more. &amp;nbsp;Ruby cured me of that, although I've been writing Java code long enough to have had my share of problems with the JVM, too (not recently; it has been pretty stable for years). &amp;nbsp;Haskell programs that I wrote 7 years ago still compile and run, and unless there's a major API change in libc or libm, compiled programs continue to run reliably as well. &amp;nbsp;This means that I can write a service, get it working the way I want, run it, and forget about it. &amp;nbsp;It just runs forever, or until it crashes and gets restarted. &amp;nbsp;Ruby applications, on the other hand, crash almost every time the Ruby VM gets upgraded, and this (IME) happens quite often, and unfortunately usually as part of a larger, system-wide upgrade. &amp;nbsp;So the admin does a 'do-release-upgrade', the Ruby VM gets upgraded, and suddenly some Ruby service that I wrote a couple of years ago stops (silently) working. &amp;nbsp;This doesn't happen with my Haskell apps; it doesn't happen to most natively compiled apps (or it happens much more rarely). &amp;nbsp;It's a matter of trust. &amp;nbsp;I trust natively compiled code more than VM code.&lt;br /&gt;&lt;br /&gt;There's also a matter of ability to distribute code. &amp;nbsp;With any VM (including the JVM), you have to be careful of the versions. &amp;nbsp;At one company I previously worked at, we specified support for specific JVM builds, and upgrading the JVM version was fairly expensive, as QA had to run through much more comprehensive tests when the JVM changed. &amp;nbsp;Because or product included several apps being developed by several different teams, we had to upgrade JVM versions in lock-step. &amp;nbsp;And if you're writing an application that's intended to run on several nodes in a network, you have to make sure that your VMs are all in sync, version-wise. &amp;nbsp;Erlang was especially bad about this.&lt;br /&gt;&lt;br /&gt;Finally, VMs add another layer of failure. &amp;nbsp;Not only can your hardware, OS, and libraries fail, but now you've got a VM that can contain errors and fail. &amp;nbsp;Higher levels also seem to be more prone to bugs and failures, probably due to the increased complexity of the software. &amp;nbsp;And the JVM is very complex.&lt;br /&gt;&lt;br /&gt;This is why languages like Go and Haskell interest me. &amp;nbsp;They're faster than (most) VM code, they're higher level than C, and they compile to native binaries and so don't have a dependency on a VM. &amp;nbsp;I can compile a Haskell or Go program on Ubuntu Linux and scp it over to a RHEL machine and run it. &amp;nbsp;Basically, as long as the architecture and platform is the same, you have a lot of leeway with binary portability. &amp;nbsp;There's a lot of power in that, and it greatly simplifies network administration. &amp;nbsp;And there's value in staying on the good side of &amp;nbsp;network admins.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5297772973487593985-6990857241832817778?l=blog.ser1.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ser1.net/feeds/6990857241832817778/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5297772973487593985&amp;postID=6990857241832817778' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/6990857241832817778'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/6990857241832817778'/><link rel='alternate' type='text/html' href='http://blog.ser1.net/2011/06/jvm-hate.html' title='JVM hate'/><author><name>SER</name><uri>http://www.blogger.com/profile/11674189468540726402</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5297772973487593985.post-1496205947962173952</id><published>2011-06-10T06:27:00.000-07:00</published><updated>2011-06-10T06:27:36.581-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='vala'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='golang'/><title type='text'>Go, Vala, and benchmarks</title><content type='html'>When posting benchmarks comparisons, it's pretty important to ensure that you're testing the same amount of work. &amp;nbsp;For example, if you're comparing the ability to construct objects and index them, you should make sure that you're generating the same indexes. &amp;nbsp;Similarly, when you're comparing languages and are benchmarking the costs of things, you really should make sure that your programs are doing the same amount of work.&lt;br /&gt;&lt;br /&gt;For example, consider &lt;a href="http://article.gmane.org/gmane.comp.programming.vala/7690"&gt;Serge Hulne's posting&lt;/a&gt; to the Vala newsgroup, whereby he shows that Go is 2x slower than Vala. &amp;nbsp;He posts programs in both languages that calculate the 10 most used words in the entire works of William Shakespeare. &amp;nbsp;His algorithm is not the most efficient way to perform this task, but that's not the point; his point was to exercise aspects of both languages: object creation, array and associative array performance, IO; that sort of thing. &amp;nbsp;The problem is that his Go version did a lot more&amp;nbsp;unnecessary&amp;nbsp;work than his Vala version; whether he did this on purpose to make Vala look better, or if he just accidentally left in some debugging code, I don't know... but if you're going to make assertions about performance benchmarks and you don't make sure that you're being as fair as reasonably possible given your skill level, then you just end up making yourself look silly.&lt;br /&gt;&lt;br /&gt;In Serge's case, his Go program processed the text as Unicode, whereas his Vala program processed it as raw bytes. &amp;nbsp;There was no excuse for this; it was a trivial change to make the Go version process bytes, too. &amp;nbsp;That smells a lot like an intentional cheat. &amp;nbsp;Then, too, the Go version did extra work that the Vala version didn't, like calculating the number of lines, words, and characters in the file -- although it never did anything with the values except sum them up. &amp;nbsp;That's 5M (chars) + 900k (words) + 124k (lines) additional addition operations for the Go version. &amp;nbsp;Finally, there was an unnecessary if-else-clause in a loop in the Go version that was being executed for every word, so 900k more branch operations (and branches are relatively expensive, as far as CPU operations go).&lt;br /&gt;&lt;br /&gt;Getting rid of these things that the Go code was doing that the Vala code wasn't improved the speed of the Go program by 30% (0.78s vs 1.1s).&lt;br /&gt;&lt;br /&gt;Go isn't going to be as fast as Vala, at least not in the immediate future; Vala does a pretty good job in it's translation to C, and GCC is pretty scary in it's ability to optimize. &amp;nbsp;Go's compiler (6g, which is what I used, because gccgo isn't available on my Mac) is new -- although, I don't doubt Google's ability to eventually bring it down to the point where there's very little discernable difference in performance. &amp;nbsp;My point is that if people are going to pretend to be scientists, doing benchmarks, collecting statistics, and presenting them as proofs, they really should be more careful about their code.&lt;br /&gt;&lt;br /&gt;By the way, I'm not a Go programmer. &amp;nbsp;I don't even &lt;i&gt;know&lt;/i&gt;&amp;nbsp;Go, beyond what I've learned by fixing Serge's code. &amp;nbsp;I haven't read the tutorial, and this was the first time I'd ever compiled a Go program. &amp;nbsp;There's something to be learned about that: both Vala and Go are readable enough that I could easily see what two programs were doing and identify the functional differences. &amp;nbsp;Ironically, I did play around with Vala when it first came out, so I actually had more experience with Vala when I first came across Serge's code. &amp;nbsp;For what it's worth, after this exercise, I've found that I prefer Go, and could see myself writing code in it. &amp;nbsp;Vala is more like C++; it's basically C with some tacked-on features -- that's one reason why it's benchmarks are so close to C's. &amp;nbsp;Go is a new language, with C-like syntax -- sort of like Java was, when it first came out, only less so. &amp;nbsp;I'll have to see how goroutines work out; if they're not as effective as Erlang's processes, I may lose interest.&lt;br /&gt;&lt;br /&gt;Jorg Walter posted a sort of critique focusing on Go (well, mostly it's a highly subjective rant) that gives a good overview of some of the more compelling features of Go; if you can ignore the opinions-stated-as-fact (which have the flavor of "only idiots prefer their braces on their own lines!") and the occasionally erroneous "factiod" ("Ruby faded away" -- huh?[1]), it's an interesting read.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;[1] Visits to ruby-lang.org are &lt;a href="http://goo.gl/F5yBL"&gt;down by half&lt;/a&gt; over the past year, but so are &lt;a href="http://goo.gl/L1b6V"&gt;visits to python.org&lt;/a&gt; is down by half, and perl.org is &lt;a href="http://goo.gl/CpZHN"&gt;down almost 2/3&lt;/a&gt; -- he'd have more accurately implied that Perl faded away, although he'd have been better off not making that claim at all.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5297772973487593985-1496205947962173952?l=blog.ser1.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ser1.net/feeds/1496205947962173952/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5297772973487593985&amp;postID=1496205947962173952' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/1496205947962173952'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/1496205947962173952'/><link rel='alternate' type='text/html' href='http://blog.ser1.net/2011/06/go-vala-and-benchmarks.html' title='Go, Vala, and benchmarks'/><author><name>SER</name><uri>http://www.blogger.com/profile/11674189468540726402</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5297772973487593985.post-8709876266151204993</id><published>2011-06-07T17:25:00.000-07:00</published><updated>2011-06-07T17:25:45.320-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='bitcoin'/><title type='text'>VirWox is a rip-off</title><content type='html'>Say you want to buy some bitcoins. &amp;nbsp;It turns out that it's a major pain in the ass, but one route is VirWox. &amp;nbsp;Here's what you're looking at when you go this route:&lt;br /&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;&lt;span class="Apple-style-span" style="font-family: Times, 'Times New Roman', serif;"&gt;Deposit money with PayPal. &amp;nbsp;VirWox takes&amp;nbsp;&lt;span class="Apple-style-span" style="-webkit-border-horizontal-spacing: 2px; -webkit-border-vertical-spacing: 2px; border-collapse: collapse;"&gt;EUR 0.35 + 3.4%&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span class="Apple-style-span" style="font-family: Times, 'Times New Roman', serif;"&gt;&lt;span class="Apple-style-span" style="-webkit-border-horizontal-spacing: 2px; -webkit-border-vertical-spacing: 2px; border-collapse: collapse;"&gt;Convert dollars to SLL. &amp;nbsp;VirWox takes 3.5%&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span class="Apple-style-span" style="font-family: Times, 'Times New Roman', serif;"&gt;&lt;span class="Apple-style-span" style="-webkit-border-horizontal-spacing: 2px; -webkit-border-vertical-spacing: 2px; border-collapse: collapse;"&gt;Convert SLL to BTC (bitcoins). &amp;nbsp;VirWox takes 3.5%&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span class="Apple-style-span" style="font-family: Times, 'Times New Roman', serif;"&gt;&lt;span class="Apple-style-span" style="-webkit-border-horizontal-spacing: 2px; -webkit-border-vertical-spacing: 2px; border-collapse: collapse;"&gt;Withdraw bitcoins. &amp;nbsp;VirWox takes 0.02 BTC (currently, about 40¢) as a fee, &lt;i&gt;and&lt;/i&gt;&amp;nbsp;holds the transfer for 48 hours.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;&lt;/ol&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: Times, 'Times New Roman', serif;"&gt;&lt;span class="Apple-style-span" style="-webkit-border-horizontal-spacing: 2px; -webkit-border-vertical-spacing: 2px; border-collapse: collapse;"&gt;It's a lot like the old company towns: once you get your money in, they charge you for everything that you do with it. &amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span" style="-webkit-border-horizontal-spacing: 2px; -webkit-border-vertical-spacing: 2px; border-collapse: collapse; font-family: Times, 'Times New Roman', serif;"&gt;Luckily, this was just an experiment for me, and I didn't put any real money into this. &amp;nbsp;If you're smart, you won't, either. &amp;nbsp;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family: Times, 'Times New Roman', serif;"&gt;&lt;span class="Apple-style-span" style="-webkit-border-horizontal-spacing: 2px; -webkit-border-vertical-spacing: 2px; border-collapse: collapse;"&gt;VirWox is a con.&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5297772973487593985-8709876266151204993?l=blog.ser1.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ser1.net/feeds/8709876266151204993/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5297772973487593985&amp;postID=8709876266151204993' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/8709876266151204993'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/8709876266151204993'/><link rel='alternate' type='text/html' href='http://blog.ser1.net/2011/06/virwox-is-rip-off.html' title='VirWox is a rip-off'/><author><name>SER</name><uri>http://www.blogger.com/profile/11674189468540726402</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5297772973487593985.post-4306486630473348888</id><published>2011-06-05T07:37:00.000-07:00</published><updated>2011-06-05T07:37:18.815-07:00</updated><title type='text'>Parallels between Aikido and Wing Chun</title><content type='html'>The seminar is going well, but is frustrating. &amp;nbsp;We spent the entire four hours yesterday looking at daan chi sao: fook sao, tan sao, and bon sao. &amp;nbsp;There are so many details in the smallest things!&lt;br /&gt;&lt;br /&gt;I want to preface this by saying that I make a lot of gross generalizations here. &amp;nbsp;These are nothing more than observations of an amateur.&lt;br /&gt;&lt;br /&gt;I'm continually struck by the similarities between Aikido and Wing Chun, even though they're totally different. &amp;nbsp;It may be the case for every martial art, but in particular it seems that both Aikido and Wing Chun share the characteristic that you can easily learn, say, bon sao or irimi nage, and they &lt;i&gt;look&lt;/i&gt;&amp;nbsp;bloody simple, but it takes a lot of practice (years?) to be able to do it correctly and effectively, and even after you think you've finally "got it", small deviations in the behavior of your partner (or changing partners) can totally throw you off and make you think that you haven't learned anything at all!&lt;br /&gt;&lt;br /&gt;I'm seeing parallels between chi sao and ryote dori tenchinage; I'm thinking in particular of tenchinage suwari waza. &amp;nbsp;They feel similar to me; although the details are wildly different, both emphasize the connection and feeling what the other person is doing. &amp;nbsp;In fact, the dominant difference (again, from my extremely novice perspective) is that in Aikido one person is designated Uke; in Wing Chun, there is no Uke. &amp;nbsp;I also see parallels in the fact that both Wing Chun and Aikido tend to minimize foot strikes, preferring to maintain a solid, stable connection to the ground. &amp;nbsp;Aikido places more emphasis on this, but in Wing Chun we also talk about how to take advantage of balance (or imbalance). &amp;nbsp;Aikido is all about using your opponent's force -- Wing Chun has that in spades, too. &amp;nbsp;I keep encountering these parallels.&lt;br /&gt;&lt;br /&gt;This brings up another interesting paradoxical, dichotomy of the two arts: Wing Chun is Japanese in style, and Aikido is Chinese. &amp;nbsp;Traditionally, Japanese arts tend to be agressive: weight on the forward foot, always attacking, always moving forward; the epitome of this philosophy is Kendo. &amp;nbsp;Contrast this with traditional Chinese Kung Fu, which is full of things like the cat stance, where weight is on the rear foot. &amp;nbsp;However, (until recently, at least) our style of Wing Chun had a more 50/50 weight distribution and we've always emphasized that movement should always be forward. &amp;nbsp;Blocks in Wing Chun aren't blocks; they're attacks. &amp;nbsp;If your partner removes drops his arm, you're (usually) going to hit him. &amp;nbsp;Again, I'm grossly generalizing, but I'm learning that if you think of your blocks this way, the techniques tend to work better. &amp;nbsp;Movements in Wing Chun are economical and direct, linear -- rarely are they circular, like the windmilling movements found in other styles of Kung Fu. &amp;nbsp;My point is that Wing Chun is Japanese in it's philosophy of forward movement and attacks in everything, even defense. &lt;br /&gt;&lt;br /&gt;Aikido, on the other hand, is very receptive and indirect. &amp;nbsp;Yes, in Aikido, you always move forward, and you tend to distribute your weight forward; that is still very Japanese. &amp;nbsp;But the movements are circular; while you don't have the big, grand movements of many Kung Fu forms, you can still see the oscillations, the whirling swirls, of Kung Fu. &amp;nbsp;I think this is what makes them seem so oddly similar to me; despite the fundamental differences in philosophy, they borrow enough from each other's cultural stereotypes that the parallels keep popping out at me.&lt;br /&gt;&lt;br /&gt;No, Aikido isn't anything like Wing Chun. &amp;nbsp;But consider this: if you imagine time-lapse photographs of Aikido and, say, the Kung Fu Snake form, and similar photographs of Wing Chun and, say, Kenpo Karate, I think you'd agree that the &lt;i&gt;basic&lt;/i&gt;&amp;nbsp;perception was that Wing Chun is more similar to Kenpo than to the Snake form, and Aikido more similar to the Snake form than to Kenpo. &amp;nbsp;Absurdly, I am fascinated by this. &amp;nbsp;I do think that culture has a strong influence on style and behavior, and that both Aikido and Wing Chun have gone against traditional stylistic norms, to meet (in a way) in the middle. &amp;nbsp;No, they're not the same art, at all, but the ways in which they are similar is intriguing, given their unrelated origins.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5297772973487593985-4306486630473348888?l=blog.ser1.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ser1.net/feeds/4306486630473348888/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5297772973487593985&amp;postID=4306486630473348888' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/4306486630473348888'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/4306486630473348888'/><link rel='alternate' type='text/html' href='http://blog.ser1.net/2011/06/parallels-between-aikido-and-wing-chun.html' title='Parallels between Aikido and Wing Chun'/><author><name>SER</name><uri>http://www.blogger.com/profile/11674189468540726402</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5297772973487593985.post-3680075558062658703</id><published>2011-06-03T19:36:00.000-07:00</published><updated>2011-06-03T19:36:08.421-07:00</updated><title type='text'>Never trust your wife's martinis</title><content type='html'>Sure, they &lt;b&gt;look&lt;/b&gt;&amp;nbsp;ordinary martinis, but they're mostly gin, and three times as large as a normal martini. I've always lived by the Dorothy Parker rule of thumb:&lt;br /&gt;&lt;blockquote&gt;I like to have a martini&lt;br /&gt;Two at the very most&lt;br /&gt;Three, I'm under the table;&lt;br /&gt;Four, I'm under the host.&lt;/blockquote&gt;&amp;nbsp;Do not trust this rule-of-thumb when your wife is mixing the martinis!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5297772973487593985-3680075558062658703?l=blog.ser1.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ser1.net/feeds/3680075558062658703/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5297772973487593985&amp;postID=3680075558062658703' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/3680075558062658703'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/3680075558062658703'/><link rel='alternate' type='text/html' href='http://blog.ser1.net/2011/06/never-trust-your-wifes-martinis.html' title='Never trust your wife&apos;s martinis'/><author><name>SER</name><uri>http://www.blogger.com/profile/11674189468540726402</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5297772973487593985.post-777279449330866790</id><published>2011-06-03T06:53:00.000-07:00</published><updated>2011-06-03T06:53:23.012-07:00</updated><title type='text'>Kung Fu seminars</title><content type='html'>Sifu Dana Wong is back in town for a seminar at the &lt;a href="http://www.mafcenter.com/"&gt;MAF&lt;/a&gt;. &amp;nbsp;As I was digging around to find the link to &lt;a href="http://www.qianlidao.com.au/sifus-blog/"&gt;Sifu Wong's blog&lt;/a&gt;, I came across this photo from last year's seminar:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://goo.gl/y0YGT" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="216" src="http://goo.gl/y0YGT" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;I'm really looking forward to this year's seminar!&lt;br /&gt;&lt;br /&gt;Dana's a pretty good author, by the way. &amp;nbsp;His blog is worth reading.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5297772973487593985-777279449330866790?l=blog.ser1.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ser1.net/feeds/777279449330866790/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5297772973487593985&amp;postID=777279449330866790' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/777279449330866790'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/777279449330866790'/><link rel='alternate' type='text/html' href='http://blog.ser1.net/2011/06/kung-fu-seminars.html' title='Kung Fu seminars'/><author><name>SER</name><uri>http://www.blogger.com/profile/11674189468540726402</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5297772973487593985.post-321202195794359367</id><published>2011-04-22T19:10:00.000-07:00</published><updated>2011-04-22T19:10:19.410-07:00</updated><title type='text'>I'm a Slidy convert</title><content type='html'>&lt;a href="http://www.w3.org/Talks/Tools/Slidy2"&gt;HTML Slidy&lt;/a&gt;&amp;nbsp;is a way of putting slide shows (presentations) on the web. &amp;nbsp;Think of it like OpenOffice Impress or MicroSoft PowerPoint, but more convenient and with less lock-in. &amp;nbsp;Your viewers only need a web browser to view the presentation, and &lt;i&gt;everybody&lt;/i&gt;&amp;nbsp;has a web browser. &amp;nbsp;Not everybody has MS PowerPoint. &amp;nbsp;Not everybody has OO Impress. &amp;nbsp;Furthermore, if you're on a conference call, it's much safer to use a web presentation than, say, NetMeeting, LiveMeeting, or whatever. In my experience, those are much more prone to network glitches bringing the entire meeting down than a simple HTTP connection. &amp;nbsp;The one way that meeting software is superior to a web-shared presentation is that it's harder to sync advancing the slides with a web presentation.&lt;br /&gt;&lt;br /&gt;Anyway, that's not what I was intending to write about. &amp;nbsp;For years, I'd been using S5 for my slides. &amp;nbsp;It works pretty well, but lately I've been using Slidy, and here's why:&lt;br /&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Slidy is more lightweight. &amp;nbsp;Boy, does S5 have a lot of JavaScript. &amp;nbsp;There are a lot of files, in general, to use S5. &amp;nbsp;Slidy is two files: one JavaScript, and one CSS (not counting image files, of course).&lt;/li&gt;&lt;li&gt;Slidy takes advantage of the fact that browsers have scroll bars. &amp;nbsp;Yeah, ideally, you don't have them, but the fact is that it's never perfect, and there's always going to be somebody using an oddball sized screen so that one of your slides just doesn't quite fit. &amp;nbsp;With S5, they'll never see that content. &amp;nbsp;With Slidy, they just scroll.&lt;/li&gt;&lt;li&gt;I have experienced less wierdness with Slidy. &amp;nbsp;Maybe it's because it is more simple, and doesn't get as fancy with the JavaScript as S5 does, but I encounter many fewer screwed-up layouts resulting from different browser vendor/versions. &amp;nbsp;S5 was sort of twitchy. &amp;nbsp;Slidy just works.&lt;/li&gt;&lt;li&gt;I've found that I spend far less time tweaking the input and output to get it to look right. &amp;nbsp;In fact, with Slidy, it's usually just a matter of breaking lists (or, preferably, removing content), or setting the size on images. &amp;nbsp;With S5, I'd sometimes spend hours tweaking font sizes to try to get things to fit and look right.&lt;/li&gt;&lt;li&gt;It's way, &lt;i&gt;way&lt;/i&gt; easier to style Slidy.&lt;/li&gt;&lt;/ol&gt;&lt;div&gt;S5 does have better transition support; but I don't use transitions. &amp;nbsp;S5 does try harder to scale content so that it fits on the slide, and when it works, it's nice.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Mind you, I haven't actually written either S5 or Slidy HTML in a long while. &amp;nbsp;I generate all of my presentations from RestructuredText, which is how I like it. &amp;nbsp;There's a tool for generating PDF from Slidy called Prince that I haven't tried (it isn't freeware, and I haven't thought about trying to justify it to Management as a necessary purchase, yet); PDF generation of slides is about the only thing I'm missing.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Other things I've learned: pandoc's Slidy generation is painful, and I've yet to figure out how to coerce it to use &lt;i&gt;my&lt;/i&gt;&amp;nbsp;style sheets. It appears to utterly ignore the --data-dir directive, and insists on either linking directly to the W3C resources, or using it's own. rst2slidy works pretty well, but there's no real way to insert header images except my sed'ing the generated output (booo!), and docutils is broken in it's handling of images and SVG; it doesn't allow % in height or width, &lt;a href="http://www.w3.org/TR/html401/struct/objects.html#h-13.2"&gt;which is supported by HTML&lt;/a&gt;&amp;nbsp;(c'mon, read the spec, people!), and it embeds SVG images in &lt;object&gt; tags, rather than &lt;img /&gt; tags, despite the fact that &lt;img /&gt; works better (like, browsers figure out the image size from the image itself) and is supported by all modern browsers. &amp;nbsp;Despite these annoyances, it works remarkably well, and these are all easily fixed -- which isn't the case when things go wrong with S5. &amp;nbsp;You can spend hours in the guts of the S5 CSS trying to get things to look right. &amp;nbsp;I know. &amp;nbsp;I have.&lt;div&gt;&lt;/div&gt;&lt;div&gt;So, yeah. &amp;nbsp;I've been using Slidy exclusively for about 8 months, and I'm definitely a convert. &amp;nbsp;I spend less time tweaking it, and it's more reliable.&lt;/div&gt;&lt;/object&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5297772973487593985-321202195794359367?l=blog.ser1.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ser1.net/feeds/321202195794359367/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5297772973487593985&amp;postID=321202195794359367' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/321202195794359367'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/321202195794359367'/><link rel='alternate' type='text/html' href='http://blog.ser1.net/2011/04/im-slidy-convert.html' title='I&apos;m a Slidy convert'/><author><name>SER</name><uri>http://www.blogger.com/profile/11674189468540726402</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5297772973487593985.post-821812913259421093</id><published>2011-03-18T07:06:00.000-07:00</published><updated>2011-03-18T07:06:57.832-07:00</updated><title type='text'>Erlang's limitations in module encapsulation</title><content type='html'>&lt;span class="Apple-style-span" style="font-size: x-small;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;div class="gmail_quote" style="font-family: arial;"&gt;I was recently looking at mocking frameworks for Erlang, to facilitate unit testing. &amp;nbsp;Mocking is a mechanism for easily producing objects and functionality to limit the scope of your tests. &amp;nbsp;For example:&amp;nbsp;&lt;/div&gt;&lt;blockquote&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;-module(a).&lt;br /&gt;-export([a/1).&lt;br /&gt;a(X) -&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;b(X) * X.&lt;br /&gt;b(X) -&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;X * 5.&lt;/span&gt;&lt;/blockquote&gt;&lt;div class="gmail_quote" style="font-family: arial;"&gt;Pretend that b/1 does something like go to a database, or connect to some other remote service, or read from a file... something that may either take a long time, or might not be available in a test environment. &amp;nbsp;For a unit test for a/1, what you're really concerned with is testing the functionality of a/1, not b/1; to accomplish this, you'd like to mock up the functionality of b/1 to be something known, so that you can prove a/1.&lt;/div&gt;&lt;div class="gmail_quote" style="font-family: arial;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="gmail_quote" style="font-family: arial;"&gt;What you should be able to do with mocking is something like this (there are a lot of different ways that you could define the mocking interface; I'm just picking an arbitrary one):&lt;/div&gt;&lt;blockquote&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;mock:expect( ?MODULE, b, fun(X) -&amp;gt; X end).&lt;/span&gt;&lt;/blockquote&gt;&lt;div class="gmail_quote" style="font-family: arial;"&gt;Then you could define a unit test that says:&lt;/div&gt;&lt;blockquote&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;4 = a(2).&lt;/span&gt;&lt;/blockquote&gt;&lt;div class="gmail_quote" style="font-family: arial;"&gt;This makes a/1 provable through induction, because it isolates the code in a/1 -- which is exactly what you want in a unit test.&lt;/div&gt;&lt;div class="gmail_quote" style="font-family: arial;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="gmail_quote"&gt;&lt;span class="Apple-style-span" style="font-family: arial;"&gt;Unfortunately, you can't do this in Erlang in such a way that the unit tests don't impact the way you write the code being tested. &amp;nbsp;This is due to a flaw in Erlang modules, and particularly, the &lt;/span&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;export&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family: arial;"&gt; directive.&lt;/span&gt;&lt;/div&gt;&lt;div class="gmail_quote" style="font-family: arial;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="gmail_quote" style="font-family: arial;"&gt;If you want to do mocking sample above in Erlang, the code being tested must become&amp;nbsp;&lt;/div&gt;&lt;blockquote&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;-module(a).&lt;br /&gt;-export([a/1,b/1]).&lt;br /&gt;a(X) -&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;a:b(X) * X.&lt;br /&gt;b(X) -&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;X * 5.&lt;/span&gt;&lt;/blockquote&gt;&lt;div class="gmail_quote" style="font-family: arial;"&gt;This is because mocking &lt;i&gt;replaces&lt;/i&gt;&amp;nbsp;the functionality of a function with different functionality, and the Erlang compiler hard-links the call to b/1 in a/1 unless you specify the module of b/1. &amp;nbsp;This, in itself, is not a problem: the problem is that even &lt;i&gt;within&lt;/i&gt; module a, references to functions within the same module must be exported. &amp;nbsp;That is, if a/1 wants to call a:b/1, b/1 must be exported. &amp;nbsp;Why is this a problem? &amp;nbsp;Because it leads to comments like this, which you find all too commonly in Erlang projects:&lt;/div&gt;&lt;blockquote&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;-export([a/1]).&lt;br /&gt;% -------------------------------------&lt;br /&gt;% WARNING: DO NOT CALL THESE FUNCTIONS&lt;br /&gt;-export([b/1]).&lt;/span&gt;&lt;/blockquote&gt;&lt;div class="gmail_quote" style="font-family: arial;"&gt;There are a number of reasons why the authors had to export b/1. &amp;nbsp;Because spawn/3 can't call functions that aren't exported. Because mocking requires it. &amp;nbsp;Because they want to support code reloading. &amp;nbsp;All of which are valid use cases, and all of which force developers to over-expose their API, breaking encapsulation, and suggesting that Erlang's module handling is flawed.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5297772973487593985-821812913259421093?l=blog.ser1.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ser1.net/feeds/821812913259421093/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5297772973487593985&amp;postID=821812913259421093' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/821812913259421093'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/821812913259421093'/><link rel='alternate' type='text/html' href='http://blog.ser1.net/2011/03/erlangs-limitations-in-module.html' title='Erlang&apos;s limitations in module encapsulation'/><author><name>SER</name><uri>http://www.blogger.com/profile/11674189468540726402</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5297772973487593985.post-9141144923634580106</id><published>2011-02-04T07:07:00.000-08:00</published><updated>2011-02-04T07:07:57.939-08:00</updated><title type='text'>The superiority of base-12</title><content type='html'>It should be evident that, all other factors being equal, base-12 is a superior number base for humans than base-10. &amp;nbsp;I won't go into too many details why this is true; there &lt;a href="http://www.youtube.com/watch?v=-nu_pjgTIZ4"&gt;are&lt;/a&gt; &lt;a href="http://duodecimal/"&gt;many&lt;/a&gt; &lt;a href="http://www.dozens.org/articles/db043r2.pdf"&gt;resources&lt;/a&gt; that &lt;a href="http://www.dozenalsociety.org.uk/basicstuff/campbell.html"&gt;discuss&lt;/a&gt; these details in length, but it's main strength is that it has more prime factors than base-10. &amp;nbsp;12 is divisible by 1,2,3,4 and 6; 10 is divisible only by 1,2, and 5. &amp;nbsp;As to it's suitability over other systems, the next step up occurs at sexagesimal (base-60), which is divisible by 1,2,3,4,5 and 6, and that's an inconveniently large set of base numbers for humans. Dozenal is so useful that it's still commonly used today; a dozen eggs, a gross, our analog clocks, the number of signs in the zodiak, inches in a foot, etc., etc.&lt;br /&gt;&lt;br /&gt;What this post is about, however, is how useful this is even beyond the first 12 numbers. &amp;nbsp;Consider a gross (a dozen dozen, or 144&lt;sub&gt;10&lt;/sub&gt;), or 100&lt;sub&gt;12&lt;/sub&gt;:&lt;br /&gt;&lt;br /&gt;&lt;table border="1"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;100&lt;sub&gt;10&lt;/sub&gt;&lt;/td&gt;&lt;td&gt;100&lt;sub&gt;12&lt;/sub&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;50&lt;sub&gt;10&lt;/sub&gt;&lt;/td&gt;&lt;td&gt;60&lt;sub&gt;12&lt;/sub&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;33.3&lt;span style="text-decoration: overline;"&gt;3&lt;/span&gt;&lt;sub&gt;10&lt;/sub&gt;&lt;/td&gt;&lt;td&gt;40&lt;sub&gt;12&lt;/sub&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;4&lt;/td&gt;&lt;td&gt;25&lt;sub&gt;10&lt;/sub&gt;&lt;/td&gt;&lt;td&gt;30&lt;sub&gt;12&lt;/sub&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;5&lt;/td&gt;&lt;td&gt;10&lt;sub&gt;10&lt;/sub&gt;&lt;/td&gt;&lt;td&gt;24;1&lt;sub&gt;12&lt;/sub&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;6&lt;/td&gt;&lt;td&gt;16.&lt;span style="text-decoration: overline;"&gt;66&lt;/span&gt;&lt;sub&gt;10&lt;/sub&gt;&lt;/td&gt;&lt;td&gt;20&lt;sub&gt;12&lt;/sub&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;7&lt;/td&gt;&lt;td&gt;14.&lt;span style="text-decoration: overline;"&gt;285714&lt;/span&gt;&lt;sub&gt;10&lt;/sub&gt;&lt;/td&gt;&lt;td&gt;18;&lt;span style="text-decoration: overline;"&gt;6A3518&lt;/span&gt;&lt;sub&gt;12&lt;/sub&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;8&lt;/td&gt;&lt;td&gt;12.5&lt;sub&gt;10&lt;/sub&gt;&lt;/td&gt;&lt;td&gt;16&lt;sub&gt;12&lt;/sub&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;9&lt;/td&gt;&lt;td&gt;11.&lt;span style="text-decoration: overline;"&gt;11&lt;/span&gt;&lt;sub&gt;10&lt;/sub&gt;&lt;/td&gt;&lt;td&gt;14&lt;sub&gt;12&lt;/sub&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;A&lt;sub&gt;12&lt;/sub&gt;&lt;/td&gt;&lt;td&gt;10&lt;sub&gt;10&lt;/sub&gt;&lt;/td&gt;&lt;td&gt;12;5&lt;sub&gt;12&lt;/sub&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;B&lt;sub&gt;12&lt;/sub&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;11;&lt;span style="text-decoration: overline;"&gt;11&lt;/span&gt;&lt;sub&gt;12&lt;/sub&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;10&lt;sub&gt;12&lt;/sub&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;10&lt;sub&gt;12&lt;/sub&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;There are really only two (base) numbers that produce non-terminating divisors in a gross: B and 7.  For 100&lt;sub&gt;10&lt;/sub&gt;, you have 3, 6, 7, and 9. &amp;nbsp;Sidenote: I'm not thrilled with the selection of ";" as a floating-point character, but it seems to be what everybody is using, and there's really nothing better (unless we just stick with "."). &amp;nbsp;Thankfully, there's no universal agreement on the character to use, and I reject the Dozenal Society of America's choice of "*" -- it's a really, really poor choice, as anybody who does any programming or, verily, &lt;i&gt;basic math&lt;/i&gt; can tell you. &amp;nbsp;"A" and "B" aren't very good, either, but at least they're recognizable to anybody who's ever taken a computer programming course.&lt;br /&gt;&lt;br /&gt;BTW, the Dozenal Society of America has &lt;a href="http://www.dozens.org/"&gt;been busy&lt;/a&gt;&amp;nbsp;building a nice new site; check it out.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5297772973487593985-9141144923634580106?l=blog.ser1.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ser1.net/feeds/9141144923634580106/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5297772973487593985&amp;postID=9141144923634580106' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/9141144923634580106'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/9141144923634580106'/><link rel='alternate' type='text/html' href='http://blog.ser1.net/2011/02/superiority-of-base-12.html' title='The superiority of base-12'/><author><name>SER</name><uri>http://www.blogger.com/profile/11674189468540726402</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5297772973487593985.post-1337515466599931019</id><published>2011-01-24T07:32:00.000-08:00</published><updated>2011-01-24T07:32:09.520-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='erlang'/><category scheme='http://www.blogger.com/atom/ns#' term='software'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>My shocking Erlang discovery</title><content type='html'>Sending messages to PIDs is really, really slow (comparatively).&lt;br /&gt;&lt;br /&gt;Consider the following code, containing two tests that do almost the same thing. &amp;nbsp;I say "almost" because there's a small bug causing the totals to be off-by-one, but they're performing the same amount of functional work. &amp;nbsp;One uses PID message passing to increment a value; the other uses lambda expressions.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: 'Courier New', Courier, monospace;"&gt;-module(test).&lt;/div&gt;&lt;div style="font-family: 'Courier New', Courier, monospace;"&gt;-export([test/2]).&lt;/div&gt;&lt;div style="font-family: 'Courier New', Courier, monospace;"&gt;-export([one/1, two/1]).&lt;/div&gt;&lt;div style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-family: 'Courier New', Courier, monospace;"&gt;test(F, N) -&amp;gt;&lt;/div&gt;&lt;div style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;A = F(init),&lt;/div&gt;&lt;div style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;{T,R} = timer:tc( lists,foldl, [ fun( _, Acc) -&amp;gt; F(Acc) end, A, lists:seq(1, N) ]),&amp;nbsp;&lt;/div&gt;&lt;div style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;F({stop,R}),&lt;/div&gt;&lt;div style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;io:format("\n~ps~n",[T/1000000]).&lt;/div&gt;&lt;div style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-family: 'Courier New', Courier, monospace;"&gt;done(T) -&amp;gt;&lt;/div&gt;&lt;div style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;io:format("\nDone ~p~n",[T]),&lt;/div&gt;&lt;div style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;io:format("Memory: ~p~n",[erlang:memory(processes_used)]).&lt;/div&gt;&lt;div style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-family: 'Courier New', Courier, monospace;"&gt;one(init) -&amp;gt;&lt;/div&gt;&lt;div style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;spawn( fun loop/0 );&lt;/div&gt;&lt;div style="font-family: 'Courier New', Courier, monospace;"&gt;one({stop,X}) -&amp;gt;&lt;/div&gt;&lt;div style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;X ! exit;&lt;/div&gt;&lt;div style="font-family: 'Courier New', Courier, monospace;"&gt;one( X ) -&amp;gt;&lt;/div&gt;&lt;div style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;X ! go, X.&lt;/div&gt;&lt;div style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;/div&gt;&lt;div style="font-family: 'Courier New', Courier, monospace;"&gt;loop() -&amp;gt; loop(0,0).&lt;/div&gt;&lt;div style="font-family: 'Courier New', Courier, monospace;"&gt;loop( State, Ttl ) when State &amp;gt; 100000 -&amp;gt;&lt;/div&gt;&lt;div style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;io:format("."),&amp;nbsp;&lt;/div&gt;&lt;div style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;loop(0, Ttl + 1);&lt;/div&gt;&lt;div style="font-family: 'Courier New', Courier, monospace;"&gt;loop( State, Ttl ) -&amp;gt;&lt;/div&gt;&lt;div style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;receive&lt;/div&gt;&lt;div style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;go -&amp;gt; loop( State + 1, Ttl + 1 );&lt;/div&gt;&lt;div style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;exit -&amp;gt; done(Ttl)&lt;/div&gt;&lt;div style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;end.&lt;/div&gt;&lt;div style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;/div&gt;&lt;div style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-family: 'Courier New', Courier, monospace;"&gt;two(init) -&amp;gt;&lt;/div&gt;&lt;div style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;fun(_) -&amp;gt; make_next( 0,0 ) end;&lt;/div&gt;&lt;div style="font-family: 'Courier New', Courier, monospace;"&gt;two({stop,R}) -&amp;gt;&lt;/div&gt;&lt;div style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;R(exit);&lt;/div&gt;&lt;div style="font-family: 'Courier New', Courier, monospace;"&gt;two( F ) -&amp;gt;&lt;/div&gt;&lt;div style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;F(0).&lt;/div&gt;&lt;div style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-family: 'Courier New', Courier, monospace;"&gt;make_next(C,T) when C &amp;gt; 100000 -&amp;gt;&lt;/div&gt;&lt;div style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;io:format("."),&lt;/div&gt;&lt;div style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;make_next( 0, T+1);&lt;/div&gt;&lt;div style="font-family: 'Courier New', Courier, monospace;"&gt;make_next(C,T) -&amp;gt;&lt;/div&gt;&lt;div style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;fun(K) -&amp;gt;&lt;/div&gt;&lt;div style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;case K of&lt;/div&gt;&lt;div style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;exit -&amp;gt; done(T);&lt;/div&gt;&lt;div style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;_ -&amp;gt; make_next(C+1,T+1)&lt;/div&gt;&lt;div style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;end&lt;/div&gt;&lt;div style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;end.&lt;/div&gt;&lt;div style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;one()&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&amp;nbsp;retains state by spawning a process that loops with it's own state. &amp;nbsp;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;two()&lt;/span&gt; is the interesting code; it retains state by passing back a function factory. &amp;nbsp;Would you have expected &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;one()&lt;/span&gt; to be slower than &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;two()&lt;/span&gt;? &amp;nbsp;I wouldn't have, at least not to this degree (I cleaned the output a little):&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;447)~ % erl +native -smp&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;Erlang R14B01 (erts-5.8.2) [source] [smp:2:2] [rq:2] [async-threads:0] [hipe] [kernel-poll:false]&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;Eshell V5.8.2 &amp;nbsp;(abort with ^G)&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;1&amp;gt; c(test).&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;{ok,test}&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;2&amp;gt; test:test( fun test:one/1, 10000000 ).&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;...................................................................................................&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;39.70179s&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;Done 10000099&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;ok&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;Memory: 111707084&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;3&amp;gt; test:test( fun test:two/1, 10000000 ).&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;...................................................................................................&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;Done 10000098&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;Memory: 111710500&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;2.410091s&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;ok&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;4&amp;gt;&amp;nbsp;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;Using functions like this &lt;i&gt;sixteen times faster&lt;/i&gt; than passing messages. &amp;nbsp;That really surprises me, to be honest, and gives me something to think about before I go off and gen_server-ize all of my code.&lt;/span&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5297772973487593985-1337515466599931019?l=blog.ser1.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ser1.net/feeds/1337515466599931019/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5297772973487593985&amp;postID=1337515466599931019' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/1337515466599931019'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/1337515466599931019'/><link rel='alternate' type='text/html' href='http://blog.ser1.net/2011/01/my-shocking-erlang-discovery.html' title='My shocking Erlang discovery'/><author><name>SER</name><uri>http://www.blogger.com/profile/11674189468540726402</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5297772973487593985.post-7003452051454740489</id><published>2011-01-23T14:37:00.000-08:00</published><updated>2011-01-23T14:37:44.739-08:00</updated><title type='text'>I hate Yahoo!</title><content type='html'>It's buggy and irritating (I'm talking about the Groups web site). &amp;nbsp;Joining is totally borked if you don't already have a Yahoo! account, and the "tell the moderator why you want to join" has a 200-character limit on the message you send. &amp;nbsp;Only, the Yahoo! web devs are so stupid that they didn't put a live character count on the text box, so it's a guessing game to figure out if your text is short enough -- or you have to manually count the letters yourself. &amp;nbsp;Give me a break. &amp;nbsp;Idiots. &amp;nbsp;No wonder they're an almost-ran to Google.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5297772973487593985-7003452051454740489?l=blog.ser1.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ser1.net/feeds/7003452051454740489/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5297772973487593985&amp;postID=7003452051454740489' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/7003452051454740489'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/7003452051454740489'/><link rel='alternate' type='text/html' href='http://blog.ser1.net/2011/01/i-hate-yahoo.html' title='I hate Yahoo!'/><author><name>SER</name><uri>http://www.blogger.com/profile/11674189468540726402</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5297772973487593985.post-7306640630244868695</id><published>2011-01-22T05:59:00.000-08:00</published><updated>2011-01-22T07:07:06.434-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='erlang'/><category scheme='http://www.blogger.com/atom/ns#' term='software'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Running a code server in erlang</title><content type='html'>For as old as Erlang is, there's a distinct lack of documentation about some aspects of it. &amp;nbsp;I've had a hard time finding information about how to run a code server, so I'm going to write down what I've learned here.&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;First, there are a couple of things I should note:&lt;/div&gt;&lt;div&gt;&lt;ol&gt;&lt;li&gt;While you &lt;b&gt;can&lt;/b&gt;&amp;nbsp;use the erlang runtime as a hack-as-you-go platform, it's really better to follow the OTP&amp;nbsp;principles; you get a lot of things for free that you otherwise end up writing yourself. &amp;nbsp;So, go ahead and use gen_servers, and write your &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;.app&lt;/span&gt; and &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;.rel&lt;/span&gt; scripts. &amp;nbsp;I know, it feels like a lot of metadata, and&lt;b&gt;&amp;nbsp;&lt;/b&gt;I &lt;i&gt;hate&lt;/i&gt;&amp;nbsp;systems that require a bunch of metadata files scattered around. &amp;nbsp;This is because a profusion of metadata files is a common feature of some of the &lt;i&gt;worst&lt;/i&gt;&amp;nbsp;software systems -- Hibernate, for example -- but in this case, it's a necessary evil. &amp;nbsp;You&lt;i&gt;&amp;nbsp;&lt;/i&gt;can get pretty far without the files, but in the end, you'll end up needing them anyway to take full advantage of the OTP, so go ahead and write them.&lt;/li&gt;&lt;li&gt;Erlang has a really wicked (as in, good) distribution mechanism, so if you can use it, consider it. &amp;nbsp;When used, it'll build a tar-ball containing everything you need, including a stripped-down runtime, to run &amp;nbsp;a worker node; copy the tar-ball to the destination machine, unpack it, and run your app.&lt;/li&gt;&lt;li&gt;What I describe here is probably not Erlang an "best practice," but it satisfies a need, and with a small tweak, it produces #3 above.&lt;/li&gt;&lt;/ol&gt;&lt;div&gt;This whole thing hinges entirely on the boot script. &amp;nbsp;I have been unable to figure out a way to run a code server without a boot script; the Erlang documentation says this, indirectly, in several places by noting that the boot script tells the runtime how (and from where) to load code. &amp;nbsp;Although you might think, from reading the erl manpage, that you can inform the runtime about where to get code purely with &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;-loader inet -hosts IP -id Name&lt;/span&gt;, you can't -- at least, I've not been able to coerce it so. &amp;nbsp;A boot script depends on an &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;.app&lt;/span&gt;, and while it can be written by hand, it's easier to generate it from a &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;.rel&lt;/span&gt;.&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;This is what I believe to be the bare minimum to get a client to load code from a boot server:&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;A module with the application behaviour&lt;/li&gt;&lt;li&gt;A module with the supervisor behaviour&lt;/li&gt;&lt;li&gt;An &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;.app&lt;/span&gt; file for your application&lt;/li&gt;&lt;li&gt;A &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;.rel&lt;/span&gt; file, used to generate the boot script.&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;And if anybody knows of a way to use a code server with less crap, let me know. &amp;nbsp;If you want to start up services on the worker, that's easy enough -- just fire them off from within the supervisor.&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I'm starting entirely from scratch; you should be able to copy/paste and get up and running:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;44)~% mkdir myapp ; cd myapp&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;45)~/myapp % mkdir ebin src&amp;nbsp;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;46)~/myapp % cat &amp;gt; src/myapp.erl&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;-module(myapp).&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;-behaviour(application).&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;-export([start/2,stop/1]).&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;start(_,_) -&amp;gt; myapp_sup:start_link().&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;stop(_) -&amp;gt; ok.&amp;nbsp;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;47)~/myapp % cat &amp;gt; src/myapp_sup.erl&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;-module(myapp_sup).&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;-behaviour(supervisor).&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;-export([start_link/0, init/1]).&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;start_link() -&amp;gt; supervisor:start_link({local, ?MODULE}, ?MODULE, []).&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;init(_) -&amp;gt; {ok, {{one_for_one, 1, 60}, [] }}.&amp;nbsp;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;48)~/myapp % cat &amp;gt; ebin/myapp.app&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;{application, myapp,&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;[{description, "MyApp"},&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; {id, "MyApp"},&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; {vsn, "0.0.1"},&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; {modules, [ myapp, myapp_sup ]},&amp;nbsp;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; {applications, [kernel,stdlib]},&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; {maxT, infinity},&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; {registered, [myapp, myapp_sup]},&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; {mod, {myapp,[]}}&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;]&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;}.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;49)~/myapp % cat &amp;gt; ebin/myapp.rel&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;{release, {"MyApp", "0.0.1"}, {erts, "5.8.2"},&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;[{kernel, "2.14.2"}&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;,{stdlib, "1.17.2"}&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;,{sasl, "2.1.9.2"}&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;,{crypto, "2.0.2"}&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;,{myapp, "0.0.1"}&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;]&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;}.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;50)~/myapp % erlc -o ebin src/*.erl&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;51)~/myapp % erl -id bootserver -name bootserver -setcookie cookie -run erl_boot_server start 192.168.0.6&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;Erlang R14B01 (erts-5.8.2) [source] [smp:2:2] [rq:2] [async-threads:0] [hipe] [kernel-poll:false]&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;Eshell V5.8.2 &amp;nbsp;(abort with ^G)&amp;nbsp;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;(bootserver@Sean-Russells-MacBook-Pro.local)1&amp;gt; systools:make_script("ebin/myapp", [{path,["./ebin"]},local]).&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;ok&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;(bootserver@Sean-Russells-MacBook-Pro.local)2&amp;gt; ls("ebin").&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;myapp.app &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;myapp.beam &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; myapp.boot &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; myapp.rel &amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;myapp.script &amp;nbsp; &amp;nbsp; &amp;nbsp; myapp_sup.beam &amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;ok(bootserver@Sean-Russells-MacBook-Pro.local)3&amp;gt;&amp;nbsp;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;User switch command&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;--&amp;gt; q&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;52)~/myapp %&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;You don't &lt;b&gt;need&lt;/b&gt;&amp;nbsp;to run erl with all of those args if you're just making the boot script; they're needed to run the boot server. &amp;nbsp;Here's the client -- I've pared it down, because SASL dumps a bunch of debugging information that isn't relevant for this tutorial:&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;53)~ % erl -id slave -name slave -setcookie cookie -loader inet -hosts 192.168.0.6 -boot /Users/seanrussell/myapp/ebin/myapp&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;Erlang R14B01 (erts-5.8.2) [source] [smp:2:2] [rq:2] [async-threads:0] [hipe] [kernel-poll:false]&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;=PROGRESS REPORT==== 22-Jan-2011::08:40:58 ===&amp;nbsp;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;supervisor: {local,sasl_safe_sup}&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; started: [{pid,&amp;lt;0.40.0&amp;gt;},&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; {name,alarm_handler},&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; {mfargs,{alarm_handler,start_link,[]}}, &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; {restart_type,permanent},&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; {shutdown,2000},&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; {child_type,worker}]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;A bunch of stuff snipped here&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;=PROGRESS REPORT==== 22-Jan-2011::08:40:58 ===&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; application: myapp&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;started_at: 'slave@Sean-Russells-MacBook-Pro.local'&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;Eshell V5.8.2 &amp;nbsp;(abort with ^G)&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;(slave@Sean-Russells-MacBook-Pro.local)1&amp;gt; pwd().&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;/Users/seanrussell&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;ok&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;(slave@Sean-Russells-MacBook-Pro.local)2&amp;gt; code:which(myapp).&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;"/Users/seanrussell/myapp/ebin/myapp.beam"&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;(slave@Sean-Russells-MacBook-Pro.local)3&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;User switch command&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;--&amp;gt; q&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;54)~ %&amp;nbsp;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Because I did this all on one machine, you might think that the slave is getting the code directly from the filesystem, but it isn't; this works just as well from a different machine.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;It's instructive to take a look at the &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;myapp.script&lt;/span&gt; file that &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;systools&lt;/span&gt; creates, paying special attention to the paths. &amp;nbsp;If you remove the &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;local&lt;/span&gt; argument from &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;make_script&lt;/span&gt;, then &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;systools&lt;/span&gt; will change the absolute paths to &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;$ROOT&lt;/span&gt;, which it expands to the value of that variable on the server, which is in turn set by the &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;erl&lt;/span&gt; command. &amp;nbsp;You can also add an argument {variable,[MYAPP,"."]}, and systools will replace the full path with the variable $MYAPP, which you can then define on the slave by adding the argument &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;-env MYAPP /absolute/path/to/myapp/parent&lt;/span&gt;&amp;nbsp;to the &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;erl&lt;/span&gt; command.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Hopefully, this will help somebody else; it took me about three full days to figure this out. &amp;nbsp;Erlang is well documented, but having good documentation doesn't always provide enough information for how to use a system, and Erlang OTP is horrible in that respect. &amp;nbsp;Once you get it up and running, though, it's a thing of beauty.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5297772973487593985-7306640630244868695?l=blog.ser1.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ser1.net/feeds/7306640630244868695/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5297772973487593985&amp;postID=7306640630244868695' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/7306640630244868695'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/7306640630244868695'/><link rel='alternate' type='text/html' href='http://blog.ser1.net/2011/01/running-code-server-in-erlang.html' title='Running a code server in erlang'/><author><name>SER</name><uri>http://www.blogger.com/profile/11674189468540726402</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5297772973487593985.post-4298159799131832281</id><published>2010-12-12T06:15:00.000-08:00</published><updated>2010-12-12T06:15:26.186-08:00</updated><title type='text'>Google eBookstore</title><content type='html'>I was really excited about the opening of the Google eBookstore; unfortunately, and depressingly, eBookstore is made of fail:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Not all viewing format options are supported for all books. &amp;nbsp;Some support epub and PDF, some are only readable online with a web viewer or some custom app&lt;/li&gt;&lt;li&gt;It isn't possible to find out what viewing format options are available for books&lt;/li&gt;&lt;li&gt;It isn't possible to filter books by download options&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;I'm not reading a book on my computer. &amp;nbsp;I'm not reading it on my cell phone, either. &amp;nbsp;And I'm not going to buy from a store that doesn't let me download (i.e. "own") what I purchase. &amp;nbsp;eBookstore is, consequently, useless, to me, and it seems I'm stuck with Sony's overpriced store for now.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Google, until you support ePub for all of the books you sell, or at least give&amp;nbsp;customers enough information for them to tell whether or not they're throwing money away on you, you won't be getting any more of mine.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5297772973487593985-4298159799131832281?l=blog.ser1.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ser1.net/feeds/4298159799131832281/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5297772973487593985&amp;postID=4298159799131832281' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/4298159799131832281'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/4298159799131832281'/><link rel='alternate' type='text/html' href='http://blog.ser1.net/2010/12/google-ebookstore.html' title='Google eBookstore'/><author><name>SER</name><uri>http://www.blogger.com/profile/11674189468540726402</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5297772973487593985.post-959209223832865724</id><published>2010-11-30T19:52:00.000-08:00</published><updated>2010-11-30T19:52:29.170-08:00</updated><title type='text'>Erlang + TokyoTyrant / TokyoCabinet / medici</title><content type='html'>My first real, significant, observation about Erlang is this piece of advice to new Erlangers: repeat this&amp;nbsp;mantra:&lt;br /&gt;&lt;blockquote&gt;Mailboxes are &lt;b&gt;not&lt;/b&gt;&amp;nbsp;queues!&lt;/blockquote&gt;More experienced devs than I get this wrong. &amp;nbsp; In any case,&amp;nbsp;I've been working with&amp;nbsp;&lt;a href="http://www.erlang.org/"&gt;Erlang&lt;/a&gt;&amp;nbsp;and&amp;nbsp;&lt;a href="http://fallabs.com/tokyocabinet/"&gt;TokyoCabinet&lt;/a&gt;/&lt;a href="http://fallabs.com/tokyotyrant/"&gt;Tyrant&lt;/a&gt;&amp;nbsp;at work -- I've been using&amp;nbsp;&lt;a href="http://www.mongodb.org/"&gt;MongoDB&lt;/a&gt;, too; it isn't clear yet which will best fit our needs -- and&amp;nbsp;I've been using &lt;a href="https://github.com/mccoy/medici"&gt;medici&lt;/a&gt;&amp;nbsp;by Jim McCoy, a set of libraries for interfacing with TokyoTyrant/Cabinet. &amp;nbsp;I &lt;a href="https://github.com/mccoy/medici/issues#issue/2"&gt;found a bug&lt;/a&gt;&amp;nbsp;today in the principe module, which the following code demonstrates:&lt;br /&gt;&lt;br /&gt;&lt;pre style="background-color: rgb(248, 248, 255) !important; border-bottom-color: rgb(222, 222, 222) !important; border-bottom-style: solid !important; border-bottom-width: 1px !important; border-left-color: rgb(222, 222, 222) !important; border-left-style: solid !important; border-left-width: 1px !important; border-right-color: rgb(222, 222, 222) !important; border-right-style: solid !important; border-right-width: 1px !important; border-top-color: rgb(222, 222, 222) !important; border-top-style: solid !important; border-top-width: 1px !important; color: rgb(68, 68, 68) !important; font-family: helvetica, arial, freesans, clean, sans-serif; font-size: 12px !important; font: normal normal normal 12px/normal Monaco, 'Courier New', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; line-height: 1.5em !important; margin-bottom: 1em !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 1em !important; overflow-x: auto !important; overflow-y: auto !important; padding-bottom: 0.5em !important; padding-left: 0.5em !important; padding-right: 0.5em !important; padding-top: 0.5em !important;"&gt;&lt;code style="background-color: rgb(248, 248, 255) !important; border-bottom-color: rgb(222, 222, 222) !important; border-bottom-style: none !important; border-bottom-width: 1px !important; border-color: initial !important; border-left-color: rgb(222, 222, 222) !important; border-left-style: none !important; border-left-width: 1px !important; border-right-color: rgb(222, 222, 222) !important; border-right-style: none !important; border-right-width: 1px !important; border-top-color: rgb(222, 222, 222) !important; border-top-style: none !important; border-top-width: 1px !important; border-width: initial !important; color: rgb(68, 68, 68) !important; font-size: 12px !important; font: normal normal normal 12px/normal Monaco, 'Courier New', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; line-height: 1.4em; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important;"&gt;-module(principe_test).&lt;br /&gt;-export([runtest/0, loop/0]).&lt;br /&gt;&lt;br /&gt;mongod() -&amp;gt; "mongod".  % Where's your mongod executable?&lt;br /&gt;&lt;br /&gt;runtest() -&amp;gt;&lt;br /&gt;    start_mongo(),&lt;br /&gt;    Inserter = spawn_link( fun ?MODULE:loop/0 ),&lt;br /&gt;    Inserter ! quit,&lt;br /&gt;    Inserter ! go,&lt;br /&gt;    timer:sleep(500),&lt;br /&gt;    {ok, Fd} = file:open("mongod.pid",[read]),&lt;br /&gt;    {ok, Data} = file:read_line( Fd ),&lt;br /&gt;    os:cmd(io_lib:format("kill -HUP ~p",[string:strip(Data,both,10)])).&lt;br /&gt;&lt;br /&gt;start_mongo() -&amp;gt;&lt;br /&gt;    filelib:ensure_dir("test/file"),&lt;br /&gt;    Cmd = io_lib:format(&lt;br /&gt;        "~s --dbpath ~s --port 9999 --fork --logpath ~s --pidfilepath ~s", &lt;br /&gt;        [   mongod(),&lt;br /&gt;            filename:absname("test"), &lt;br /&gt;            filename:absname("mongod.log"), &lt;br /&gt;            filename:absname("mongod.pid") ]),&lt;br /&gt;    os:cmd(Cmd),&lt;br /&gt;    timer:sleep(1000).&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;loop() -&amp;gt;&lt;br /&gt;    {ok, P} = principe:connect( [{port, 9999}] ),&lt;br /&gt;    receive&lt;br /&gt;        go -&amp;gt; principe:put( P, "key", "value" )&lt;br /&gt;    end.&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div style="font-size: 13px; line-height: 1.5em !important; margin-bottom: 1em !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 1em !important; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;&lt;span class="Apple-style-span" style="font-family: helvetica, arial, freesans, clean, sans-serif;"&gt;The problem is that &lt;/span&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;principe:get/3&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family: helvetica, arial, freesans, clean, sans-serif;"&gt; (and many other functions) use &lt;/span&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;receive&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family: helvetica, arial, freesans, clean, sans-serif;"&gt; to get call-backs from the socket library, and assume that they're going to be receiving messages from &lt;i&gt;only&lt;/i&gt; the socket library. &amp;nbsp;This is a bad assumption. &amp;nbsp;In effect, the methods hijack the mailbox of the calling process and then assume that they'll only find socket messages in there. &amp;nbsp;Happily, the fix is really small:&lt;/span&gt;&lt;/div&gt;&lt;pre style="background-color: rgb(248, 248, 255) !important; border-bottom-color: rgb(222, 222, 222) !important; border-bottom-style: solid !important; border-bottom-width: 1px !important; border-left-color: rgb(222, 222, 222) !important; border-left-style: solid !important; border-left-width: 1px !important; border-right-color: rgb(222, 222, 222) !important; border-right-style: solid !important; border-right-width: 1px !important; border-top-color: rgb(222, 222, 222) !important; border-top-style: solid !important; border-top-width: 1px !important; color: rgb(68, 68, 68) !important; font-family: helvetica, arial, freesans, clean, sans-serif; font-size: 12px !important; font: normal normal normal 12px/normal Monaco, 'Courier New', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; line-height: 1.5em !important; margin-bottom: 1em !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 1em !important; overflow-x: auto !important; overflow-y: auto !important; padding-bottom: 0.5em !important; padding-left: 0.5em !important; padding-right: 0.5em !important; padding-top: 0.5em !important;"&gt;&lt;code style="background-color: rgb(248, 248, 255) !important; border-bottom-color: rgb(222, 222, 222) !important; border-bottom-style: none !important; border-bottom-width: 1px !important; border-color: initial !important; border-left-color: rgb(222, 222, 222) !important; border-left-style: none !important; border-left-width: 1px !important; border-right-color: rgb(222, 222, 222) !important; border-right-style: none !important; border-right-width: 1px !important; border-top-color: rgb(222, 222, 222) !important; border-top-style: none !important; border-top-width: 1px !important; border-width: initial !important; color: rgb(68, 68, 68) !important; font-size: 12px !important; font: normal normal normal 12px/normal Monaco, 'Courier New', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; line-height: 1.4em; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important;"&gt;diff --git a/src/principe.erl b/src/principe.erl&lt;br /&gt;--- a/src/principe.erl&lt;br /&gt;+++ b/src/principe.erl&lt;br /&gt;@@ -718,7 +718,7 @@&lt;br /&gt;            {error, conn_closed};&lt;br /&gt;         {tcp_error, _, _} -&amp;gt; &lt;br /&gt;            {error, conn_error};&lt;br /&gt;-        Data -&amp;gt; &lt;br /&gt;+    {tcp, _, _} = Data -&amp;gt;&lt;br /&gt;            ResponseHandler(Data)&lt;br /&gt;     after ?TIMEOUT -&amp;gt; &lt;br /&gt;            {error, timeout}&lt;/code&gt;&lt;/pre&gt;Incidentally, MongoDB is about 4x as fast as Riak, and TokyoTyrant is about 2x as fast as MongoDB. &amp;nbsp;MongoDB and TokyoTyrant are faster than opening a file directly on disk for each small record. &amp;nbsp;Riak is about as fast as direct filesystem access. &amp;nbsp;That's almost certainly due to memory caching + bulk writes used by MDB &amp;amp; TT, vs. many, many inode create/write/close calls.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5297772973487593985-959209223832865724?l=blog.ser1.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ser1.net/feeds/959209223832865724/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5297772973487593985&amp;postID=959209223832865724' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/959209223832865724'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/959209223832865724'/><link rel='alternate' type='text/html' href='http://blog.ser1.net/2010/11/erlang-tokyotyrant-tokyocabinet-medici.html' title='Erlang + TokyoTyrant / TokyoCabinet / medici'/><author><name>SER</name><uri>http://www.blogger.com/profile/11674189468540726402</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5297772973487593985.post-8985634191700925773</id><published>2010-11-25T05:56:00.000-08:00</published><updated>2010-11-25T05:56:11.778-08:00</updated><title type='text'>Taco Bell Programming</title><content type='html'>Ted Dziuba has an &lt;a href="http://teddziuba.com/2010/10/taco-bell-programming.html"&gt;interesting article&lt;/a&gt; on what he calls Taco Bell Programming; it's worth reading -- there is a lot of value in the concept he's promoting. &amp;nbsp;I had some concerns about the practicality of the approach, so I ran some tests.&lt;br /&gt;&lt;br /&gt;I produced a 297MB file containing 20 million lines of more-or-less random data:&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: x-small;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;blockquote class="webkit-indent-blockquote" style="border-bottom-style: none; border-color: initial; border-left-style: none; border-right-style: none; border-top-style: none; border-width: initial; font-family: arial; margin-bottom: 0px; margin-left: 40px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'courier new', monospace;"&gt;&lt;br class="Apple-interchange-newline" /&gt;ruby -e '20000000.times { |x| \&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'courier new', monospace;"&gt;&amp;nbsp;&amp;nbsp;puts "#{x}\03#{rand(1000000)}" \&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'courier new', monospace;"&gt;}' &amp;gt; bigfile.txt&lt;/span&gt;&lt;/div&gt;&lt;/blockquote&gt;&lt;div style="font-family: arial;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-family: arial;"&gt;Then I ran:&lt;/div&gt;&lt;div style="font-family: arial;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;blockquote class="webkit-indent-blockquote" style="border-bottom-style: none; border-color: initial; border-left-style: none; border-right-style: none; border-top-style: none; border-width: initial; font-family: arial; margin-bottom: 0px; margin-left: 40px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;&lt;div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'courier new', monospace;"&gt;cat bigfile.txt&amp;nbsp;|\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/blockquote&gt;&lt;blockquote class="webkit-indent-blockquote" style="border-bottom-style: none; border-color: initial; border-left-style: none; border-right-style: none; border-top-style: none; border-width: initial; font-family: arial; margin-bottom: 0px; margin-left: 40px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;&lt;div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'courier new', monospace;"&gt;&amp;nbsp;&amp;nbsp;gawk -F '\03' '{print $1, $0}' |\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/blockquote&gt;&lt;blockquote class="webkit-indent-blockquote" style="border-bottom-style: none; border-color: initial; border-left-style: none; border-right-style: none; border-top-style: none; border-width: initial; font-family: arial; margin-bottom: 0px; margin-left: 40px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;&lt;div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'courier new', monospace;"&gt;&amp;nbsp;&amp;nbsp;xargs -n2 -P7 printf "%s -- %s\n" &amp;gt; newfile.txt&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/blockquote&gt;&lt;div style="font-family: arial;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-family: arial;"&gt;This is a baseline; a data producer, such as pulling from a database,&amp;nbsp;is going to produce data more slowly than&amp;nbsp;&lt;span class="Apple-style-span" style="font-family: 'courier new', monospace;"&gt;cat&lt;/span&gt;, and&amp;nbsp;&lt;span class="Apple-style-span" style="font-family: 'courier new', monospace;"&gt;printf&lt;/span&gt;&amp;nbsp;is going to write lines more quickly than whatever we're doing to process the data. &amp;nbsp;Here are the results:&lt;/div&gt;&lt;div style="font-family: arial;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;blockquote class="webkit-indent-blockquote" style="border-bottom-style: none; border-color: initial; border-left-style: none; border-right-style: none; border-top-style: none; border-width: initial; font-family: arial; margin-bottom: 0px; margin-left: 40px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: arial, helvetica, sans-serif;"&gt;P &amp;nbsp; &amp;nbsp; Time&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: arial, helvetica, sans-serif;"&gt;----- --------------&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: arial, helvetica, sans-serif;"&gt;7 &amp;nbsp; &amp;nbsp; 68m&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: arial, helvetica, sans-serif;"&gt;3 &amp;nbsp; &amp;nbsp; 88m&lt;/span&gt;&lt;/div&gt;&lt;/blockquote&gt;&lt;blockquote class="webkit-indent-blockquote" style="border-bottom-style: none; border-color: initial; border-left-style: none; border-right-style: none; border-top-style: none; border-width: initial; font-family: arial; margin-bottom: 0px; margin-left: 40px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: arial, helvetica, sans-serif;"&gt;1 &amp;nbsp; &amp;nbsp; 241m&lt;/span&gt;&lt;/div&gt;&lt;/blockquote&gt;&lt;div style="font-family: arial;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: arial;"&gt;xargs was doing all of the work in this test, as far as &lt;/span&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;top&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family: arial;"&gt; could tell, but the multiple processing helped. &amp;nbsp;This confirmed my suspicions: xargs is having to spawn off a new Linux process for every line, which is not cheap. &amp;nbsp;For comparison, a similar program written in Erlang (not known as being the fastest language in the world) was able to process the same amount of data, on a machine with half as many cores, in 20 minutes. &amp;nbsp;In addition, the machine that it was running on was also: (1) running a process which was pulling data from a SqlServer database, itself, consuming 90% of a core, (2) inserting the results into a MongoDB database, and (3) running the MongoDB server that was being inserted into. &amp;nbsp;So: half the resources, doing much more work, and it still runs over 3x as fast.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: arial;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: arial;"&gt;Ted's main point -- that code is a liability -- is still valid, and it's always useful starting out a project asking yourself how you could solve your problem with such tools. &amp;nbsp;However, take such approaches with a grain of salt; if you can afford lackluster performance, it's probably a worthwhile solution. &amp;nbsp;If performance is any sort of consideration, you may need to seek other solutions.&lt;/span&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5297772973487593985-8985634191700925773?l=blog.ser1.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ser1.net/feeds/8985634191700925773/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5297772973487593985&amp;postID=8985634191700925773' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/8985634191700925773'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/8985634191700925773'/><link rel='alternate' type='text/html' href='http://blog.ser1.net/2010/11/taco-bell-programming.html' title='Taco Bell Programming'/><author><name>SER</name><uri>http://www.blogger.com/profile/11674189468540726402</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5297772973487593985.post-2331599318672145419</id><published>2010-11-24T04:35:00.000-08:00</published><updated>2010-11-24T04:35:58.336-08:00</updated><title type='text'>Raw milk is made of win</title><content type='html'>Have I sung you the praises of raw milk? &amp;nbsp;No?&lt;br /&gt;&lt;br /&gt;Its good stuff, milk. &amp;nbsp;I used to drink between a half and a full gallon a week, prior to moving to our house in Elverson, PA. &amp;nbsp;The best thing about this house is that near it, just down the road, is a dairy, and this dairy sells raw milk. &amp;nbsp;My wife commented the other day that we're up to two gallons a week. &amp;nbsp;Sometimes, that's all I have as a meal.&lt;br /&gt;&lt;br /&gt;The first time you try raw milk, the flavor is a bit odd -- it's not bad, but is definitely different. &amp;nbsp;However, going back in the other direction (from raw to pasteurized) is an entirely different matter: after drinking raw milk for a few months, pasteurized milk sucks. &amp;nbsp;It's almost undrinkable.&lt;br /&gt;&lt;br /&gt;Anyway, I'm not on a campaign to convert anybody. &amp;nbsp;I just love raw milk. &amp;nbsp;If you want to know more, though, check out the &lt;a href="http://en.wikipedia.org/wiki/Raw_milk"&gt;wikipedia article&lt;/a&gt;, and &lt;a href="http://www.raw-milk-facts.com/"&gt;this site&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5297772973487593985-2331599318672145419?l=blog.ser1.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ser1.net/feeds/2331599318672145419/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5297772973487593985&amp;postID=2331599318672145419' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/2331599318672145419'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/2331599318672145419'/><link rel='alternate' type='text/html' href='http://blog.ser1.net/2010/11/raw-milk-is-made-of-win.html' title='Raw milk is made of win'/><author><name>SER</name><uri>http://www.blogger.com/profile/11674189468540726402</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5297772973487593985.post-1526080474357842419</id><published>2010-11-22T18:42:00.000-08:00</published><updated>2010-11-22T18:42:35.628-08:00</updated><title type='text'>Linux and the Mimo 710S</title><content type='html'>The Mimo 710S is a small-ish LCD panel (7" diameter) that connects to your computer via a USB cable and acts as a monitor. &amp;nbsp;It uses DisplayLink as a protocol. &amp;nbsp;After some trial and tribulation, I got it set up and working with Ubuntu 10.10, and here is how I did it.&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;First, you need the udlfb kernel module, and the displaylink xorg driver. &amp;nbsp;One of the best tutorials is &lt;a href="http://mulchman.org/blog/?tag=displaylink"&gt;provided by mulchman&lt;/a&gt;; I won't repeat what he says, although I will point out that he has you reboot your computer in the middle of the install, and that this was entirely&amp;nbsp;unnecessary&amp;nbsp;(I didn't). &amp;nbsp;It's all pretty easy; install some build tools if you don't already have them, get the udlfb sources, and build and install the two parts. &amp;nbsp;There are no pre-built packages for Ubuntu (as of the time of this writing).&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;That was the easy part. &amp;nbsp;I had no problem getting the device registered, and the test cases packaged with udlfb worked immediately. &amp;nbsp;My problem was getting something &lt;b&gt;useful&lt;/b&gt; onto&amp;nbsp;the device.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I had a number of issues. &amp;nbsp;First, I didn't want to use &lt;a href="http://en.wikipedia.org/wiki/Xinerama"&gt;Xinerama&lt;/a&gt;, because Xinerama imposes a bit-depth limitation (all displays under Xinerama must share the same bit depth; the Mimo has a max depth of 16 bits, and my graphics card doesn't want to do anything less than 24). &amp;nbsp;Second, I have to use the fglrx drivers, because&amp;nbsp;&lt;a href="http://xmonad.org/"&gt;xmonad&lt;/a&gt;&amp;nbsp;-- the very finest window manager, ever -- &lt;a href="http://code.google.com/p/xmonad/issues/detail?id=253"&gt;doesn't work on two screens&lt;/a&gt; properly&amp;nbsp;without Xinerama. &amp;nbsp;Third, fglrx crashes when it's paired with a displaylink device. &amp;nbsp;I didn't dig into this very far; it's a pretty hard core-dump, though -- it's proprietary software, so that's to be expected. &amp;nbsp;This combination of limitations (and it took me a while to discover all of them, let me tell you) drove my final solution, which I'm happy to say works just fine.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;What I ended up with is two xserver instances -- one basically my pre-Mimo instance, and one a new instance for just the Mimo -- tied together with &lt;a href="http://synergy-foss.org/"&gt;Synergy&lt;/a&gt;. &amp;nbsp;If you don't know Synergy, go download it, now. &amp;nbsp;It ranks among the most useful pieces of software, ever. &amp;nbsp;It runs on Windows, Linux, and OSX, and lets you share a keyboard and mouse between multiple computers (or, as it happens, xservers) like a software KVM.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Anyway, here are the bits. &amp;nbsp;First, you need to add a mimo section to your xorg. &amp;nbsp;I just tacked this on to the end of mine:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;div style="background-color: lightgrey; margin-left: 20px;"&gt;########################################&lt;br /&gt;# DisplayLink Screen&lt;br /&gt;########################################&lt;br /&gt;Section "ServerLayout"&lt;br /&gt;&amp;nbsp;&amp;nbsp;Identifier "Mimo Layout"&lt;br /&gt;&amp;nbsp;&amp;nbsp;Screen 0 "DisplayLinkScreen" 0 0&lt;br /&gt;&amp;nbsp;&amp;nbsp;Option "AutoAddDevices" "false"&lt;br /&gt;&amp;nbsp;&amp;nbsp;Option "AllowEmptyInput" "true"&lt;br /&gt;&amp;nbsp;&amp;nbsp;Option "AutoEnableDevices" "false"&lt;br /&gt;EndSection&lt;br /&gt;&lt;br /&gt;Section "Device"&lt;br /&gt;&amp;nbsp;&amp;nbsp;Identifier "DisplayLinkDevice"&lt;br /&gt;&amp;nbsp;&amp;nbsp;Driver "displaylink"&lt;br /&gt;&amp;nbsp;&amp;nbsp;Option "fbdev" "/dev/fb1"&lt;br /&gt;EndSection&lt;br /&gt;&lt;br /&gt;Section "Monitor"&lt;br /&gt;&amp;nbsp;&amp;nbsp;Identifier "DisplayLinkMonitor"&lt;br /&gt;&amp;nbsp;&amp;nbsp;DisplaySize 152 92&lt;br /&gt;EndSection&lt;br /&gt;&lt;br /&gt;Section "Screen"&lt;br /&gt;&amp;nbsp;&amp;nbsp;Identifier "DisplayLinkScreen"&lt;br /&gt;&amp;nbsp;&amp;nbsp;Device "DisplayLinkDevice"&lt;br /&gt;&amp;nbsp;&amp;nbsp;Monitor "DisplayLinkMonitor"&lt;br /&gt;&amp;nbsp;&amp;nbsp;DefaultDepth 16&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;SubSection "Display"&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;Viewport 0 0&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;Depth 16&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;Modes "800x480"&lt;br /&gt;&amp;nbsp;&amp;nbsp;EndSubSection&lt;br /&gt;EndSection&lt;/div&gt;&lt;br /&gt;The next bit is a script that looks like this:&lt;br /&gt;&lt;br /&gt;&lt;div style="background-color: lightgrey; margin-left: 20px;"&gt;#!/bin/sh&lt;br /&gt;&lt;br /&gt;X -nolisten tcp -novtswitch -sharevts -layout "Mimo Layout" :1 &amp;amp;&lt;br /&gt;synergys --config .synergyl --display :0 --name big --daemon&lt;br /&gt;synergyc --display :1 --name little -f localhost &amp;amp;&lt;br /&gt;DISPLAY=:1 xmonad &amp;amp;&lt;/div&gt;&lt;br /&gt;The first piece of magic starts X in multihead mode; the &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;-novtswitch&lt;/span&gt; argument is fricken' critical; without it, you're locked out of your computer. &amp;nbsp;At least, I was. &amp;nbsp;Once that's up, X is running on the Mimo, and you can now start synergys (server) on your main display (:0), and synergyc (client) on your Mimo display (:1), which allows you access to that second xserver with your input devices. &amp;nbsp;I won't describe how to configure synergy; you can figure that out better from the synergy documentation. &amp;nbsp;Suffice it to say that the &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;--name&lt;/span&gt; arguments have to match what's in the file. &amp;nbsp;The final line starts up another xmonad (or whatever window manager you like) on the second xserver. &amp;nbsp;And voila:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_gnlBpVYS_QM/TOsoTOAGF4I/AAAAAAAAB50/YA3D78nTePQ/s1600/IMG_20101122_194306.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="480" src="http://3.bp.blogspot.com/_gnlBpVYS_QM/TOsoTOAGF4I/AAAAAAAAB50/YA3D78nTePQ/s640/IMG_20101122_194306.jpg" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;That's the Mimo to the left, in case you couldn't tell. &amp;nbsp;I run an IRC/IM client (&lt;a href="http://www.irssi.org/"&gt;irssi&lt;/a&gt;) on it, and it's perfect -- the chat's always visible. &amp;nbsp;Here's another shot with xlock(s) running:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_gnlBpVYS_QM/TOsnyXwI_0I/AAAAAAAAB5w/wTOk8_hfHP8/s1600/IMG_20101122_194540.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="300" src="http://1.bp.blogspot.com/_gnlBpVYS_QM/TOsnyXwI_0I/AAAAAAAAB5w/wTOk8_hfHP8/s400/IMG_20101122_194540.jpg" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;For $100-some bucks, it's not a bad investment.&lt;br /&gt;&lt;br /&gt;Oh, that's a fairly decent Dell under the Mimo, but it has Windows 7 on it, and so is practically useless.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5297772973487593985-1526080474357842419?l=blog.ser1.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ser1.net/feeds/1526080474357842419/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5297772973487593985&amp;postID=1526080474357842419' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/1526080474357842419'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/1526080474357842419'/><link rel='alternate' type='text/html' href='http://blog.ser1.net/2010/11/linux-and-mimo-710s.html' title='Linux and the Mimo 710S'/><author><name>SER</name><uri>http://www.blogger.com/profile/11674189468540726402</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_gnlBpVYS_QM/TOsoTOAGF4I/AAAAAAAAB50/YA3D78nTePQ/s72-c/IMG_20101122_194306.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5297772973487593985.post-3522867521331017219</id><published>2010-11-20T16:59:00.000-08:00</published><updated>2010-11-20T16:59:59.741-08:00</updated><title type='text'>A perfect example of horrible design</title><content type='html'>The &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;vorbiscomment&lt;/span&gt; command of the Vorbis toolset is one of the most poorly designed pieces of software that I've had the misfortune of using in a long time. &amp;nbsp;The tool can be used to either add a new comment to an ogg file, or entirely replace all comments. &amp;nbsp;It can &lt;b&gt;not&lt;/b&gt;&amp;nbsp;be used to update an existing comment. &amp;nbsp;It's a clear violation of "make the common case simple, and the uncommon possible." &amp;nbsp;Updating, or modifying, comments is the one, most common, thing a user might want to do -- and you can't do that with &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;vorbiscomment&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;Here's a use case: you rip a CD, and accidentally enter the wrong DISCNUMBER -- say, 1 instead of 3 -- and you're left with 16 ripped songs all with good metadata, &lt;i&gt;except&lt;/i&gt; for the DISCNUMBER. &amp;nbsp;How do you fix this with &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;vorbiscomment&lt;/span&gt;? &amp;nbsp;Well, if the tool was not totally stupid, you'd do something like this:&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; $ nonstupidvorbiscomment -u -t "DISKNUMBER=3" *.ogg&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;or, if (for whatever reason) you need to perform a more complex search:&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; $ find . -regex ".*/Album/.*\.ogg" -exec \&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp;nonstupidvorbiscomment -u -t "DISCNUMBER=3" {} \;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;But you can't do that, because vorbiscomment doesn't have a "-u" (update) argument. &amp;nbsp;No, all you get is -w (write) and -a (add). &amp;nbsp;So you end up doing something like this:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; $ find . -regex ".*/Album/.*\.ogg" -exec &amp;nbsp;\&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp;echo vorbiscomment -l \"{}\" \| &amp;nbsp; &amp;nbsp; &amp;nbsp;\&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp;sed \'s/DISCNUMBER=1/DISCNUMBER=3/\' \&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp;vorbiscomment -w -c - \"{}\" &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; \; &amp;gt; runme.sh&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; $ ./runme.sh&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; $ rm runme.sh&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;Yeah, that's slick. &amp;nbsp;Not. &amp;nbsp;Somebody should have reviewed &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;vorbiscomment&lt;/span&gt; before letting it be released.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5297772973487593985-3522867521331017219?l=blog.ser1.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ser1.net/feeds/3522867521331017219/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5297772973487593985&amp;postID=3522867521331017219' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/3522867521331017219'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/3522867521331017219'/><link rel='alternate' type='text/html' href='http://blog.ser1.net/2010/11/perfect-example-of-horrible-design.html' title='A perfect example of horrible design'/><author><name>SER</name><uri>http://www.blogger.com/profile/11674189468540726402</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5297772973487593985.post-2618561704563164810</id><published>2010-11-08T04:41:00.000-08:00</published><updated>2010-11-08T04:42:59.232-08:00</updated><title type='text'>Our new media streamer</title><content type='html'>Sometime in the past couple of months, our Roku died. &amp;nbsp;I don't know why; but it doesn't power on any more. &amp;nbsp;Monika's never liked the thing, because she found it difficult to navigate -- we've digitized all of our CDs over the years, which means that we have some 6k songs on the server, and it can be challenging to find music when you're used to flipping through CD covers.&lt;br /&gt;&lt;br /&gt;Another new development in our music consumption has been Pandora, and it's been nothing but a pain trying to stream Pandora over the Roku. &amp;nbsp;So, last weekend, I purchased a new media streaming device; the one I chose was the &lt;a href="http://www.logitech.com/en-us/speakers-audio/wireless-music-systems/devices/3817"&gt;Logitech Squeezebox Duet&lt;/a&gt;. &amp;nbsp;We've had it for almost a week now, and it's been great -- this one is definitely a "buy."&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://www.logitech.com/assets/15273/15273.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="320" src="http://www.logitech.com/assets/15273/15273.png" width="291" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;In particular, there are three things about it we like:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;The remote is awesome. &amp;nbsp;It is large enough to show cover art, and scroll-lists (it might remind one of the iPod interface). &amp;nbsp;It controls the server via wifi, so you don't need line-of-sight to control the box. &amp;nbsp;The battery is respectable, considering the remote is running a full-fledged Linux install.&lt;/li&gt;&lt;li&gt;Pandora. &amp;nbsp;Plus everything else you could want to stream. &amp;nbsp;There are applets for a bunch of &amp;nbsp;services, such as Last.fm, BBC, and Napster, as well as non-music stuff such as Flickr and Facebook. &amp;nbsp;It's all easy to install, too.&lt;/li&gt;&lt;li&gt;Squeezebox Server. &amp;nbsp;First, it runs on Linux. &amp;nbsp;Yay, Logitech! &amp;nbsp;And it's a nice piece of software, to boot: the UI is handsome and easy to navigate, it is trivial to set up, and it understands Ogg and Flac, and transcodes automatically if it needs to. &amp;nbsp;Monika particularly likes setting up play lists on it, and controlling the music if she happens to be working on her computer.&lt;/li&gt;&lt;/ul&gt;Beyond these features, which are alone worth the cost of the system, I liked the fact that set-up was dead-simple. &amp;nbsp;I've been struggling with mt-daapd (Firefly) for the past couple of years, and it's been challenging at times to keep transcoding configured and working. &amp;nbsp;I've spent waaaay too much time in the mt-daapd.conf file. &amp;nbsp;Squeezebox Server is in the Ubuntu repositories, so it wasn't even a manual download, and configuration is all through the web interface. &amp;nbsp;Again, I've been thrilled with the server.&lt;br /&gt;&lt;br /&gt;I also got a thrill when I discovered that you could ssh into the remote. &amp;nbsp;I spent some time on the weekend unsuccessfully enabling the IR emitter on the handset; there's an app, but configuration is a pain and I never did get it to work. &amp;nbsp;However, being able to ssh into the handset and look at the log files was interesting, and merits more exploration.&lt;br /&gt;&lt;br /&gt;I'm excited about the extensibility, too. &amp;nbsp;Theoretically, you can simul-stream to multiple devices in the house, and that's something else I'd like to look into; it'd be nice to have around the holidays.&lt;br /&gt;&lt;br /&gt;There are always a couple of things that could be improved. &amp;nbsp;First, I'd like to get the IR working so that I can use the remote to turn on the amp. &amp;nbsp;There are solutions, but they're all pretty hacky, and the four hours I spent on the weekend trying to get SqueezeIR working is my limit these days for messing with something like this. &amp;nbsp;Second, the remote takes a long time to wake up. &amp;nbsp;I'm used to my Mac, which wakes up from sleep mode before I can fully open the lid; the Duet remote sits and spins for a dozen seconds or so before it presents it's UI. &amp;nbsp;I blame Linux for that, though; I've yet to see a Linux box wake up from a sleep with anything resembling agility. &amp;nbsp;Even Windows beats Linux there, and that's just sad.&lt;br /&gt;&lt;br /&gt;So, if you're in the market for a streaming media receiver, I highly recommend the Logitech Squeezebox Duet. &amp;nbsp;If all you're looking for is to stream Pandora, there are much cheaper alternatives; however, if you have a digital library that you want access to, as well as access to online streaming internet radio, the Duet is a low-pain solution.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5297772973487593985-2618561704563164810?l=blog.ser1.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ser1.net/feeds/2618561704563164810/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5297772973487593985&amp;postID=2618561704563164810' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/2618561704563164810'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/2618561704563164810'/><link rel='alternate' type='text/html' href='http://blog.ser1.net/2010/11/our-new-media-streamer.html' title='Our new media streamer'/><author><name>SER</name><uri>http://www.blogger.com/profile/11674189468540726402</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5297772973487593985.post-2717111047844027859</id><published>2010-11-06T05:13:00.000-07:00</published><updated>2010-11-06T05:13:31.843-07:00</updated><title type='text'>Success!</title><content type='html'>I did it. &amp;nbsp;It was a long, hard slog, but I've finally gotten down to my target weight.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_gnlBpVYS_QM/TNVFaWfbqkI/AAAAAAAAB38/jcPlVtz1c8g/s1600/Screen+shot+2010-11-06+at+8.08.27+AM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="157" src="http://4.bp.blogspot.com/_gnlBpVYS_QM/TNVFaWfbqkI/AAAAAAAAB38/jcPlVtz1c8g/s400/Screen+shot+2010-11-06+at+8.08.27+AM.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Actually, the first 35 pounds wasn't all that difficult. &amp;nbsp;I started this last January at 240 pounds, setting my target at 200. &amp;nbsp;The first 12 pounds went in the first two weeks, and then it was a pound here, two pounds there, with slippages around holidays. &amp;nbsp;The problem was the plateau: I hovered between 204 and 206 for over 5 months, and although I never went back up above 206, I could never break 201, either.&lt;br /&gt;&lt;br /&gt;There are a number of reasons for this. &amp;nbsp;We're on the South Beach Diet, and while Monika has been pretty strict about it, I haven't been. &amp;nbsp;For example, the holidays that I mentioned above means that we both gorge on whatever we normally would on the holiday, with little restraint. &amp;nbsp;Those times have set me back as much as 4 pounds per event. &amp;nbsp;When we go overseas, we don't watch what we eat (although, we tend to walk more, so that's nearly always a break-even for me -- I think I need to vacation more). &amp;nbsp;And I have long-standing, recurring lunch with a friend on Friday, which ritualistically consists of a burger and beer (I give a nod to the diet by eschewing the fries for a salad, which I never eat anyway). &amp;nbsp;Except at the start, I don't eat the menus for lunch. &amp;nbsp;I eyeball it and stay low on the carbs. &amp;nbsp;And breakfasts are fudge-able, as far as the fat content goes. &amp;nbsp;I'm sure that the single-malts on the weekend haven't helped.&lt;br /&gt;&lt;br /&gt;The lesson here is that if one sticks to the diet, one can lose weight pretty fast. &amp;nbsp;Monika got down to her target weight (-35 lbs) within four months, and neither of us have changed our exercise habits significantly (she's always worked out; I, never). &amp;nbsp;I also know that I can keep my weight to within this range by watching what I eat, and still allow myself scotch, and the occasionally beer and burger.&lt;br /&gt;&lt;br /&gt;I claim this diet a success. &amp;nbsp;Now, bring on the Thanksgiving and Christmas holidays!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5297772973487593985-2717111047844027859?l=blog.ser1.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ser1.net/feeds/2717111047844027859/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5297772973487593985&amp;postID=2717111047844027859' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/2717111047844027859'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/2717111047844027859'/><link rel='alternate' type='text/html' href='http://blog.ser1.net/2010/11/success.html' title='Success!'/><author><name>SER</name><uri>http://www.blogger.com/profile/11674189468540726402</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_gnlBpVYS_QM/TNVFaWfbqkI/AAAAAAAAB38/jcPlVtz1c8g/s72-c/Screen+shot+2010-11-06+at+8.08.27+AM.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5297772973487593985.post-4114049257733242413</id><published>2010-11-05T06:03:00.000-07:00</published><updated>2010-11-05T06:03:53.967-07:00</updated><title type='text'>pandoc &amp; PDF</title><content type='html'>Oy.&lt;br /&gt;&lt;br /&gt;There's a good reason why I hate LaTeX, and that's bloat. &amp;nbsp;Here's an example:&lt;br /&gt;&lt;br /&gt;rst2pdf (restructured text to PDF) converts marked-up text to PDF. &amp;nbsp;The package, and it's dependencies, take maybe a couple of tens of megs at the most.&lt;br /&gt;&lt;br /&gt;pandoc depends on LaTeX to produce PDF files. &amp;nbsp;The &lt;b&gt;minimum&lt;/b&gt;&amp;nbsp;packages needed to produce PDF with LaTeX (using markdown2pdf) consumes nearly a&amp;nbsp;&lt;b&gt;gigabyte&lt;/b&gt;&amp;nbsp;of disk space. &amp;nbsp;I mean, seriously; that's absurd. I'm no LaTeX newbie; it's always been a bloated hog, but it's getting much worse. &amp;nbsp;And you know what? &amp;nbsp;It doesn't do anything more than the alternatives. &amp;nbsp;Lout, for example, at a fraction of the disk space will do math just as well, thank you, and produce just as pretty output with just as much control over formatting -- although, Lout appears to be defunct. &amp;nbsp;Oh, well.&lt;br /&gt;&lt;br /&gt;Anyway, I'm just frustrated with bloat lately, and although I really want to use pandoc (docutils is nice, but pandoc has a broader range of formatting options, such as markup for strike-out, subscript, and superscript, and better table formatting control), I'm loath to install a gig of crap just to get it.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5297772973487593985-4114049257733242413?l=blog.ser1.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ser1.net/feeds/4114049257733242413/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5297772973487593985&amp;postID=4114049257733242413' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/4114049257733242413'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/4114049257733242413'/><link rel='alternate' type='text/html' href='http://blog.ser1.net/2010/11/pandoc-pdf.html' title='pandoc &amp; PDF'/><author><name>SER</name><uri>http://www.blogger.com/profile/11674189468540726402</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5297772973487593985.post-4041185017586539721</id><published>2010-08-07T07:35:00.000-07:00</published><updated>2010-08-07T07:35:34.572-07:00</updated><title type='text'>Another reason why I hate Java</title><content type='html'>It's the whole problem that there are layers and layers of crap between you, and what you want to do, and every one of those layers can have bugs. &amp;nbsp;In this case, the bug is in Jakarta Ant. &amp;nbsp;The problem is that it took me two hours to figure out that it wasn't something that &lt;b&gt;I&lt;/b&gt;&amp;nbsp;was doing, but that it was something in Ant, or the interaction with Ant and OSX's Java, or... well, I don't know where the problem is, and I don't really care. &amp;nbsp;Struggling to get something as fundamental as this working isn't how I want to spend my time.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;So, here's the thing: you'd &lt;b&gt;expect&lt;/b&gt;&amp;nbsp;these two chunks of Ant script to do, essentially, the same thing. &amp;nbsp;Yeah, the internals are different, but you'd hope they'd have the same high-level effect:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt; &lt;/span&gt;&lt;java classpath="${run.classpath}"&gt;&lt;/java&gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt; &lt;/span&gt; &amp;nbsp; &amp;nbsp; &amp;nbsp;classname="com.tsl.server.TslRMIImpl"/&amp;gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;  &lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt; &lt;/span&gt;&lt;exec executable="java"&gt;&lt;/exec&gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;  &lt;/span&gt;&lt;arg value="-cp"&gt;&lt;/arg&gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;  &lt;/span&gt;&lt;arg value="${run.classpath}"&gt;&lt;/arg&gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;  &lt;/span&gt;&lt;arg value="com.tsl.server.TslRMIImpl"&gt;&lt;/arg&gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt; &lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Right? &amp;nbsp;I mean, nothing complex or tricky here. &amp;nbsp;But they don't. &amp;nbsp;Ant completely fucks up the first one; the class path is not set correctly. &amp;nbsp;At least, it doesn't work with Ant 1.7.1 and Java 1.6.0 on OSX 10.6.4. &amp;nbsp;Put these two lines in the &lt;b&gt;same&lt;/b&gt;&amp;nbsp;target, just like they are, and the first one prints out an entirely different classpath than the second (assuming that the class code prints the class path). &amp;nbsp;The first one's class path isn't even remotely correct; a dump of the ClassLoader class path contains exactly one element, and that's some ant jar file.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Seriously, I have better things to do with my time than waste it tracking down somebody else's brain farts. &amp;nbsp; It's why I hate the Java VM, and more especially, things built on top of it -- frameworks are especially bad, because they're just chock full of other people bad code.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5297772973487593985-4041185017586539721?l=blog.ser1.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ser1.net/feeds/4041185017586539721/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5297772973487593985&amp;postID=4041185017586539721' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/4041185017586539721'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/4041185017586539721'/><link rel='alternate' type='text/html' href='http://blog.ser1.net/2010/08/another-reason-why-i-hate-java.html' title='Another reason why I hate Java'/><author><name>SER</name><uri>http://www.blogger.com/profile/11674189468540726402</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5297772973487593985.post-367232705902984775</id><published>2010-07-25T10:03:00.000-07:00</published><updated>2010-07-25T10:03:45.832-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='review'/><category scheme='http://www.blogger.com/atom/ns#' term='scotch'/><title type='text'>Single Malt Scotches</title><content type='html'>For the past few weeks, I've been sampling single malt scotches.&lt;br /&gt;&lt;br /&gt;It all started when we went to visit Brett and Marnie in Paris, and were trying to think of a hostess gift for them. &amp;nbsp;Marnie likes Cognac, so we thought we'd bring them a bottle -- there's a whole story around that which I won't get into, involving the TSA and difficulties in transporting bottles of flammable liquid on an airplane -- and in doing that, I spent a lot of time online researching Cognac, and found it fascinating.&lt;br /&gt;&lt;br /&gt;When we got back, I got to thinking about Scotch. &amp;nbsp;I have a couple of friends who are "into" single malts, and one who told me that the single malts are an entirely different beast from lesser Scotches (and whiskeys), so I thought I'd try it. &amp;nbsp;I started out with a mid-range bottle of Scotch (ca. $70), which was good enough that I'm now on my fifth bottle (all of varying degrees of emptiness), and I thought it was a good time to lay down some thoughts on the varying brands.&lt;br /&gt;&lt;br /&gt;I didn't keep track of the first bottle, but it was an 18yo bottle of something, and I liked it enough to consume the entire thing in short order. &amp;nbsp;Can't say much about it aside from that. &amp;nbsp;Here are the others currently in my cabinet.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Laphroaig 18yo&lt;/b&gt;&lt;br /&gt;Very, very smokey. &amp;nbsp;In fact, that's about all I taste, but then, my palette is not yet very sophisticated. &amp;nbsp;Nonetheless, I really like this Scotch, and will keep it stocked. &amp;nbsp;I don't yet know whether it would pair well with a cigar; it might be too much smoke, but then again, maybe it'd draw out the other flavors. &amp;nbsp;Definitely needs water; it's 96 proof.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Balvenie, 15yo (cask 198, bottle 82)&lt;/b&gt;&lt;br /&gt;Currently, my favorite Scotch. &amp;nbsp;Very smooth, very fruity. &amp;nbsp;Almost -- very nearly, sweet. &amp;nbsp;Does not need water.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Tomatin 12yo&lt;/b&gt;&lt;br /&gt;This tastes like what I expect Scotch to taste like; that is to say, it isn't very distinctive to me. &amp;nbsp;Unfortunately, in the Army I once made myself extremely ill on a bunch of different alcohols, one of which was a whiskey that tasted very much like this Scotch, so I don't much like the taste. &amp;nbsp;I may try an older Tomatin before I give up on the brand, but I suspect that I won't be stocking this one. &amp;nbsp;Mind you: I'm sure it's a fine Scotch, and this is purely a matter of personal taste. &amp;nbsp;It is well served (for me) by adding water, despite being (only?) 86 proof.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Glenfiddich 18yo&lt;/b&gt;&lt;br /&gt;To my immature palette, this tastes identical to the Tomatin. &amp;nbsp;I have a map of Scotland, and where in it each distillery finds itself, and I strongly suspect that these two distilleries are in close proximity. &amp;nbsp;In any case, it'd take a more discerning palette than mine to tell the difference between these two, and (as I've said), I don't care for this particular taste.&lt;br /&gt;&lt;br /&gt;My next bottle will be a 21yo bottle of Balvenie, and I may purchase a step up on the Laphroaig as well. I also need to start learning to pronounce these names.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5297772973487593985-367232705902984775?l=blog.ser1.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ser1.net/feeds/367232705902984775/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5297772973487593985&amp;postID=367232705902984775' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/367232705902984775'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/367232705902984775'/><link rel='alternate' type='text/html' href='http://blog.ser1.net/2010/07/single-malt-scotches.html' title='Single Malt Scotches'/><author><name>SER</name><uri>http://www.blogger.com/profile/11674189468540726402</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5297772973487593985.post-1348687445959524521</id><published>2010-07-20T04:36:00.000-07:00</published><updated>2010-07-20T04:36:41.438-07:00</updated><title type='text'>American Manufacturing is dead to me</title><content type='html'>Ok, ok... I'm making this statement based on a single Japanese company. &amp;nbsp;It isn't fair. &amp;nbsp;Zojirushi (the Japanese company in question) makes peerless products, though. &amp;nbsp;They're well-designed and have outstanding production quality.&lt;br /&gt;&lt;br /&gt;On the recommendation of our friends Asher and Harumi, we bought a Zojirushi rice cooker a couple of years ago. &amp;nbsp;Mind you, I never realized what I'd be getting into when I went shopping for a rice cooker; Zojirushi themselves make, like, two dozen different models, and they aren't the only company in the business. &amp;nbsp; In any case, we're really happy with the rice cooker. &amp;nbsp;It's easy to use, easy to clean, and is full of features like a delayed start timer. &amp;nbsp;What I found I liked the most, though, was the build quality. &amp;nbsp;The plastic parts are robust, and it fits together nicely. &amp;nbsp;There are no loose, wiggling bits, and it isn't complicated or ambiguous in dis-or-re-assembly.&lt;br /&gt;&lt;br /&gt;The thing that prompted me to write this post, though, was that I just bought a Zojirushi travel mug. &amp;nbsp;Now, you'd think that purchasing a travel mug would be simplicity itself, but it isn't. &amp;nbsp;There are countless available, but most of the web sites don't tell you about the construction, and by the time I'd purchased my third one, I'd discovered that the American manufacturing industry's definition of "insulated" doesn't match my definition. &amp;nbsp;Every one of those mugs is double-walled aluminum, and none of them retain heat even over the course of my commute into work.&lt;br /&gt;&lt;br /&gt;As an aside: I know that these mugs are all, probably, produced in China. &amp;nbsp;I'm guessing that they're designed in the U.S., and even if they aren't, they're branded by U.S. company logos. &amp;nbsp;So take note: I don't give a hoot &lt;b&gt;who's&lt;/b&gt;&amp;nbsp;making the thing... you put your name on it, you're responsible for it.&lt;br /&gt;&lt;br /&gt;I eventually discovered that Zojirushi manufactures a line of mugs. &amp;nbsp;A bunch of them. &amp;nbsp;Again, they have about a dozen models, of various designs, sizes, and intended use. &amp;nbsp;I settled pretty quickly on the SM-DA series (there are two sizes sub-models, DA35 and DA50) because of their heat retention. &amp;nbsp;Now, here's the first way in which this Japanese company beats it's American counterparts: they &lt;i&gt;actually put heat retention metrics on their product page&lt;/i&gt;. &amp;nbsp;You know, the kind of information you'd want to know if you were purchasing something that was supposed to be retaining heat. &amp;nbsp;This model had other nice features, such as a locking, sealing lid. &amp;nbsp;It took a while to find a place (that wasn't in Japan) to purchase it -- I got mine from a "bargaincell" through buy.com -- and it took even longer for them to ship it, but it arrived yesterday.&lt;br /&gt;&lt;br /&gt;Here's where it gets good. &amp;nbsp;First, &amp;nbsp;nothing is in English except for the company name and product model. &amp;nbsp;There are a lot of cute pictures of Anime-style people burning their faces by doing naughty things (not &lt;i&gt;that&lt;/i&gt;&amp;nbsp;kind of naughty, you pervert) with the mug. &amp;nbsp;But, hell... it's a travel mug; how hard can it be?&lt;br /&gt;&lt;br /&gt;The construction is, again, amazing. &amp;nbsp;The cover is almost as heavy as the rest of the mug, which is amazing in two ways: the mug itself is super-light, and the lid is unusually heavy. &amp;nbsp;The plastics are, again, robust; the rubbers are soft and pliable, and it all goes together with that typical Zojiushi &lt;i&gt;solidness&lt;/i&gt;. &amp;nbsp;The locking and release mechanism are simple to operate, and the seal is perfect. &amp;nbsp;A perfect example of the quality is a very small thing: when the lid is folded back, it locks into place so it doesn't flop forward onto your face when you drink. &amp;nbsp;It is details like that, which I know from experience you don't get from American products&lt;b&gt;*&lt;/b&gt;, that make the difference.&lt;br /&gt;&lt;br /&gt;And the best thing is that I poured coffee into it about 45 minutes ago, and the canister and lid are&amp;nbsp;&lt;b&gt;still&lt;/b&gt;&amp;nbsp;cool to the touch, which means that there's been no (or very little) heat loss.&lt;br /&gt;&lt;br /&gt;It might just be that Zojirushi makes good products. &amp;nbsp;It might be that the Japanese, unlike Americans, are unwilling to settle for cheap, throw-away, crap. &amp;nbsp;I suspect that it's the latter; Japan is a wealthy country. &amp;nbsp;Regardless, I'll be looking first to Japanese products -- specifically, products primarily intended for the Japanese market -- for future purchases.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5297772973487593985-1348687445959524521?l=blog.ser1.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ser1.net/feeds/1348687445959524521/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5297772973487593985&amp;postID=1348687445959524521' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/1348687445959524521'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/1348687445959524521'/><link rel='alternate' type='text/html' href='http://blog.ser1.net/2010/07/american-manufacturing-is-dead-to-me.html' title='American Manufacturing is dead to me'/><author><name>SER</name><uri>http://www.blogger.com/profile/11674189468540726402</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5297772973487593985.post-7301955510581667842</id><published>2010-06-17T19:32:00.000-07:00</published><updated>2010-06-17T19:32:48.754-07:00</updated><title type='text'>Erlang vs. Haskell</title><content type='html'>Well, I'm on the Erlang train, now. &amp;nbsp;It's really hard to resist some of the platform's features; in particular:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;The dead-simple threading and message passing&lt;/li&gt;&lt;li&gt;The syntactic sugar for binary matching&lt;/li&gt;&lt;li&gt;The VM, which provides support for dynamically modifying code at runtime&lt;/li&gt;&lt;li&gt;All of the support for supervisors, which provides massive reliability with minimal effort&lt;/li&gt;&lt;/ul&gt;What I &lt;b&gt;don't&lt;/b&gt;&amp;nbsp;like is the syntax. &amp;nbsp;Erlang is a horrible, horrible language when it comes to syntax. &amp;nbsp;It's verbose, and awkward, especially when coming from a language like Haskell. &amp;nbsp; Take, for example, the following code, which finds the longest prefix that is also a suffix in a string (where prefix is defined as any sequence of characters including the first but excluding the last, and suffix as any sequence of characters including the last but excluding the first)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;import List as D&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;longest arry = head $ D.sortBy&amp;nbsp;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;(\x y -&amp;gt; compare (length y) (length x)) (matches arry)&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;matches x@(a:as) = D.intersect (substrs x) (map reverse (substrs (reverse as)))&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;where substrs xs = [ take n xs | n &amp;lt;- [1..length xs]]&lt;/span&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Pretty simple, eh? &amp;nbsp;Here's the exact same algorithm in Erlang:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;-module(presuf).&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;-export([longest/1, max/1]).&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;max(Arry) -&amp;gt; length(longest(Arry)).&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;longest(Arry) -&amp;gt; head(lists:sort(fun(A,B) -&amp;gt; length(A) &amp;gt; length(B) end, matches(Arry))).&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;head([H|_]) -&amp;gt; H;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;head([]) -&amp;gt; [].&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;matches(Xs) -&amp;gt;&amp;nbsp;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;Pres = substrs(1,Xs),&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;Sufs = lists:map( fun lists:reverse/1, substrs( 1, lists:reverse(Xs) ) ),&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;sets:to_list( sets:intersection( sets:from_list(Pres), sets:from_list(Sufs) ) ).&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;substrs( N, Xs ) when N &amp;lt; length(Xs) -&amp;gt; [lists:sublist( Xs, N )|substrs(N+1, Xs)];&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;substrs( _, _ ) -&amp;gt; [].&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Pretty ugly. &amp;nbsp;Despite this, the other platform features are, as I've said, irresistible, so I'm trying to wrap my head around the stupid syntax, crossing my fingers, and hoping for the best.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5297772973487593985-7301955510581667842?l=blog.ser1.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ser1.net/feeds/7301955510581667842/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5297772973487593985&amp;postID=7301955510581667842' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/7301955510581667842'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/7301955510581667842'/><link rel='alternate' type='text/html' href='http://blog.ser1.net/2010/06/erlang-vs-haskell.html' title='Erlang vs. Haskell'/><author><name>SER</name><uri>http://www.blogger.com/profile/11674189468540726402</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5297772973487593985.post-6107014226028146439</id><published>2010-04-06T05:13:00.000-07:00</published><updated>2010-04-06T05:13:11.360-07:00</updated><title type='text'>My Adventure with OSX</title><content type='html'>I recently bought a Mac. &amp;nbsp;I've had it now for a couple of months, and my opinions about it have stabilized enough for an essay on the topic. &amp;nbsp;The summary: it was a mistake.&lt;br /&gt;&lt;br /&gt;It isn't my first Mac. &amp;nbsp; I've owned, used, and programmed on several different OSes in my life: Apple ][s, MVS, Amiga OS, &amp;nbsp;NeXTSTEP, Windows, MacOS 9, and Linux.&lt;br /&gt;&lt;br /&gt;Now, the Apple ][ OS was outstanding for the fact that, with a little experience, you could fully understand all parts of the system. &amp;nbsp;DOS sucked, and still does. &amp;nbsp;Amiga OS was OK, but while I used it for several years, it didn't have any particularly outstanding characteristics -- the thing that made the Amiga great was that it was really the first microcomputer to have every subcomponent as a separate processor. &amp;nbsp;It had a CPU, a GPU, and sound chip... that's pretty common today, but Amiga pioneered that.&lt;br /&gt;&lt;br /&gt;NeXTSTEP was the first truly great OS. &amp;nbsp;It was what OSX tries to be: cohesive, integrated, simple, and powerful. &amp;nbsp;Programming in NeXTSTEP was a joy. &amp;nbsp;It clearly separated the view from the model/control through a drag/drop GUI builder that built &lt;i&gt;resources&lt;/i&gt;, which you then loaded and hooked in to. &amp;nbsp;Again, this (to my knowledge) was has generally been ignored by other technologies (Java, in particular, although Gnome does this right, and yes, I'm aware one's a language and the other's a GUI toolkit. &amp;nbsp;Java is inseparable from its own GUI toolkit, though, and a platform as much as NeXTSTEP was, so I think it's a fair comparison). &amp;nbsp;Steve Jobs killed NeXTSTEP through incompetent marketting -- he focused entirely on the business market and ignored the personal computer market, and if Steve had been less dense, NeXT &lt;b&gt;could&lt;/b&gt;&amp;nbsp;have killed Windows right then and there -- it was that much better.&lt;br /&gt;&lt;br /&gt;For the past thirteen years, we've been a Linux house. &amp;nbsp;For the past ten years, we've been a laptop house; both my wife and I use Linux -- for most of that time, running KDE. &amp;nbsp;However, I've become increasingly pissed off with Linux. &amp;nbsp;In the past two or three years, the kernel has been unstable and unreliable. &amp;nbsp;Every release of Ubuntu should be called "Rampant Regressions," and even the Gentoo server (my old tower desktop, now full of disk and server processes) is a pain to upgrade because of a lack of clear migration paths for so much of the software that's running on it -- although, I don't have any problems with stability of the overall system. &amp;nbsp;KDE4 was a nightmare -- if ever a piece of software should have been witheld for quality control, it was KDE4. &amp;nbsp;It was so bad, we had switched to Gnome, which deserves its own rant, but at least it was stable.&lt;br /&gt;&lt;br /&gt;I'd been looking for a new laptop to replace my aged TC4200, and was about to buy an Acer Timeline to match my wife's, which I got her for Christmas and which has been an outstanding little computer. &amp;nbsp;However, I was seduced by the siren call of the MacBook Pro, which is an extremely attractive package. &amp;nbsp;I went for the 15", and that was my first mistake.&lt;br /&gt;&lt;br /&gt;The 15" MacBook Pro doesn't fit in anything. &amp;nbsp;Tumi doesn't make much that it'll fit into (and I only buy Tumi), and while thin, it is simply too cumbersome to carry around. &amp;nbsp;I'd probably have been happier with a 13", but the 15" was definitely a mistake. &amp;nbsp;Comparatively, my wife's 14" Acer Aspire Timeline 4810T has a slightly smaller screen, but it fits in everything and is noticeably more portable.&lt;br /&gt;&lt;br /&gt;There are several nice things about the MacBook:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;It starts up really quickly. &amp;nbsp;That login prompt is there in a dozen seconds; Linux can take up to a minute to start.&lt;/li&gt;&lt;li&gt;It resumes from a sleep super fast. &amp;nbsp;I mean, super fast. &amp;nbsp;Like, suspiciously fast. &amp;nbsp;Like, moving your mouse to un-blank your screen fast.&lt;/li&gt;&lt;li&gt;I've never had a problem with the wifi. &amp;nbsp;Computer wakes up, computer connects to the internet.&lt;/li&gt;&lt;li&gt;The hardware itself is pretty. &amp;nbsp;I like the backlit keyboard, and the control keys for brightness, media control, and so on all work.&lt;/li&gt;&lt;li&gt;It is pretty dead-quiet, and doesn't get very hot.&lt;/li&gt;&lt;li&gt;I like some of the software on it. &amp;nbsp;iBank is pretty nice, and it is a relief to finally have software support for some hardware, such as the Logitech Harmony remotes.&lt;/li&gt;&lt;/ul&gt;I have had quite a few problems with it, though:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;The first laptop I got had a busted DVD-ROM. &amp;nbsp;The Apple store replaced it, but I got the distinct impression that had my laptop been more than a day or two old, they'd have given me more trouble about it.&lt;/li&gt;&lt;li&gt;I've had the video camera lock up, such that the cam software isn't able to acquire an image. &amp;nbsp;The only fix for this is a reboot.&lt;/li&gt;&lt;li&gt;I've had the bluetooth stop working, and the only fix for this is to reboot. &amp;nbsp;I mean, seriously: reboot?? &amp;nbsp;What is this, Windows?&lt;/li&gt;&lt;li&gt;I&amp;nbsp;hate the OSX key bindings. &amp;nbsp;Who the hell&amp;nbsp;at Apple decided to ignore the defacto standards and change the bindings? &amp;nbsp;CTRL-C/V/X doesn't copy/paste/cut -- no, you have to use &amp;nbsp;Apple-C/V/X keys. &amp;nbsp;Want to select a work in your editor with the keyboard? &amp;nbsp;Is it CTRL-Shift-LeftArrow? &amp;nbsp;No, no... that'd be too standard. &amp;nbsp;On an Apple, it's Option-Shift-LeftArrow. &amp;nbsp;What we need is a YouTube video of somebody repeatedly kicking the idiot who made the decision to have the OSX key bindings be&amp;nbsp;different&amp;nbsp;from every other fucking OS in the world in the nuts. &amp;nbsp;Repeatedly. &amp;nbsp;For an hour.&lt;/li&gt;&lt;li&gt;The mouse pad is stupid. &amp;nbsp;Apple really needs to stop being idiots and finally put a second mouse button on their mouse pad. &amp;nbsp;Seriously. &amp;nbsp;Having to CTRL-Click to get the secondary functions is user-hostile, not user-friendly, you idiots.&lt;/li&gt;&lt;li&gt;There's no &lt;a href="http://steve-yegge.blogspot.com/2008/04/settling-osx-focus-follows-mouse-debate.html"&gt;focus-follows-mouse&lt;/a&gt;. &amp;nbsp;That's the second-most stupid thing about OSX (right after the key bindings).&lt;/li&gt;&lt;li&gt;All of the photo-editing software is expensive. &amp;nbsp;Actually, all software for OSX is expensive, and I don't see much that's better than the free software equivalents on Linux. &amp;nbsp;I don't think that Aperture is any easier to use than &lt;a href="http://bibblelabs.com/"&gt;Bibble&lt;/a&gt;, and actually feels less powerful (and Bibble is available on Linux, too). &amp;nbsp;It's better for managing photos than the Gimp, but Gimp is more powerful. &amp;nbsp;And if you start looking at the Photoshop line of software, you're looking at serious money.&lt;/li&gt;&lt;/ul&gt;The battery life isn't &lt;i&gt;quite&lt;/i&gt;&amp;nbsp;as advertised. &amp;nbsp;In fact, I get significantly less, and it isn't like I'm doing anything more than just browsing the web, most of the time.&lt;br /&gt;&lt;br /&gt;The MacBook is nice hardware, although, as I've said, the 15" is awkward, and was a mistake. &amp;nbsp;The 13" might have been OK. &amp;nbsp;But, for less than half the price, you could get an Acer Timeline (or any of a number of similar laptops available today), with similar battery life, as good a screen, as much memory, albeit less CPU (most laptops this use scaled down CPUs; the MacBook has a pretty hefty one).&lt;br /&gt;&lt;br /&gt;I won't be buying another Mac; in fact, I may be installing Linux on this one. &amp;nbsp;At this point, I don't think OSX really offers any advantage -- and has several disadvantages -- to Linux. &amp;nbsp;I don't think it is any easier to use, although it may be easier to maintain.&lt;br /&gt;&lt;br /&gt;Apple is doing well, but I suspect that it's because they're successfully pulling off what Microsoft got away with in the 80's and 90's: they're suckering people into buying overly expensive products that are actually technically inferior to less-well-advertised alternatives. &amp;nbsp;And I've been one of those suckers.&lt;br /&gt;&lt;br /&gt;Fool me once, shame on me. &amp;nbsp;You won't fool me twice, Apple.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5297772973487593985-6107014226028146439?l=blog.ser1.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ser1.net/feeds/6107014226028146439/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5297772973487593985&amp;postID=6107014226028146439' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/6107014226028146439'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/6107014226028146439'/><link rel='alternate' type='text/html' href='http://blog.ser1.net/2010/04/my-adventure-with-osx.html' title='My Adventure with OSX'/><author><name>SER</name><uri>http://www.blogger.com/profile/11674189468540726402</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5297772973487593985.post-7536162695953766507</id><published>2010-03-29T04:33:00.000-07:00</published><updated>2010-03-29T04:33:05.833-07:00</updated><title type='text'>Dear Google: Stop Reminding Me About Chrome</title><content type='html'>I get these pop-ups on Google's web search page telling me to install Chrome (when I'm not using Chrome), because it's a faster way to search the web. &amp;nbsp;Do you get them?&lt;br /&gt;&lt;br /&gt;Well, dear Google, the reason why I'm &lt;b&gt;not&lt;/b&gt;&amp;nbsp;using Chrome at the moment is because your browser doesn't support Java on OSX, and there are several web sites that I use that rely on Java. &amp;nbsp;Like it or not, Java is a standard; there are a lot of things you can't do with Javascript, and IMO, Java is a lesser evil when compared with the alternative of ActiveX. &amp;nbsp;I'd have thought that that smart people like you would have had reservations about recommending a browser that lacks support for some pretty basic web technology in such a public manner.&lt;br /&gt;&lt;br /&gt;Sincerely,&lt;br /&gt;&lt;br /&gt;Somebody Who &lt;b&gt;Wants&lt;/b&gt;&amp;nbsp;To Use Chrome, But Can't.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5297772973487593985-7536162695953766507?l=blog.ser1.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ser1.net/feeds/7536162695953766507/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5297772973487593985&amp;postID=7536162695953766507' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/7536162695953766507'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/7536162695953766507'/><link rel='alternate' type='text/html' href='http://blog.ser1.net/2010/03/dear-google-stop-reminding-me-about.html' title='Dear Google: Stop Reminding Me About Chrome'/><author><name>SER</name><uri>http://www.blogger.com/profile/11674189468540726402</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5297772973487593985.post-1443828554810171407</id><published>2010-03-13T07:51:00.000-08:00</published><updated>2010-03-13T07:51:04.305-08:00</updated><title type='text'>Canonical's Misrepresentation of Support (Ubuntu)</title><content type='html'>I've noticed something disturbing about the Ubuntu distribution. &amp;nbsp;Mind you, Ubuntu is free software, so I don't expect much from it. &amp;nbsp;However, I find this behavior disturbing. &amp;nbsp;I'd prefer if they simply didn't pretend to offer support, rather than what they &lt;b&gt;do&lt;/b&gt;&amp;nbsp;do, which is waste everybody's time.&lt;br /&gt;&lt;br /&gt;Here's &lt;a href="https://bugs.launchpad.net/bugs/254458"&gt;an example&lt;/a&gt;, and here's how it goes:&lt;br /&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;User reports a bug. &amp;nbsp;Often, user spends a lot of time actually looking into the problem, and provides dmesg dumps, lspci dumps, and so on.&lt;/li&gt;&lt;li&gt;Ubuntu team responds that this still isn't enough information, and requests increasingly obscure data&lt;/li&gt;&lt;li&gt;User provides the data. &amp;nbsp;In the meantime, several other people post to the same bug, reporting the same behavior, and provide their own, additional, data&lt;/li&gt;&lt;li&gt;Ubuntu team recommends upgrading to some unsupported, alpha release, despite the fact that the bug was reported against an LTS (Long Term Support == "Stable") release, and the alpha release page includes warnings about not using it in a production environment&lt;/li&gt;&lt;li&gt;User is unwilling to upgrade to an unstable release&lt;/li&gt;&lt;li&gt;Ubuntu team closes the bug as invalid due to "not enough information."&lt;/li&gt;&lt;/ol&gt;&lt;div&gt;Basically, the tactic is to make unreasonable demands of the bug submitter, and then close the bug by insinuating that the user is either (a) hallucinating, or (b) lazy and stupid. &amp;nbsp;Look through the Ubuntu bug submissions, and a fair percentage of them follow this pattern. &amp;nbsp;My own personal experience is that about 80% of Ubuntu bugs are handled this way (and, usually, I'm not the original submitter, but somebody who's experiencing the bug and has put it on their "watch" list). &amp;nbsp;Very, very few bugs are actually closed with a satisfactory resolution by the submitter.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Back to my original comment: nobody buys Ubuntu. &amp;nbsp;It's free, and it's stupid and selfish to get mad when you don't get support for something that's free. &amp;nbsp;However, it's also stupid, and insensitive as well, to pretend to provide support when, really, you're just jacking off and wasting people's time. &amp;nbsp;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5297772973487593985-1443828554810171407?l=blog.ser1.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ser1.net/feeds/1443828554810171407/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5297772973487593985&amp;postID=1443828554810171407' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/1443828554810171407'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/1443828554810171407'/><link rel='alternate' type='text/html' href='http://blog.ser1.net/2010/03/canonicals-misrepresentation-of-support.html' title='Canonical&apos;s Misrepresentation of Support (Ubuntu)'/><author><name>SER</name><uri>http://www.blogger.com/profile/11674189468540726402</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5297772973487593985.post-7852385313591177882</id><published>2010-03-03T18:20:00.000-08:00</published><updated>2010-03-03T18:20:42.239-08:00</updated><title type='text'>I Give Up.  I'm Buying A Mac</title><content type='html'>I can't resist any longer.&amp;nbsp; I'm going to give OSX a chance to piss me off for a while now.&lt;br /&gt;&lt;br /&gt;It actually isn't my first Mac.&amp;nbsp; At one time, I owned three Mac clones, running OS9.&amp;nbsp; Boy, that sucked.&amp;nbsp; I ended up with them mostly by accident; I bought one, and then got two given to me over the next year by people who hated OS9 as much as I did, but had more good sense to get rid of it much sooner.&lt;br /&gt;&lt;br /&gt;Anyhoo, I can't resist the seductive siren call of the Macbook Pros.&amp;nbsp; 15" screens, 0.9" thick, and 7 hours of battery life?&amp;nbsp; Sign me up.&amp;nbsp; If I really loath OSX, I can always use BootCamp to install Linux on it, although OSX would have to suck quite a bit to make up for the amount that Linux is annoying me right now.&lt;br /&gt;&lt;br /&gt;Oh, and the 7 hour battery life is no joke.&amp;nbsp; AnandTech did &lt;a href="http://www.anandtech.com/mac/showdoc.aspx?i=3580"&gt;some tests&lt;/a&gt; on how long the 15" version runs, and they came up with 8 hours with light use: screen on 50%, loading web pages every 20s (so wireless was on), and playing MP3s the whole time.&amp;nbsp; That's pretty damned impressive.&lt;br /&gt;&lt;br /&gt;Plus, if I actually go insane and buy an &lt;a href="http://www.eigenlabs.com/tau/"&gt;Eigenharp&lt;/a&gt;, I &lt;i&gt;have&lt;/i&gt; to have a Mac... they don't support anything else.&lt;br /&gt;&lt;br /&gt;I know, I know; Apple is as evil, if not more, than Microsoft, but at least they don't also make shitty products, which Microsoft &lt;i&gt;does&lt;/i&gt; do.&lt;br /&gt;&lt;br /&gt;And there's my P.S.: I finally found a company that consistently makes worse products than Microsoft.&amp;nbsp; A company that churns out steaming piles of stinky code more reliably than the Big Disease: Rational.&amp;nbsp; I'm serious; I have yet to encounter a software product from Rational that wasn't indistinguishable from vomit.&amp;nbsp; It's poorly designed, even more poorly implemented, and if it were free, it'd be overpriced.&amp;nbsp; It makes me really nervous when people who supposedly are software experts -- because they do it for a living -- think that anything that comes from Rational is worth the CPU cycles, much less actual &lt;i&gt;money&lt;/i&gt;.&amp;nbsp; At least Microsoft has Excel, which isn't too bad.&amp;nbsp; Rational just has overpriced consultants who, by the way, can't actually solve any of your problems.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5297772973487593985-7852385313591177882?l=blog.ser1.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ser1.net/feeds/7852385313591177882/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5297772973487593985&amp;postID=7852385313591177882' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/7852385313591177882'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/7852385313591177882'/><link rel='alternate' type='text/html' href='http://blog.ser1.net/2010/03/i-give-up-im-buying-mac.html' title='I Give Up.  I&apos;m Buying A Mac'/><author><name>SER</name><uri>http://www.blogger.com/profile/11674189468540726402</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5297772973487593985.post-7759405910710212557</id><published>2010-02-13T05:56:00.000-08:00</published><updated>2010-02-13T06:00:45.524-08:00</updated><title type='text'>Sweeteners</title><content type='html'>We've been on the South Beach diet for nearly four weeks now, and it's working.&amp;nbsp; Painfully slowly, but the scale is trending steadily downwards.&amp;nbsp; We've both lost 16 lbs so far, and it hasn't sucked too much.&amp;nbsp; Most of the time, we're not starving.&lt;br /&gt;&lt;br /&gt;One note on the book: I know that they do it because it's "motivational," but I find the case studies in the book to be ludicrous. They're all, "I don't even &lt;i&gt;look&lt;/i&gt; at ice cream in the store anymore."&amp;nbsp; Yeah, that's technically true, but it's misleading; you &lt;i&gt;don't&lt;/i&gt; look, because it's so fucking painful to not be able to have that stuff.&amp;nbsp; Chips isle?&amp;nbsp; Big no-no.&amp;nbsp; I come out the other end crying.&amp;nbsp; Bakery? I hear their voices, in my head, even from home.&lt;br /&gt;&lt;br /&gt;By far the worst, though, is the beer.&amp;nbsp; Beer, to the South Beach dieter, is Satan.&amp;nbsp; Apparently, if you even look at beer, you'll gain weight.&amp;nbsp; The theory is sound: beer is 100% carbs, with nothing redeeming in it to stop your body from turning it directly into fat.&amp;nbsp; Personally, I think they're ignoring the fact that beer, like a person, also contains a &lt;i&gt;soul&lt;/i&gt;, which is not measurable and is the source of everything good and right in the world.&amp;nbsp; The inability to drink beer on this diet causes me actual, physical, pain.&amp;nbsp; You know Munch's &lt;i&gt;The Scream&lt;/i&gt;?&amp;nbsp; That's me.&lt;br /&gt;&lt;br /&gt;Anyway, to the point of this post: sugar substitutes.&amp;nbsp; We've been through most of them over the past month, and I'm here to tell you:&amp;nbsp; nothing is as good as sugar.&amp;nbsp; Anybody who tells you that there &lt;b&gt;is&lt;/b&gt; something as good as sugar is delusional (and you have to be, a little bit, to make diets like this bearable).&amp;nbsp; Rather than lie to yourself, have a little backbone, and man up.&amp;nbsp; Nothing is as good as sugar, but we did find something that's pretty darn close.&amp;nbsp; I haven't found any substitute for honey, which means that I don't drink tea any more.&amp;nbsp; That's sad, but compared to the other inconveniences of this diet (no baguette &lt;i&gt;&lt;sob&gt;&lt;/sob&gt;&lt;/i&gt;), it ranks pretty far down on my complaints.&lt;br /&gt;&lt;ul&gt;&lt;li&gt; &lt;b&gt;Stevia&lt;/b&gt;.&amp;nbsp; Ok, I really don't see how anybody can count this as a "sweetener."&amp;nbsp; The fact that it has a sweet-like taste is overwhelmed by the fact that it is also overwhelmingly nasty.&lt;/li&gt;&lt;li&gt;&lt;b&gt;Splenda&lt;/b&gt;. I actually don't mind Splenda.&amp;nbsp; It doesn't fool me, but it's an adequate substitute in a pinch.&amp;nbsp; Monika really dislikes it, though, and claims that it has that oddly common "sugar substitute taste."&amp;nbsp; I don't notice the aftertaste too much.&lt;/li&gt;&lt;li&gt;&lt;b&gt;Agave&lt;/b&gt;. Ok, now, I could see how eating an Agave plant might be interesting, but as a sugar substitute... yuck.&amp;nbsp; It doesn't have the aftertaste, but it certainly has it's own peculiar quality that completely distracts from whatever it is that you're trying to sweeten.&amp;nbsp; I mean, when I eat or drink something sweetened with Agave, what I taste is Agave.&amp;nbsp; Cookies taste like Agave.&amp;nbsp; Coffee tastes like Agave juice.&amp;nbsp; If you like eating Agave, then this is probably fine, but in my opinion, it defeats the purpose of the thing.&lt;/li&gt;&lt;li&gt;&lt;b&gt;Aspartame&lt;/b&gt;. Yeah, this is the reason why there are so many other alternative sugar substitutes.&amp;nbsp; It tastes like you're sweetening your food with cancer.&amp;nbsp; I just can't get past the &lt;i&gt;industrial&lt;/i&gt; taste.&lt;/li&gt;&lt;li&gt;&lt;b&gt;Swerve&lt;/b&gt;.&amp;nbsp; And now we come to the pay-out.&amp;nbsp; This is an amazing product. The only problem with it is that it is outrageously expensive, and you can get it from only one source.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;I discovered Swerve in the first week of the diet, while desperately looking for &lt;i&gt;something&lt;/i&gt; that we could sweeten the South Beach desert recipes with (they only call for "sugar substitute" in the recipes) that we could stand.&amp;nbsp; This stuff is, frankly, amazing.&amp;nbsp; Like most sugar substitutes, the sweet taste hits your tongue at different places than sugar, but there's no real aftertaste to speak of.&amp;nbsp; The oddest part of this sweetener is that it has an ethereal quality, almost like a &lt;i&gt;smell&lt;/i&gt; of sweet, that lingers.&amp;nbsp; It is hard to explain; however, even if we weren't on the South Beach diet, I could easily replace sugar with Swerve in my diet, and thereby reduce my daily glycemic index significantly... except for the price.&lt;br /&gt;&lt;br /&gt;As I mentioned before, the only downside to this product is the cost.&amp;nbsp; A pound is $17.&amp;nbsp; Seventeen dollars.&amp;nbsp; A five pound bag of sugar is, like, $2.50.&amp;nbsp; That makes this Swerve about 35 times as expensive as sugar.&amp;nbsp; And since one of Swerve's claims to fame is that it is a 1-to-1 substitute for sugar (1tbsp Swerve = 1tbsp sugar), that's pretty horrible.&amp;nbsp; I tell you now, Swerve: those prices aren't sustainable.&amp;nbsp; As we near our weight goals, we're going to re-introduce sugar, not because you're not good enough, but because you're so damned expensive.&lt;br /&gt;&lt;br /&gt;I won't do too much of a sell-job on Swerve.&amp;nbsp; I'm pretty much a convert, though, and highly recommend to anybody wanting to reduce the number of carbs they're intaking to give it a try.&amp;nbsp; I'm guessing that cutting sugar out of your diet would make a pretty significant dent in your weight gain, or give a boost to your weight loss, or if you're hypoglycemic, or diabetic, or any of a number of other reasons.&lt;br /&gt;&lt;br /&gt;As long as you can afford it.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5297772973487593985-7759405910710212557?l=blog.ser1.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ser1.net/feeds/7759405910710212557/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5297772973487593985&amp;postID=7759405910710212557' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/7759405910710212557'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/7759405910710212557'/><link rel='alternate' type='text/html' href='http://blog.ser1.net/2010/02/sweeteners.html' title='Sweeteners'/><author><name>SER</name><uri>http://www.blogger.com/profile/11674189468540726402</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5297772973487593985.post-5763483848260973611</id><published>2010-01-31T10:09:00.000-08:00</published><updated>2010-01-31T10:10:18.664-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='issue tracking'/><title type='text'>The Intersection of Distributed Bug Trackers and End Users</title><content type='html'>&lt;span class="Apple-style-span" style="font-family: arial; font-size: small;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;div&gt;Lately (the past year or so) I've been using a tool called &lt;a href="http://ditz.rubyforge.org/"&gt;Ditz&lt;/a&gt; to manage my software issues. &amp;nbsp;Ditz is a distributed issue tracking system; it stores issues locally in a format that is easy for version control systems to efficiently track. &amp;nbsp;There are some interesting theories about these sorts of trackers (of which Ditz is but one); for one thing, issues can be branched and merged, which means that each branch can have a different state for the same issue -- a feature that most issue trackers lack. &amp;nbsp;But it is also much easier for developers to use; there's no network traffic, so it can be used offline, and much more quickly than an online tracker. &amp;nbsp;And you don't have to use a web interface.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;However, these offline trackers have a disadvantage: they're offline. &amp;nbsp;Ditz comes with a web interface, but it is static. &amp;nbsp;There's a dynamic interface (called Sheila), but it's a pain to set up and doesn't have a lot of features. &amp;nbsp;This makes it difficult to elicit bug reports from users.&lt;br /&gt;&lt;br /&gt;On the other hand, you have &lt;a href="http://trac.edgewall.org/"&gt;Trac&lt;/a&gt; (and &lt;a href="http://www.redmine.org/projects/redmine/wiki"&gt;Redmine&lt;/a&gt;) which are outstanding issue trackers. &amp;nbsp;&lt;i&gt;Their&lt;/i&gt;&amp;nbsp;only limitation is that they're entirely online, and I find them awkward -- as a developer -- to use.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;So, I wrote a &lt;a href="http://trac.germane-software.com/ditz-trac"&gt;Ditz plugin, called ditz-trac&lt;/a&gt;,&amp;nbsp;to sync Ditz with Trac. &amp;nbsp;Admit it... you're awed by my creative naming scheme. &amp;nbsp;In any case, it works fairly well, but the experience left me with some reservations, and ruminations.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I'm considering whether some sort of direct offline Trac tool might be lower cost for me. &amp;nbsp;I like ditz just fine, but I'm not sure that I need my issue tracker to be version controllable -- I just need it to be off-line-able, and have a separate DB per project. &amp;nbsp;The version-controllable nature of Ditz is inspiring, but I haven't used it yet (I don't branch a lot), and most projects get by without the feature. &amp;nbsp;In fact, I'm increasingly convinced from what I've seen that software developers have no more -- and possibly less -- than a 50% stake in issue management; users have the other solid 50%. &amp;nbsp;As for ease-of-use, ditz-like trackers have a split more like 95/5; web-based trackers split it more 80/20. &amp;nbsp;The more I think about it, the more I believe that a solid web tracker which can be used off-line and sync'ed is the ideal design.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;My experience with ditz-trac has shown me that the XMLRPC interface to Trac simply isn't sufficient for a robust off-line sync'ing. &amp;nbsp;It imposes too many limitations on the event log. &amp;nbsp;What I really need is direct write access to the Trac DB, so that I can manage the events with finer resolution. &amp;nbsp;If I go down that path, though, then one wonders whether it might not be better to simply copy the sqlite DB, rather than trying to shoe-horn the two differing schemas (Trac/ditz) together.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;I'm not sure what the best solution is, here. &amp;nbsp;The branch-ability of Ditz creates obstacles for synchronization, and there are always going to be troubles while the web tracker doesn't support the concept of hash IDs (all that I've seen rely on sequential integer DB IDs, which play hell with implementing writeable clones). &amp;nbsp;To really get a good sync solution, I'd have to modify the Trac or Redmine DB, and now the solution becomes more complex and difficult for users to install.&lt;br /&gt;&lt;br /&gt;I need to think about this some more, obviously. &amp;nbsp;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5297772973487593985-5763483848260973611?l=blog.ser1.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ser1.net/feeds/5763483848260973611/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5297772973487593985&amp;postID=5763483848260973611' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/5763483848260973611'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/5763483848260973611'/><link rel='alternate' type='text/html' href='http://blog.ser1.net/2010/01/lately-past-year-or-so-ive-been-using.html' title='The Intersection of Distributed Bug Trackers and End Users'/><author><name>SER</name><uri>http://www.blogger.com/profile/11674189468540726402</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5297772973487593985.post-2971173508453947776</id><published>2009-12-31T09:15:00.000-08:00</published><updated>2009-12-31T09:15:18.079-08:00</updated><title type='text'>Gobo is made of Fail</title><content type='html'>I spent a day messing with Gobo, and X is still messed up. &amp;nbsp;Plus, Gobo seems to have even worse problems figuring out dependencies than other distros. &amp;nbsp;Here's my experience, in a nutshell:&lt;br /&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Look for some commo package using FindPackage (ssh, Xorg, etc). &amp;nbsp;Wait while, every &lt;b&gt;single&lt;/b&gt;&amp;nbsp;time, it tries to sync with some server (caching is, obviously, broken).&lt;/li&gt;&lt;li&gt;Be unable to tell whether the package is a available as a binary, but at least we have a name.&lt;/li&gt;&lt;li&gt;Try to install some common package (InstallPackage). &amp;nbsp;After another annoyingly long wait, InstallPackage fails to find a binary.&lt;/li&gt;&lt;li&gt;Compile the recipe from FindPackage.&lt;/li&gt;&lt;li&gt;The config stage, invariably, fails because it can't find some dependency.&lt;/li&gt;&lt;li&gt;Repeat from (1) for the dependency.&lt;/li&gt;&lt;li&gt;When finished recursing for the dependency missing in (5), try to compile the original package again.&lt;/li&gt;&lt;li&gt;Compile will fail, again, because -- for some unfathomable reason -- Gobo is too stupid to add the package you just installed to the pkgconfig chain.&lt;/li&gt;&lt;li&gt;Manually set PKG_CONFIG_PATH and try to compile again.&lt;/li&gt;&lt;li&gt;Repeat from (5), failing on some &lt;b&gt;other&lt;/b&gt;&amp;nbsp;dependency&lt;/li&gt;&lt;li&gt;And, remember, at every step, baby the process along because the Gobo tools are extremely interactive, and will always require at least one human query/response.&lt;/li&gt;&lt;/ol&gt;This is worse, even, than RPM, where at least you didn't have to go through the entire autobuild process to discover the recursive dependencies. &lt;br /&gt;&lt;br /&gt;This renders Gobo entirely useless. &amp;nbsp;I finally gave up when xorg-server&amp;nbsp;needed&amp;nbsp;&lt;b&gt;specific&lt;/b&gt;&amp;nbsp;versions of other packages that, apparently, weren't installed. &amp;nbsp;I'm not even sure where it was getting the wrong versions of the packages. &amp;nbsp;Furthermore, xrandr was utterly useless, having either been compiled without most of the functionality, or being some ancient version that didn't have the options. &amp;nbsp;I'm not sure which is the case. &amp;nbsp;I had been working with Gobo 014, which was supposed to be the most current, but the version of xrandr that was installed was some three-year-old 1.1 version which was unable to detect my external monitor correctly (xrandr 1.2, which has been out for a couple of years, has no such problem).&lt;br /&gt;&lt;br /&gt;Other niggles:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;SSH isn't installed off the CD. &amp;nbsp;WTF?&lt;/li&gt;&lt;li&gt;X doesn't work with the install off the CD.&lt;/li&gt;&lt;li&gt;There are almost &lt;b&gt;no&lt;/b&gt;&amp;nbsp;binary packages. &amp;nbsp;Everything is built through Compile, which is too stupid to resolve dependencies by itself (yah, this is my main complaint)&lt;/li&gt;&lt;li&gt;Half of the default mirrors are broken, or down. &amp;nbsp;It is &lt;b&gt;extremely&lt;/b&gt;&amp;nbsp;common to wait while Gobo tries to contact two or three mirrors and times out before finding a useful mirror. &amp;nbsp;This makes all Compiles even more painfully slow.&lt;/li&gt;&lt;li&gt;The recipes, apparently, don't refer to cached tarballs, and I encountered several recipes that referred to tarballs on third party servers which simply didn't exist any more. &amp;nbsp;For example, the unionfs recipe is utterly broken. &amp;nbsp;Which is exceptionally annoying as InstallPackage keeps complaining about unionfs not being available -- and you can't install it because of the missing tarball: catch-22.&lt;/li&gt;&lt;li&gt;The Gobo folk are focusing on the &lt;b&gt;wrong&lt;/b&gt;&amp;nbsp;thing. &amp;nbsp;All of the emphasis is on the new filesystem layout. &amp;nbsp;I don't object to the layout; it is an interesting experiment. &amp;nbsp;But the real innovation is a package manager which can have multiple versions of software installed on the same system. &amp;nbsp;If it worked, which it doesn't.&lt;/li&gt;&lt;/ul&gt;So, sadly, I'm downloading Ubuntu 9.10, because as much as it sucks, as flaky as new releases are, as many regressions as every upgrade introduces... at least you can get a working Xorg out of it.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5297772973487593985-2971173508453947776?l=blog.ser1.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ser1.net/feeds/2971173508453947776/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5297772973487593985&amp;postID=2971173508453947776' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/2971173508453947776'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/2971173508453947776'/><link rel='alternate' type='text/html' href='http://blog.ser1.net/2009/12/gobo-is-made-of-fail.html' title='Gobo is made of Fail'/><author><name>SER</name><uri>http://www.blogger.com/profile/11674189468540726402</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5297772973487593985.post-7977980232738689837</id><published>2009-12-29T05:46:00.000-08:00</published><updated>2009-12-29T05:46:42.342-08:00</updated><title type='text'>sup... again!</title><content type='html'>Argh! &amp;nbsp;Aaaaargh!&lt;br /&gt;&lt;br /&gt;So, I'm totally addicted to the gmail mail management workflow. &amp;nbsp;I admit it. &amp;nbsp;I can't bring myself to use a traditional email client -- it is, simply, painful. &amp;nbsp;So, yesterday, I was going to use sup again (despite it's critical flaws), only now sup-sync segfaults! &amp;nbsp;I've tried everything, including deleting the .sup directory. &amp;nbsp;There's no help from Google.&lt;br /&gt;&lt;br /&gt;There's no other explanation: I've angered the software gods, and they're punishing me.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5297772973487593985-7977980232738689837?l=blog.ser1.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ser1.net/feeds/7977980232738689837/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5297772973487593985&amp;postID=7977980232738689837' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/7977980232738689837'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/7977980232738689837'/><link rel='alternate' type='text/html' href='http://blog.ser1.net/2009/12/sup-again.html' title='sup... again!'/><author><name>SER</name><uri>http://www.blogger.com/profile/11674189468540726402</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5297772973487593985.post-312987617492763812</id><published>2009-12-29T05:42:00.000-08:00</published><updated>2009-12-29T05:42:32.065-08:00</updated><title type='text'>The sad state of operating systems (and Linux distributions)</title><content type='html'>I spent several hours yesterday getting &lt;a href="http://nixos.org/nixos/"&gt;NixOS&lt;/a&gt; installed on a spare laptop. &amp;nbsp;I've been toying with Nix for a couple of years; it is an elegant solution to a thorny problem that all operating systems face, and about which I've written before: dependency hell.&lt;br /&gt;&lt;br /&gt;The problem, in a nutshell, is that all modern software is built upon other software. &amp;nbsp;These are dependencies, and often, the software being depended &lt;b&gt;upon&lt;/b&gt;&amp;nbsp;isn't explicitly aware of the dependents. &amp;nbsp;Certainly, when the authors of the dependees update their software, they're unable to test all of the dependents. &amp;nbsp;Consequently, and perhaps inevitably, things break when software is upgraded.&lt;br /&gt;&lt;br /&gt;Linux -- and software written for Linux -- is particularly susceptible to this. &amp;nbsp;Debian's deb packager has done an admirable job getting around software dependencies; Redhat's RPM has not. &amp;nbsp;Still, the frustrating thing for me has been the fact that none of these deal with two very common things:&lt;br /&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Users may want to install multiple versions of the same application at the same time. &amp;nbsp;Stable, and test. &amp;nbsp;This is extremely common because distributions simply can't keep up with the pace of software development. There is always some piece of software that I need to upgrade that the Ubuntu package maintainers simply haven't gotten to yet, and may not get to for a while. &amp;nbsp;I want Mercurial 1.2, and Ubuntu is still on 1.0. &amp;nbsp;Or, worse, and more commonly, there's no back-port for it; I have to upgrade my entire system just to get newer software. &amp;nbsp;Which leads to the second item:&lt;/li&gt;&lt;li&gt;Linux is horribly unstable. &amp;nbsp;Ever since Linus made the decision to get rid of stable kernels, any kernel upgrade is as likely as not to cause &lt;b&gt;something&lt;/b&gt;&amp;nbsp;on my laptop to fail. &amp;nbsp;Linux has the worst history of regressions of any software I've seen, and I've written a fair share of regressions myself. &amp;nbsp;Quality control is almost non-existent. &amp;nbsp;The CDROM stops working, or the sound stops working, or the damned graphics card stops working, or the network...&amp;nbsp;&lt;u&gt;something&lt;/u&gt;&amp;nbsp;breaks, almost every time. &amp;nbsp;What this means is that upgrading between versions of Ubuntu -- which always involves a major kernel upgrade -- is like playing Russian Roulette with five of the six chambers full. &amp;nbsp;After it's aged a bit, any given version stabilizes. &amp;nbsp;In the meantime, you're stuck with using year-old software releases, which -- in computer terms -- means that you're using obsolete and unsupported software most of the time.&lt;/li&gt;&lt;/ol&gt;Ubuntu is &lt;b&gt;really&amp;nbsp;&lt;/b&gt;bad about the latter point. &amp;nbsp;Every single time I've upgraded to a new, major, release of Ubuntu, hardware that previously worked perfectly fails. &amp;nbsp;Gentoo is, possibly, worse. &amp;nbsp;I'm running that on a server and upgrade it rarely, and it is always a chore to get software working again after an upgrade. &amp;nbsp;So, when I recently revived a laptop, I decided to try a different distribution, called NixOS.&lt;br /&gt;&lt;br /&gt;NixOS gets around both of those issues with a clever approach which is simple in concept: build and install software in a sandbox and link to its dependencies using a view composed of specific versions. &amp;nbsp;So, for example (and this is greatly simplified), Nix will build and install glibc-1.0 in its own directory and creates a view that contains glibc-1.0 as the "official" glibc. &amp;nbsp;Software that depends on glibc version 1.0 is built in this view. &amp;nbsp;Views can be composed of different versions of different software, so you may have a view that looks like this:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;glibc-1.0&lt;/li&gt;&lt;li&gt;ghc-6.08&lt;/li&gt;&lt;li&gt;kde-3.5&lt;/li&gt;&lt;/ul&gt;and another view that looks like this:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;glibc-1.5&lt;/li&gt;&lt;li&gt;ghc-6.08&lt;/li&gt;&lt;li&gt;kde-3.5&lt;/li&gt;&lt;/ul&gt;Any mix of compatible software can be installed, and you can switch your environment on the fly.&lt;br /&gt;&lt;br /&gt;This is a very powerful concept; it lets you maintain multiple versions of software on a system, and it allows you to boot into different, &lt;b&gt;major&lt;/b&gt; versions of the composite whole -- which is &lt;b&gt;not&lt;/b&gt;&amp;nbsp;possible in any other Linux distro, or any other OS that I'm aware of -- without depending on virtualization (which is hideously expensive). &amp;nbsp;Ironically, this is the same approach that ClearCase -- that evil, poorly designed, poorly implemented, version control system -- uses. &amp;nbsp;Only, here it makes sense, because the views are designed by the packagers, who know what the software dependencies are.&lt;br /&gt;&lt;br /&gt;The problem with NixOS is that it doesn't work. &amp;nbsp;It may be the nature of it's being a young distro with few adherents. &amp;nbsp;It may be some flaw in the terribly complex system that implements the rather simple concepts. &amp;nbsp;But &lt;b&gt;something&lt;/b&gt;&amp;nbsp;is wrong with it. &amp;nbsp;For one thing, half of the software packages wouldn't install; half of the messages were "unable to download the source package", and the other half were compile errors in the packages. &amp;nbsp;And when I did manage to finally get X installed, it wouldn't start, because X couldn't find the video drivers -- which were also installed.&lt;br /&gt;&lt;br /&gt;So, NixOS has some pretty serious flaws, and isn't ready for even casual use. &amp;nbsp;I'll never run Gentoo again, because I don't fancy spending all of my weekends doing system maintenance. &amp;nbsp;For kicks, I tried booting the Haiku R1α1 CD; Haiku is pretty impressive for how quickly it boots, but unfortunately has no USB support, so it was a non-starter.&lt;br /&gt;&lt;br /&gt;I'd love to run Minix, except that there's no virtual memory support. &amp;nbsp;It is all pretty sad that, after -- what, &amp;nbsp;thirty? Fourty? -- years of modern computing systems, operating systems still suck. The only one that didn't was NeXTSTEP, but Steve Jobs killed that off through mis-management and incompetence.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;Right now, I'm re-installing Gobo from an old CD. &amp;nbsp;Gobo is a sort of half-way attempt at what NixOS does, but stops just short of the views. &amp;nbsp;This means that you get different versions of software, but not for the important pieces like libc. &amp;nbsp;Which is pretty crucial, if you're concerned about regressions. &amp;nbsp;But, we'll see; maybe newer versions of Gobo fix the problems I'd had with it before. &amp;nbsp;At the moment, I'm pretty depressed about it all.&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5297772973487593985-312987617492763812?l=blog.ser1.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ser1.net/feeds/312987617492763812/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5297772973487593985&amp;postID=312987617492763812' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/312987617492763812'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/312987617492763812'/><link rel='alternate' type='text/html' href='http://blog.ser1.net/2009/12/sad-state-of-operating-systems-and.html' title='The sad state of operating systems (and Linux distributions)'/><author><name>SER</name><uri>http://www.blogger.com/profile/11674189468540726402</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5297772973487593985.post-7060291650042438950</id><published>2009-12-15T07:32:00.000-08:00</published><updated>2009-12-15T07:32:54.128-08:00</updated><title type='text'>sup disenchantment</title><content type='html'>Ugh. &amp;nbsp;Harsh reality rears its ugly head.&lt;br /&gt;&lt;br /&gt;It turns out that sup isn't as great as I thought it was. &amp;nbsp;For one thing, it doesn't write state back to the server. &amp;nbsp;Consequently, if you &lt;b&gt;ever&lt;/b&gt;&amp;nbsp;view your inbox with anything other than sup -- or, god forbid, with sup itself running on a different machine -- then all of the work you've been doing to delete, flag, thread, label, and read email messages is lost. &amp;nbsp;sup &lt;b&gt;only&lt;/b&gt;&amp;nbsp;works from a single machine, and any state is known only by that sup instance.&lt;br /&gt;&lt;br /&gt;The sup folks call this a "philosophy." &amp;nbsp;I call it "stupidity of the highest order." &amp;nbsp;Or, maybe, I should call it laziness; I'd bet money that the only motivation for this "philosophy" was a desire to avoid writing synchronization code. &amp;nbsp;Sloppy, lazy programming, in other words.&lt;br /&gt;&lt;br /&gt;So, I take it back. &amp;nbsp;Stay well away from sup: it's malfunctional to the point of being broken. &amp;nbsp;And cross your fingers that someone does something similar, only implemented correctly this time, because the workflow paradigm &lt;b&gt;really&lt;/b&gt;&amp;nbsp;is powerful.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5297772973487593985-7060291650042438950?l=blog.ser1.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ser1.net/feeds/7060291650042438950/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5297772973487593985&amp;postID=7060291650042438950' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/7060291650042438950'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/7060291650042438950'/><link rel='alternate' type='text/html' href='http://blog.ser1.net/2009/12/sup-disenchantment.html' title='sup disenchantment'/><author><name>SER</name><uri>http://www.blogger.com/profile/11674189468540726402</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5297772973487593985.post-3894498301110548994</id><published>2009-12-12T07:49:00.000-08:00</published><updated>2009-12-12T07:49:52.976-08:00</updated><title type='text'>Sup is an outstanding email client</title><content type='html'>&lt;a href="http://sup.rubyforge.org/"&gt;Sup&lt;/a&gt; is a console-based email client that provides GMail-like management of &amp;nbsp;mail accounts. &amp;nbsp;It supports mbox, IMAP, and Maildir, and I'm here to tell you: it rocks. &amp;nbsp;Archiving, labeling, filtering, tagging... the workflow of mail management is almost identical to how you manage email in GMail. &amp;nbsp;I'd been slowly deprecating my other mail accounts in favor of GMail, but this was primarily because no other mail client (on Linux) provides the same capabilities as GMail. &amp;nbsp;The only thing I &lt;b&gt;don't&lt;/b&gt; like about GMail is that it is a web client, and web clients, with very few exceptions, suck. &amp;nbsp;Yes, even GMail.&lt;br /&gt;&lt;br /&gt;It take a little setting up, but you you've got the smasos, I highly recommend giving it a try. &amp;nbsp;If you have multiple email accounts, you can even aggregate them into a single buffer. &amp;nbsp;This is &lt;b&gt;very&lt;/b&gt;&amp;nbsp;nice.&lt;br /&gt;&lt;br /&gt;It does have a few issues:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;It is entirely console-based. &amp;nbsp;Don't get me wrong; this is a great -- even killer -- feature. &amp;nbsp;And while &lt;b&gt;I&lt;/b&gt;&amp;nbsp;love the vim/mutt-inspired key bindings, this is simply unusable for somebody like my wife, who isn't interested in surmounting the learning curve needed just to get to her email. &amp;nbsp;It'd be nice to have a GUI option.&lt;/li&gt;&lt;li&gt;It's a bit of a pain to set up. &amp;nbsp;There are several dependencies which, my first time around, took a while to get installed. &amp;nbsp;The gem helps -- but, as I've often argued, the Linux community does &lt;b&gt;not&lt;/b&gt;&amp;nbsp;need a package manager for every programming language (CPAN, gem, easy_install, cabal, et al.).&lt;/li&gt;&lt;li&gt;It crashes quite a bit when it is hooked up to MS Exchange.&lt;/li&gt;&lt;li&gt;The tab completion for the contacts is &lt;b&gt;very&lt;/b&gt;&amp;nbsp;flaky.&amp;nbsp;&amp;nbsp;I would almost say that it useless, except that I haven't quite figured out yet if the Exchange-crashing thing is interfering with buffer syncing -- more investigation is necessary, but it has been giving me trouble.&lt;/li&gt;&lt;/ul&gt;In any case, like I said: if you're not console-adverse, and you can manage to circumvent the installation issues, I highly recommend giving Sup a try. &amp;nbsp;I really believe that the GMail workflow is the future of mail management; it's only a matter of time before the other mail clients (Thunderbird, claws-mail, KMail, Evolution) catch on.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5297772973487593985-3894498301110548994?l=blog.ser1.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ser1.net/feeds/3894498301110548994/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5297772973487593985&amp;postID=3894498301110548994' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/3894498301110548994'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/3894498301110548994'/><link rel='alternate' type='text/html' href='http://blog.ser1.net/2009/12/sup-is-outstanding-email-client.html' title='Sup is an outstanding email client'/><author><name>SER</name><uri>http://www.blogger.com/profile/11674189468540726402</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5297772973487593985.post-8710628362877160242</id><published>2009-11-12T05:13:00.000-08:00</published><updated>2009-11-12T05:52:35.292-08:00</updated><title type='text'>NoSQL, and the Zone Of Pain</title><content type='html'>You can do some incredible things with SQL.&amp;nbsp; It's a very full and complex language, and relational databases are venerable and well-tuned.&amp;nbsp; There's a movement called NoSQL which argues that relational databases aren't appropriate for all problem domains, and that other database paradigms should be considered in any problem.&lt;br /&gt;&lt;br /&gt;NoSQL focuses on key/value databases&amp;nbsp; -- not strictly; you get graph dbs and even XPath query DBs, but most of these "distributed" DBs boil down to key/value DBs -- since that's what most of the proponents are.&amp;nbsp; I haven't yet encountered a NoSQL advocate who recomments an &lt;a href="http://en.wikipedia.org/wiki/List_of_object_database_management_systems"&gt;OODB&lt;/a&gt;, for instance.&amp;nbsp; They're all about Hadoop, Riak, CouchDB, HBase, and BigTable (although, there are more).&amp;nbsp; I suspect that this is because Google gave these DBs a lot of credence when it was exposed that they weren't storing their data in a SQL database.&amp;nbsp; OODBs never had such a persuasive validation, and consequently are much more rare.&lt;br /&gt;&lt;br /&gt;Non-relational DBs appeal to me.&amp;nbsp; For one thing, they are schema-less.&amp;nbsp; Relational DBs are in the "&lt;a href="http://www.parlezuml.com/metrics/Metrics%20Definitions.pdf"&gt;zone of pain&lt;/a&gt;" -- this is the zone of things upon which there are lots of dependencies, and yet are concrete objects (and so require changes as software is developed).&amp;nbsp; Things which have a lot of dependencies are very costly to change, which is why you want as many of these objects as possible to be abstract (interfaces) -- but DBs are concrete, which means that they &lt;b&gt;will&lt;/b&gt; change as the software grows.&amp;nbsp; The alternative is that software developers tie themselves into knots, and implement all sorts of shady coding, to avoid changing the DB schema.&amp;nbsp; If you dispense with the schema, then the DB is no longer difficult to change -- because there &lt;b&gt;is&lt;/b&gt; nothing to change.&amp;nbsp; It takes DBs out of the zone of pain.&lt;br /&gt;&lt;br /&gt;For a second thing, SQL is usually overkill. And, when it isn't, the schema is usually so baroque that nobody understands it.&amp;nbsp; I worked on a project at the US Forest Service where the schema diagram, printed, was 15 feet long and 4 feet high, and printed in a barely readable font.&amp;nbsp; It was utterly unnavigable.&amp;nbsp; I don't know if one of the NoSQL databases would have been a better fit in &lt;i&gt;that&lt;/i&gt; case, but I'd have liked to have found out.&amp;nbsp; Except, there wasn't a NoSQL movement back then.&lt;br /&gt;&lt;br /&gt;In any case, I found the following reading interesting:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://www.viget.com/extend/nosql-misconceptions/"&gt;NoSQL Misconceptions&lt;/a&gt;&amp;nbsp;&lt;/li&gt;&lt;li&gt;&lt;a href="http://havemacwillblog.com/2008/11/10/6-reasons-why-relational-database-will-be-superseded/"&gt;6 Reasons Why Relational Database Will Be Superseded&lt;/a&gt; (sic)&lt;/li&gt;&lt;li&gt;&lt;a href="http://adam.blog.heroku.com/past/2009/7/6/sql_databases_dont_scale/"&gt;SQL Databases Don't Scale&lt;/a&gt;&amp;nbsp;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.rackspacecloud.com/blog/2009/11/09/nosql-ecosystem/"&gt;The NoSQL Ecosystem&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5297772973487593985-8710628362877160242?l=blog.ser1.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ser1.net/feeds/8710628362877160242/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5297772973487593985&amp;postID=8710628362877160242' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/8710628362877160242'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/8710628362877160242'/><link rel='alternate' type='text/html' href='http://blog.ser1.net/2009/11/nosql-and-zone-of-pain.html' title='NoSQL, and the Zone Of Pain'/><author><name>SER</name><uri>http://www.blogger.com/profile/11674189468540726402</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5297772973487593985.post-4479288487340626371</id><published>2009-11-08T03:45:00.000-08:00</published><updated>2009-11-08T04:10:32.819-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Family'/><category scheme='http://www.blogger.com/atom/ns#' term='Dream'/><title type='text'>Strange dreams</title><content type='html'>&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_gnlBpVYS_QM/SvaxuzW_a1I/AAAAAAAAA4k/0I2aN1MDmNM/s1600-h/Marnie.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://3.bp.blogspot.com/_gnlBpVYS_QM/SvaxuzW_a1I/AAAAAAAAA4k/0I2aN1MDmNM/s400/Marnie.jpg" /&gt;&lt;/a&gt;&lt;a href="http://4.bp.blogspot.com/_gnlBpVYS_QM/Sva0WirBztI/AAAAAAAAA4s/Jp3cTeQRDGk/s1600-h/feh_022695_000001_img_0038.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://4.bp.blogspot.com/_gnlBpVYS_QM/Sva0WirBztI/AAAAAAAAA4s/Jp3cTeQRDGk/s400/feh_022695_000001_img_0038.jpg" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;In my dream, Brett and Marnie (my siblings-in-law, above) came by, and they both had flawless skin from the California sun. &amp;nbsp;Marnie had gotten plastic surgery, and was telling us about how disgusting it was, and about how she woke up in the middle of it and had to have more sedative administered. &amp;nbsp;She now looked like Claire Danes. &amp;nbsp;Both of their hair was lighter -- Marie is already blonde, but Brett's a brunette, so now he was sort of brown with blonde highlights.&lt;br /&gt;&lt;br /&gt;Reflecting on the dream, it seems pretty obvious that it was driven by my feelings about Brett's new position, and about how quickly they are (financially) out-distancing the rest of us. &amp;nbsp;Financial success was translated into physical perfection. &amp;nbsp;Which is funny, because I never used to care much about money, and even now, I can't work up much enthusiasm for it. &lt;br /&gt;&lt;br /&gt;Anyway, that's the dream for today. &amp;nbsp;The one I can remember, anyway.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5297772973487593985-4479288487340626371?l=blog.ser1.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ser1.net/feeds/4479288487340626371/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5297772973487593985&amp;postID=4479288487340626371' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/4479288487340626371'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/4479288487340626371'/><link rel='alternate' type='text/html' href='http://blog.ser1.net/2009/11/strange-dreams.html' title='Strange dreams'/><author><name>SER</name><uri>http://www.blogger.com/profile/11674189468540726402</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_gnlBpVYS_QM/SvaxuzW_a1I/AAAAAAAAA4k/0I2aN1MDmNM/s72-c/Marnie.jpg' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5297772973487593985.post-4404495298504122211</id><published>2009-09-25T10:09:00.000-07:00</published><updated>2009-09-25T10:09:31.836-07:00</updated><title type='text'>Quality is not optional</title><content type='html'>I keep seeing this "good enough" meme going around.&amp;nbsp; At a company meeting where I work, recently, management was espousing the same crap.&lt;br /&gt;&lt;br /&gt;I can only hope that these people are plagued with "50%-good" products.&amp;nbsp; How about some 50%-good tires on their cars?&amp;nbsp; Maybe they'd like some 50%-good surgery, or a 50%-good pacemaker.&amp;nbsp; How about getting to fly in 50% good airplanes for the rest of his life?&amp;nbsp; I know!&amp;nbsp; Let's give their kids some "good enough" education!&lt;br /&gt;&lt;br /&gt;I'm not surprised that most of this bullshit is coming out of a culture in which Walmart was able to become the success it has.&amp;nbsp; Recently, we needed something for a weekend project and bought it from Walmart, because it was closest store.&amp;nbsp; What poor quality crap.&amp;nbsp; It'll all need to be replaced in a year, contributing to landfill and wasted resources.&amp;nbsp; I'm not going purchase from Walmart any more, and I'm not going to spend money on half-baked, crap-quality software, either.&lt;br /&gt;&lt;br /&gt;Word gets around about quality.&amp;nbsp; It's the American auto-maker's nightmare right now.&amp;nbsp; Ford, Chrystler, Chevrolet... they're all struggling to reverse decades of built-up public perception about poor quality, even when some of them are actually making fairly decent cars right now.&amp;nbsp; It isn't quite the same with software; Microsoft has been making crap software for, well, ever, and they're still dominant.&amp;nbsp; But I think that if you take the monopoly factor out of it, software companies &lt;b&gt;do&lt;/b&gt; suffer from delivering half-assed product to their customers.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5297772973487593985-4404495298504122211?l=blog.ser1.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ser1.net/feeds/4404495298504122211/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5297772973487593985&amp;postID=4404495298504122211' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/4404495298504122211'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/4404495298504122211'/><link rel='alternate' type='text/html' href='http://blog.ser1.net/2009/09/quality-is-not-optional.html' title='Quality is not optional'/><author><name>SER</name><uri>http://www.blogger.com/profile/11674189468540726402</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5297772973487593985.post-2331384242118530585</id><published>2009-09-15T06:27:00.000-07:00</published><updated>2009-09-16T03:50:28.046-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='processes'/><category scheme='http://www.blogger.com/atom/ns#' term='software'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='version control system'/><title type='text'>More on VCS, branching, merging, etc.</title><content type='html'>Martin Fowler has posted a &lt;a href="http://martinfowler.com/bliki/FeatureBranch.html"&gt;good discussion&lt;/a&gt; about the various flavors of processes to manage branching and merging.&amp;nbsp; There's a lot of good information in there.&amp;nbsp; His conclusion is that cherry picking with the VCS is undesirable, that cherry picking should be done in software, and that continuous integration is the preferred process. It's a good read (with pretty diagrams), although I don't agree with his conclusion.&lt;br /&gt;&lt;br /&gt;In particular, he ignores the fact that some categories of changes can not be continuously integrated.&amp;nbsp; For example, systemic architecture changes are one category which often can not be continuously integrated.&amp;nbsp; Also, the examples provided by Martin entirely ignore parallel branches (in the feature-set sense, rather than the VCS sense) of the software, and/or long-lived support for releases.&amp;nbsp; Of course, these are exceptions, and nothing in Martin's suggestion prevents the use of cherry picking to solve these cases.&lt;br /&gt;&lt;br /&gt;Martin's suggestion is that it is better to do cherry-picking in the software through configuration, or licensing, or whatever, than to do it via the VCS.&amp;nbsp; I don't believe that these approaches are mutually exclusive.&amp;nbsp; You &lt;b&gt;can&lt;/b&gt; have a modular architecture &lt;b&gt;and&lt;/b&gt; VCS-controlled cherry picking at the same time, and I'd argue that this is where the DVCS niche lies.&amp;nbsp; With a truly modular software architecture, you should be able to cherry-pick features from multiple development branches, on demand; the very nature of the modular architecture will ensure that conflicts are minimized.&amp;nbsp; Furthermore, by the time you've invested the effort for a modular architecture, you don't need software-controlled plug-ins; in effect, you've forced yourself to isolate and compartmentalize feature sets, which is what you need to do to reduce merge conflicts.&lt;br /&gt;&lt;br /&gt;So, a modular architecture is just a generalization of good loose coupling of software components.&amp;nbsp; There's nothing inherent in this that &lt;b&gt;requires&lt;/b&gt; a plug-in architecture or any other sort of software-driven cherry-picking.&amp;nbsp; Once you have a modular architecture, you don't &lt;b&gt;need&lt;/b&gt; continuous integration, in the sense that Martin suggests.&lt;br /&gt;&lt;br /&gt;Martin proposes that incomplete features can be "hidden" by simply hiding the UI that drives the features.&amp;nbsp; This is often not possible, if the features are woven into the business logic of the tool -- as is often the case where changes are made to existing features.&amp;nbsp; The thing that bothers me the most is the concept of delivering half-baked, functionally untested code; it increases the opportunity for bugs, and eliminates the possibility of dropping features entirely.&amp;nbsp; If a company develops feature A and then, at some point, decides to &lt;b&gt;never&lt;/b&gt; deliver feature A (maybe the market has changed, perhaps some disruptive technology has rendered feature A obsolete), and that company has been doing continuous integration, then that feature is &lt;i&gt;irrevocably&lt;/i&gt; interwoven into to the code base with numerous merges.&amp;nbsp; The company is now in a position to spend significant resources un-weaving the feature, or be forced to deliver zombie code in their product.&amp;nbsp; Again, this is solvable by sufficient isolation of the feature, but if you have that level of isolation, then why do you need continuous integration in the first place?&lt;br /&gt;&lt;br /&gt;Cherry picking with the VCS can be powerful, and continuous integration should be used only where necessary.&amp;nbsp; The key is that software absolutely must have a good, modular design, and if at all possible, architectural and interface changes &lt;b&gt;should&lt;/b&gt; be continuously integrated.&amp;nbsp; With VCS based cherry picking, organizations have the opportunity to decide to cut bait on a feature, and the overall code quality of released products will improve by the very fact that they &lt;b&gt;don't&lt;/b&gt; contain hidden, unused, and un-functionally-tested code.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5297772973487593985-2331384242118530585?l=blog.ser1.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ser1.net/feeds/2331384242118530585/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5297772973487593985&amp;postID=2331384242118530585' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/2331384242118530585'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/2331384242118530585'/><link rel='alternate' type='text/html' href='http://blog.ser1.net/2009/09/more-on-vcs-branching-merging-etc.html' title='More on VCS, branching, merging, etc.'/><author><name>SER</name><uri>http://www.blogger.com/profile/11674189468540726402</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5297772973487593985.post-4194601961443122924</id><published>2009-09-14T05:26:00.001-07:00</published><updated>2009-09-14T13:42:47.526-07:00</updated><title type='text'>Multitasking and Software Development</title><content type='html'>Three articles that I came upon recently seem related, although they stem from unconnected sources.  The first is the &lt;a href="http://news.stanford.edu/news/2009/august24/multitask-research-study-082409.html"&gt;Stanford study&lt;/a&gt; which shows that multitaskers not only don't do individual tasks very well but actually multitask more poorly than non-multitaskers do, and that they don't realize it, either.  The second is a very well-written article by Paul Graham about the difference between &lt;a href="http://www.paulgraham.com/makersschedule.html"&gt;Maker's and Manager's schedules&lt;/a&gt;.  Of course, both provide examples of the &lt;a href="http://en.wikipedia.org/wiki/Dunning%E2%80%93Kruger_effect"&gt;Dunning-Kruger&lt;/a&gt; effect.&lt;br /&gt;&lt;br /&gt;It's something that's been bothering me a lot lately, because I'm beginning to suffer from effects of the first two, and I'm afraid that it will lead to the third.  In my job, I've increasingly been forced to multitask as my job duties have been changing.  This is learned behavior, in my case at least, due to Manager's Meeting schedules, interruptions, the need to respond timely to email, and having to juggle multiple responsibilities.  This has had a very definite affect on my programming ability.  I'm not sure what I'm going to do about this, yet, but it has me concerned.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5297772973487593985-4194601961443122924?l=blog.ser1.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ser1.net/feeds/4194601961443122924/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5297772973487593985&amp;postID=4194601961443122924' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/4194601961443122924'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/4194601961443122924'/><link rel='alternate' type='text/html' href='http://blog.ser1.net/2009/09/multitasking-and-software-development.html' title='Multitasking and Software Development'/><author><name>SER</name><uri>http://www.blogger.com/profile/11674189468540726402</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5297772973487593985.post-3551501386370805456</id><published>2009-07-18T05:53:00.000-07:00</published><updated>2009-11-12T05:25:41.178-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='software'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='make'/><title type='text'>GNU Make</title><content type='html'>Ok.  How do you get make to conditionally include a file, based on the calling arguments?  I'm not sure that this can be done, but I'd love to be proven otherwise.&lt;br /&gt;&lt;br /&gt;I have a make file that builds a third party sub-project if it hasn't been built.  It does this by including the sub-project's make file.  The problem is that if the make file doesn't exist, a rule gets triggered to create it, which is a laborious process involving untarring a huge tarball and running through a sort of autobuild.  The issue is that any target will trigger this, because of the "include" rule.  This looks sort of like this:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;... blah blah make rules&lt;br /&gt;include subproject/makefile&lt;br /&gt;&lt;br /&gt;subproject/makefile:&lt;br /&gt;ReallyExpensiveAutocfgChain&lt;br /&gt;&lt;br /&gt;clean:&lt;br /&gt;rm subproject/makefile&lt;br /&gt;.... blah blah other clean tasks&lt;br /&gt;&lt;/blockquote&gt;So, if you "make clean", and the project is already clean, the include triggers the create makefile rule, which then takes two or three minutes to create the makefile... so that it can be immediately deleted.&lt;br /&gt;&lt;br /&gt;What I can't figure out is how to get make to conditionally include the file, or conditionally execute the make file creation rule, such that if "clean" is being resolved, it &lt;span style="font-weight: bold;"&gt;doesn't&lt;/span&gt; include the make file.  I suspect that it has to do with some combination of $^ and the if clause, but I'm not yet sure if those auto-variables are set at the top level.  Anyway, I'm still exploring.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5297772973487593985-3551501386370805456?l=blog.ser1.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ser1.net/feeds/3551501386370805456/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5297772973487593985&amp;postID=3551501386370805456' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/3551501386370805456'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/3551501386370805456'/><link rel='alternate' type='text/html' href='http://blog.ser1.net/2009/07/gnu-make.html' title='GNU Make'/><author><name>SER</name><uri>http://www.blogger.com/profile/11674189468540726402</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5297772973487593985.post-925681723621487189</id><published>2009-07-15T04:06:00.000-07:00</published><updated>2009-09-16T03:49:01.126-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='mercurial'/><category scheme='http://www.blogger.com/atom/ns#' term='version control system'/><title type='text'>Mercurial Rebase</title><content type='html'>I read Martin Fowler's entry on &lt;a href="http://martinfowler.com/bliki/MercurialSquashCommit.html"&gt;using mqueues in Mercurial to squash a series of commits&lt;/a&gt; into a single commit, and while the post is informative, there's an easier way: the &lt;a href="http://mercurial.selenic.com/wiki/RebaseExtension"&gt;Mercurial rebase &lt;/a&gt;command.&lt;br /&gt;&lt;br /&gt;The &lt;a href="http://mercurial.selenic.com/wiki/RebaseProject"&gt;rebase extension&lt;/a&gt; has been included in Mercurial since 1.3, although it's been available since sometime in the 1.2 release, and can perform a number of useful alterations of a repository.  For Martin's use case, his 5-step mqueue sequence is accomplished with two much more simple commands:&lt;br /&gt;&lt;blockquote&gt;&lt;span style="font-family: courier new;"&gt;hg tag "Pre-rebase"&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;hg rebase -s &lt;/span&gt;&lt;rev&gt;&lt;span style="font-family: courier new;"&gt; --collapse&lt;/span&gt;&lt;br /&gt;&lt;/rev&gt;&lt;/blockquote&gt;The tag is necessary because the rebase command will not operate on descendants or ancestor, and will not automatically create a new tip to hold the rebase.  It'd be nice to be able to dispense with the tag, and it isn't necessary if you're (for example) rebasing from a branch onto another branch which already has other changes -- the key point here is that you need to have a different tip that you're rebasing onto which isn't either an ancestor or descendant of the branch that you're rebasing.&lt;br /&gt;&lt;br /&gt;Here's a simple use case.  First, I'll set up the repository:&lt;br /&gt;&lt;blockquote style="font-family: courier new;"&gt;Script started on Wed 15 Jul 2009 07:25:27 AM EDT&lt;br /&gt;% mkdir squash&lt;br /&gt;% cd squash&lt;br /&gt;% hg init&lt;br /&gt;% echo AAA &amp;gt; A&lt;br /&gt;% echo BBB &amp;gt; B&lt;br /&gt;% echo CCC &amp;gt; C&lt;br /&gt;% echo DDD &amp;gt; D&lt;br /&gt;% hg addremove&lt;br /&gt;adding A&lt;br /&gt;adding B&lt;br /&gt;adding C&lt;br /&gt;adding D&lt;br /&gt;% hg ci -m 1&lt;br /&gt;% hg mv A M&lt;br /&gt;% hg ci -m 2&lt;br /&gt;% hg mv B N&lt;br /&gt;% hg ci -m 3&lt;br /&gt;% hg mv C O&lt;br /&gt;% hg ci -m 4&lt;br /&gt;% hg mv D P&lt;br /&gt;% hg ci -m 5&lt;br /&gt;% hg glog&lt;br /&gt;@  changeset:   4:347a96580b84&lt;br /&gt;|  tag:         tip&lt;br /&gt;|  user:        Sean Russell &lt;ser@ser1.net&gt;&lt;br /&gt;|  date:        Wed Jul 15 07:26:40 2009 -0400&lt;br /&gt;|  summary:     5&lt;br /&gt;|&lt;br /&gt;o  changeset:   3:e1e2c4e27fcf&lt;br /&gt;|  user:        Sean Russell &lt;ser@ser1.net&gt;&lt;br /&gt;|  date:        Wed Jul 15 07:26:34 2009 -0400&lt;br /&gt;|  summary:     4&lt;br /&gt;|&lt;br /&gt;o  changeset:   2:47b3a7f41d78&lt;br /&gt;|  user:        Sean Russell &lt;ser@ser1.net&gt;&lt;br /&gt;|  date:        Wed Jul 15 07:26:28 2009 -0400&lt;br /&gt;|  summary:     3&lt;br /&gt;|&lt;br /&gt;o  changeset:   1:e0879f674e71&lt;br /&gt;|  user:        Sean Russell &lt;ser@ser1.net&gt;&lt;br /&gt;|  date:        Wed Jul 15 07:26:24 2009 -0400&lt;br /&gt;|  summary:     2&lt;br /&gt;|&lt;br /&gt;o  changeset:   0:2902dfa1725a&lt;br /&gt;user:        Sean Russell &lt;ser@ser1.net&gt;&lt;br /&gt;date:        Wed Jul 15 07:26:12 2009 -0400&lt;br /&gt;summary:     1&lt;/ser@ser1.net&gt;&lt;/ser@ser1.net&gt;&lt;/ser@ser1.net&gt;&lt;/ser@ser1.net&gt;&lt;/ser@ser1.net&gt;&lt;/blockquote&gt;Here's the actual rebase.  First, switch to the revision that you want to rebase &lt;span style="font-style: italic;"&gt;onto&lt;/span&gt;, then create a tag to create a new tip, then perform the rebase.  In this example, I want to collapse revisions 1-4, so I switch to revision 0:&lt;br /&gt;&lt;blockquote&gt;&lt;span style="font-family: courier new;"&gt;% hg up 0&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;4 files updated, 0 files merged, 4 files removed, 0 files unresolved&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;% hg tag Pre-collapse&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;% hg rebase -s 1 --collapse&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;saving bundle to /home/ser/squash/.hg/strip-backup/e0879f674e71-temp&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;adding branch&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;adding changesets&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;adding manifests&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;adding file changes&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;added 2 changesets with 5 changes to 5 files&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;rebase completed&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;% hg glog&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;@  changeset:   2:3420d5add3eb&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;|  tag:         tip&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;|  user:        Sean Russell &lt;/span&gt;&lt;ser@ser1.net&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;|  date:        Wed Jul 15 07:26:40 2009 -0400&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;|  summary:     Collapsed revision&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;|&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;o  changeset:   1:a98523a2e8f9&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;|  user:        Sean Russell &lt;/span&gt;&lt;ser@ser1.net&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;|  date:        Wed Jul 15 07:26:50 2009 -0400&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;|  summary:     Added tag Pre-collapse for changeset 2902dfa1725a&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;|&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;o  changeset:   0:2902dfa1725a&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;  tag:         Pre-collapse&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;  user:        Sean Russell &lt;/span&gt;&lt;ser@ser1.net&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;  date:        Wed Jul 15 07:26:12 2009 -0400&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;  summary:     1&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;% exit&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;Script done on Wed 15 Jul 2009 07:27:04 AM EDT&lt;/span&gt;&lt;br /&gt;&lt;/ser@ser1.net&gt;&lt;/ser@ser1.net&gt;&lt;/ser@ser1.net&gt;&lt;/blockquote&gt;The rebase command is quite useful, and the rebase page gives examples of a number of use cases.  For cases like this, it's much easier than using the raw mqueue operations upon which it is based.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5297772973487593985-925681723621487189?l=blog.ser1.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ser1.net/feeds/925681723621487189/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5297772973487593985&amp;postID=925681723621487189' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/925681723621487189'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/925681723621487189'/><link rel='alternate' type='text/html' href='http://blog.ser1.net/2009/07/mercurial-rebase.html' title='Mercurial Rebase'/><author><name>SER</name><uri>http://www.blogger.com/profile/11674189468540726402</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5297772973487593985.post-8351203175762813329</id><published>2009-04-05T13:36:00.001-07:00</published><updated>2009-09-16T03:50:08.901-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='subversion'/><category scheme='http://www.blogger.com/atom/ns#' term='version control system'/><title type='text'>Why not to use Subversion</title><content type='html'>I'm a big Subversion fan, and have been since it was first released.  Or, I was, until recently, when I discovered that Subversion is fundamentally broken.&lt;br /&gt;Some background: I've been using Subversion since 2000, when a project I was working on for the Forest Service chose to convert a CVS repository to it.  Since then, I've been running a Subversion server that's been hosting a number of open source projects I work on, as well as having provided hosting service for a couple of projects that I don't work on.  By and large, Subversion has been adequate, and stable enough, and I've been able to work my way out of any DB wedges or failed upgrades that I've gotten myself into.  I've never used it on a large-scale, multi-user, multi-branch project, but I've taken comfort from the fact that projects such as KDE&lt;b&gt; do&lt;/b&gt; use SVN.&lt;br /&gt;Recently, however, I've been using Subversion at work.  We switched to SVN from CVS (there's a whole story behind that) while at the same time the rest of the company was consolidating on ClearCase.  I'm not going to go into why Subversion is superior to ClearCase; I'm still convinced that it is, and I strongly believe that you have to be insane to use ClearCase in the Enterprise.  But now I think that while ClearCase should be avoided like the plague, I also believe that Subversion shouldn't be used, either.&lt;br /&gt;The reason for this is because Subversion's merging mechanism is fundamentally, structurally, and architecturally broken.  Subversion simply does not perform merges correctly.&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Subversion's --reintegrate is useless if you use any sort of cherry-picking.  Since I've never encountered a situation where cherry-picking &lt;i&gt;wasn't&lt;/i&gt; leveraged at some point or other, --reintegrate is &lt;b&gt;always&lt;/b&gt; useless.&lt;/li&gt;&lt;li&gt;Subversion performs merges changeset by changeset, which means that you will always perform more conflict resolution than you need to.&lt;/li&gt;&lt;li&gt;Subversion gets very confused, very easily, about tree merges.  This is mostly because of (2).&lt;/li&gt;&lt;/ol&gt;As an illustration of how ... well, no other adjective really captures the scale of the issue, so forgive me ... of how &lt;i&gt;fucked up&lt;/i&gt; (2) is, consider a series of changes from A to P on branch 1, and a similar -- yet unrelated -- series of changes from A to P on branch 2.  Now merge branch 2 into branch 1.  Despite the fact that P is the same on both branches, due to the fact that Subversion merges incrementally, that means that you're going to end up resolving a lot of conflicts that -- ultimately -- are entirely unnecessary.&lt;br /&gt;While you're considering this, also consider how other version control systems, such as Mercurial, Bazarr, and Git, do &lt;i&gt;not&lt;/i&gt; have this problem.&lt;br /&gt;Finally, Subversion's whole merge-tracking mechanism is really &lt;a href="http://subversion.tigris.org/issues/show_bug.cgi?id=3250"&gt;buggy&lt;/a&gt;, which makes it a pain in the ass to use.  And I don't mean "buggy in the edge cases," I mean "buggy in that you're &lt;i&gt;always&lt;/i&gt; going to hit this bug in any non-trivial merge." There are work-arounds for the bugs, usually, but you only get to these work-arounds after running into one of the bugs, spending some time finding what the work-around is, and then re-starting your merge.  You can spend &lt;i&gt;hours&lt;/i&gt; running around in circles, hitting a bug, finding a work-around, restarting your merge, resolving conflicts, hitting a bug, finding a work-around, restarting your merge, resolving the &lt;b&gt;same&lt;/b&gt; conflicts, getting a little further, hitting another bug, and so on.  Hours.&lt;br /&gt;So, I'm ditching Subversion.  It just isn't worth it any more, when there are better revision control systems out there that do handle merging properly, and have other advantages such as distributability.  If you never want to merge branches, then SVN is fine... but if you're doing only single-developer development (which is about the only place you're likely to entirely avoid branching and merging), then there are other reasons why you should consider a DVCS, anyway.  Mercurial, Bazaar, and Git are easier to set up, transport, and share with untrusted peers.  They're less effort to maintain -- admittedly, only by a tiny bit; I've never found SVN to take much administration, except in the bad-old BDB days.  And they do merging correctly. Plus, most of them have nice features (sometimes as plug-ins), like block-level commits, cherry picking that &lt;b&gt;doesn't&lt;/b&gt; break their versions of re-integrate (grumble), flexible rebasing, nice graphical views of the merge history (which can be really useful), patch queue/shelving support, integrated bisecting, and so on.  And, usually, a workspace with full history often consumes not significantly more than a single SVN check-out.  A Mercurial repository conversion (full history) of my work SVN repository consumes .99 GB, versus a single checked-out workspace in SVN at .3GB.   Two SVN check-outs take up .6GB; two HG check-outs (using hg clone) consumes 1.12G.  Three SVN, .9GB.  Three HG, 1.26.  You can see where this is going.  And this is an unusual repository, with a large number of binaries stored in the repository.&lt;br /&gt;When I use Mercurial, I miss some Subversion features, such as cheap copies and branches. Some people hate it, but I&lt;b&gt; love&lt;/b&gt; the fact that tags and branches in Subversion are merely COW links.  It solves a lot of problems in a very elegant way.  Mercurial has its own quirks -- they all do -- but at least merging isn't broken like it is in Subversion.&lt;br /&gt;&lt;b&gt;Postscript...&lt;/b&gt;&lt;br /&gt;The original title of this was "Why Subversion shouldn't be used for the enterprise," but then I realized that there's no reason to use it at all.  It would be a fine choice for the enterprise if its merging was done properly and wasn't full of bugs.  The enterprise is really the only place where Subversion's feature set makes it superior to DVCSes, but the enterprise is the one place where merging really has to be as trouble-free and correct as possible.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5297772973487593985-8351203175762813329?l=blog.ser1.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ser1.net/feeds/8351203175762813329/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5297772973487593985&amp;postID=8351203175762813329' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/8351203175762813329'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5297772973487593985/posts/default/8351203175762813329'/><link rel='alternate' type='text/html' href='http://blog.ser1.net/2009/04/why-not-to-use-subversion.html' title='Why not to use Subversion'/><author><name>SER</name><uri>http://www.blogger.com/profile/11674189468540726402</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry></feed>
