Posts for Wednesday, November 18, 2009

Command Line Dictionary Update


I do alot of work from the terminal so I like to have a dictionary available there. Previously if you read about my command line dictionary post you know that I used some html parsing with curl to do this, lately though I’ve learned about Sdcv and it’s absolutely great. Since I’m not always on the internet this option works alot better with the same results. I also added a thesaurus too. You can find it here.

Making a Django form field using aggregated values

So another Django post for those who like that sort of thing. In this post we will dynamically create the choices argument for the forms.ChoiceField.

Often you will be making a search form where the objects to be searched have common but dynamic field values. The Django admin app does this kind of thing when you use list_filter, but I haven't yet found an official function in the API for doing this (but Django adds new functions all the time).

To take a simple hypothetical example of a web application for a pet shop, you have a model 'Fish' which has a field called 'species' which is just a normal CharField that the staff enter when they get new stock.

Here is my made up Fish model:

from django.db import models
class Fish(models.Model):
    name = models.CharField(max_length=255)
    species = models.CharField(max_length=255)
    price = models.DecimalField(max_digits=7,
                                decimal_places=2,
                                null = True)
    sold = models.BooleanField(default = False)
    record_created = models.DateTimeField(auto_now_add = True)
    record_updated = models.DateTimeField(auto_now = True)

You want to allow the customers to search fish by species.

How do you do this?

Well the most obvious yet drastic solution would be to completly change the workflow to create a species model that the staff fill in every time they add a new kind of fish. Then you can make the form as follows:

class FishSearchForm(forms.Form):
    species = forms.ModelChoiceField(Species.objects.all())

This is not a complete solution however, as your form will have every species name the shop has ever stocked, not necessarily what they have now. Allowing the customer to search for a south american aquarama when you cannot get them anymore is not giving a good experience, it is just raising expectations and then dashing them straight away. So then you have to make a backwards lookup from the species to the fish and filter out the species that are not in stock and so on.

Instead, the way I have been solving this problem is by dynamically creating the 'choices' field based on what is in the database:

class FishSearchForm(forms.Form):
    species = forms.ChoiceField(choices = valuechoices(Fish, 'species'))

My valuechoices function is below:

def valuechoices(model_name, field_name):
    """Return a choices list based on aggregated values of a model.
    Like the list_filter in the admin.
    (For unaggregated objects, just use a ModelChoiceField)

    For example:
    class FishSearchForm(forms.Form):
        species = forms.ChoiceField(choices = valuechoices(Fish, 'species'))

    """
    from django.template.defaultfilters import slugify
    choice_list = model_name.objects.values_list(field_name).distinct()
    choices = [(slugify(choice[0]), choice[0]) for choice in choice_list]
    choices.sort()
    return choices

It is easy to add a none value, e.g. for advanced forms where there are lots of optional ways to search:

species_choices = valuechoices(Fish, 'species')
species_choices.insert(0, (None, u'All'))
class FishSearchForm(forms.Form):
    species = forms.ChoiceField(choices = species_choices)

Be careful when setting an initially selected value, since obviously the initial value needs to exist in the database for it to work.

if you did set the initial value to a something that is not in 'choices', the current version of Django would probably let you off and display the form anyway, but it is best not to rely on undocumented behaviour. It is quite easy just to check whether the initial value is in the database:

initial_fish_species = 'koi' if Fish.objects.filter(species = 'koi').count() else None
class FishSearchForm(forms.Form):
    species = forms.ChoiceField(choices = valuechoices(Fish, 'species'),
                                        initial = initial_fish_species)

Happy hacking and all that jazz.

Discuss this post - Leave a comment
avatar

They’re Back! Amarok Finally Reimplements Its Killer Feature!

https://bugs.kde.org/show_bug.cgi?id=168781

--- Comment #13 from Daniel Dewald <TheCrasher gmx de>  2009-11-18 02:56:25 ---
Labels support was added and is available in the GIT Version. First stable
release with label support will be 2.2.2.

Woot!

Nostalgiarama

On a whim today, I decided to dust off a bit of my Jpop (Ayumi Hamasaki, to be precise) and give it a listen. It is amazing what memories are invoked from just the first few tracks off of LOVEppears, which I first started listening to in 2000 — my first total immersion dives into computing (nay, computer appreciation), fleeting loves, the Milwaukee School of Engineering, Gentoo Linux, compiling Gentoo Linux, breaking all of my computers at least once.

Lonely nights in a warm bedroom, surrounded by computers, Ayu blasting through XMMS, heralding my first great interest shift. The daily commutes to MSOE punctuated with long sessions before and after class in the student center, tasting for the first time the sweet nectar of broadband, filling my belly with Japanese culture. My last serious interest in anime, my first serious appreciation for the potpourri of the Internet.

The whiteness of an Ayu wallpaper. My rust bucket truck. The couches in the Cudahy Student Center. The cheeseburgers they served there. Windows file sharing clients, their names long lost, from an age before the luxury of BitTorrent. A certain kind of orange present in my Trillian IM client. Shit, Trillian itself. Conversations never saved, never remembered, no longer the highlight of a lazy afternoon between classes. People not seen in years. Quiet conversations outside, in the cold, revisiting the high school roof.

All from Ayu’s “Trauma". If I had one hope, it was that I never lost these memories. If I had one fear, it would be losing the ability of music to invoke them.

avatar

Portage 2.2 and License Management

Portage 2.2 has recently introduced a new feature that allows users to select what licenses they want to allow on their install. The interface still needs some work (for example, Portage needs to explain when it has “ignored” packages due to license during updates) so that users don’t, for example, get confused as to why their system suddenly wants icedtea instead of sun-jdk. Other than these fairly minor issues, it seems to be working well so far.

There’s an article on Gentoo Wiki that explains this new feature from a users point of view and how to tailor it to your individual tastes.

Posts for Tuesday, November 17, 2009

avatar

msmtp queueing for offline use

muttWhen you start using a small and slow laptop over an unreliable GPRS connection (say while travelling in an ICE at 300km/h) you start noticing certain things that are missing from your current shell-setup. One of these problems manifests itself when you want to send the mail you just typed in mutt. Fortunately there is an easy and elegant fix (if you use mutt with msmtp): msmtpqueue. This gives you three little scripts to enqueue, list and send messages which would otherwise have gone directly into msmtp. Just put them somewhere and then add this to your .muttrc:set sendmail="$HOME/local/bin/msmtp-enqueue -i"
macro index \Cy "!$HOME/local/bin/msmtp-runqueue<enter>"</enter>
Now “sending” mail from mutt happens instantly and you can manually push the batch of mails out to your smtp once the train has stopped in a station and your reception is stable ;). msmtp-listqueue shows you the queued mails while msmtp-runqueue mails them (using msmtp of course!). For the downstream-direction of offline mutt-usage there’s offlineimap, but I feel like this deserves an extra post ;)

avatar

Get adblocking back for archivum.info

If you have adblock enabled and you try to visit any url of www.archivum.info you will get a really nasty alert saying:

You Are Using Adblock Plus or some other advert blocking software! Archivum.info relies on advertising for revenue. Please add www.archivum.info to your ad blocking whitelist or disable ad blocking when you visit www.archivum.info.

When I first saw this I laughed…and then I tried to find a way to bypass it.
I used curl to see the sites html code:

<code2>$ curl -v www.archivum.info
curl -v www.archivum.info 
* About to connect() to www.archivum.info port 80 (#0)
*   Trying 69.147.224.162... connected
* Connected to www.archivum.info (69.147.224.162) port 80 (#0)
> GET / HTTP/1.1
> User-Agent: curl/7.19.5 (i486-pc-linux-gnu) libcurl/7.19.5 OpenSSL/0.9.8k zlib/1.2.3.3 libidn/1.15 libssh2/1.2
> Host: www.archivum.info
> Accept: */*
> 
< HTTP/1.1 200 OK
< Date: Tue, 17 Nov 2009 11:24:22 GMT
< Server: Apache
< Last-Modified: Mon, 16 Nov 2009 08:41:17 GMT
< Accept-Ranges: bytes
< Content-Length: 9392
< Vary: Accept-Encoding
< Content-Type: text/html
< 
<html>
<head>
<title>archivum.info - The Internet archive.</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<script type="text/javascript">var disabled = false;</script><script type="text/javascript" src="http://www.archivum.info/js/adblocker_probe.js?
site=http://googlead.foobar.tld/"></script><script type="text/javascript">if (disabled == false) { location.replace("http://www.archivum.info/denied");
alert("You Are Using Adblock Plus or some other advert blocking software! Archivum.info relies on advertising
for revenue. Please add www.archivum.info to your ad blocking whitelist or disable ad blocking when you visit
www.archivum.info.");}</script></head>

[snip]</code2>

Here’s how this site blocks Adblockplus: there’s a variable called disabled set to “false” then if a js (http://www.archivum.info/js/adblocker_probe.js) runs it sets disabled to “true” . The hint is that adblockplus blocks urls starting with “googlead.” so it won’t visit “http://www.archivum.info/js/adblocker_probe.js?site=http://googlead.foobar.tld/” and the variable will remain “false“. Then the alert pops up.

The solution is very simple, just add an exception to your local AdblockPlus rules, AdblockPlus Preferences -> Add Filter:
@@|http://www.archivum.info/js/adblocker_probe.js?site=http://googlead.foobar.tld/

So firefox, visits the js url, disabled becomes “true” you are allowed to continue browsing the site and AdblockPlus continues blocking all blockable items.

Countries in Django

Django is my preferred web framework. If you don't know what I am talking about, then you can read my previous posts, e.g. here and here and so on.

Anyway, for the people who do like Django, it comes with a nice set of local field types for inputting post codes and zip codes in different countries as well as Peruvian national identity numbers, Australian territories, Romanian Bank Account Numbers, Icelandic phone numbers and lots of other stuff. This saves you the hassle of implementing fields for these types of data yourself.

Somewhat bizarrely however, Django does not currently contain a field for what country the person or thing is in, which is often a very basic yet important piece of data to collect.

Fortunately, people have started work on this and on a LanguageField, however the patches have been languishing on the bug tracker for some time.

I wanted the CountryField now, so I copied and pasted from the patches and put it in a module, this is on my code page (direct link is here).

It is pretty obvious how you use it, import the field into your models file:

from whatever.countries import CountryField

Then you can add it to a model:

class Whatever(models.Model):
    country_based_in = CountryField(blank = True)

Here the field is shown in the automatically generated admin:

http://commandline.org.uk/images/posts/django/django_countries_field.png

It seems to have all of the countries that one is ever likely to encounter on the web. The module lists countries alphabetically, however lots of online forms put the United States and perhaps the United Kingdom and other large Internet using countries at the top. If you want to do this, then just arrange the tuple in the order that you want, and delete everything from line 276 onwards (i.e. def sorted_countries etc)

This module is beautifully simple, yet it probably saved me 30 to 60 minutes of fiddling about. This is the joy of programming in a friendly open source community, someone has already done the hard work for you already, you just need to dig it out of some bug tracker and adapt it to your needs.

Happy hacking and all that jazz.

Discuss this post - Leave a comment

Posts for Monday, November 16, 2009

Fluxbox, we meet again

I'm sort of tired of KDE4 crashing left and right and Plasma barfing all over me all day. So I decided to check out the current state of lightweight window managers.

Lo and behold, Fluxbox is still going strong. It was the first WM I used way back in 2000-something when I started using Linux full-time. Last time I tried, there were always weird compatibility problems with system tray icons and pagers working properly when running a mix of KDE and Gnome and other apps, but those seem to have cleared up nicely; I have yet to hit any snags. Here's a screenshot.

Fluxbox

This took very minimal effort to install and set up. Maybe a couple hours total. I'm using ipager and conky. The wallpaper comes from the UniQ KDE theme. Vim and Emacs themes are my own.

The Fluxbox style is mydefcon_4 from tenr.de which is probably the largest and most thorough set of themes created by one person that I've witnessed. That fellow is motivated.

For all the bells and whistles of KDE4, what features did I actually use regularly?

  1. A menu of apps
  2. Taskbar + System tray + Clock
  3. KWin's good window management.
  4. Global keyboard shortcuts galore
  5. One widget: current CPU/RAM/Network usage
  6. Mouse/keyboard management, background-setting, etc.

Fluxbox gives me all but number 5, and Conky gives me that. Number 6 you can do with xset and feh and such.

And I like being motivated to use keyboard shortcuts for more things. I'm already halfway there. Maybe I can take the plunge eventually and try a tiling window manager. Not sure I've reached that level of nerditude yet though.

And now I can move and resize windows without my graphics card bursting into flames. Maybe when I can afford a few more cores worth of CPU I'll try KDE4 again. Honestly I think I have too much monitor real-estate for my ancient computer to handle smoothly in KDE4.

Not to knock KDE4; it's awesome and I'll probably go back someday. But everyone needs a break now and then.

Posts for Sunday, November 15, 2009

This Week in Python Stupidity: os.stat, os.utime and Sub-Second Timestamps


The primary design principle behind the Python programming language is to take everything that’s horrible and wrong with Perl and get it horrible and wrong in a completely different and even more hideous way. Today, however, we shall be looking at a particularly egregious case of stupidity the likes of which not even PHP has managed to replicate.

On Unix, timestamps have traditionally been held as an integer number of seconds since the epoch. The modification time for a file is one place such a timestamp has been used. Two groups of system calls are of interest to us here.

First, stat (and its fstat and lstat variants). The stat system call places information about a file into a struct also named stat (which is possible thanks to a lesser case of brain damage in C’s design). To get the mtime of a file, historically we would have used the st_mtime field, which is of type time_t, which is an integer of some kind:

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>

int main(int argc, char * argv[])
{
    struct stat s;
    if (-1 == stat("timmy", &s))
        return EXIT_FAILURE;

    printf("stat.st_mtime for timmy is %ld\n", s.st_mtime);
    return EXIT_SUCCESS;
}

Sometimes we might want to modify a file, but not affect its mtime. Thus, we need a way to set a file’s mtime to a given value, and to do this we would historically have used a function from the utime family:

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <utime.h>
#include <fcntl.h>

int main(int argc, char * argv[])
{
    struct stat s;
    if (-1 == stat("timmy", &s))
        return EXIT_FAILURE;

    int fd;
    fd = open("timmy", O_WRONLY, O_TRUNC | O_CREAT);
    if (-1 == fd)
        return EXIT_FAILURE;
    if (0 != close(fd))
        return EXIT_FAILURE;

    struct utimbuf times = { .actime = s.st_atime, .modtime = s.st_mtime };
    if (-1 == utime("timmy", &times))
        return EXIT_FAILURE;

    return EXIT_SUCCESS;
}

(Sidenote: the above almost certainly should be using fstat and futimes instead to avoid race conditions, but this is irrelevant for our examples.)

But all of this operates only on a second-precision basis. For many applications this is no longer sufficient. Fortunately, some kernels and filesystems now support nanosecond-resolution timestamps.

First, for the stat family: rather than using st_mtime, we now use st_mtim, which is a struct timespec:

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>

int main(int argc, char * argv[])
{
    struct stat s;
    if (-1 == stat("timmy", &s))
        return EXIT_FAILURE;

    printf("stat.st_mtim for timmy is %lds %ldns\n",
            s.st_mtim.tv_sec, s.st_mtim.tv_nsec);
    return EXIT_SUCCESS;
}

And if our filesystem supports it, we get something like:

$ ./mtimens
stat.st_mtim for timmy is 1258321672s 173919603ns

As we can see, running our old utime-using code preserves the seconds but not the nanoseconds:

$ touch timmy
$ ./mtimens
stat.st_mtim for timmy is 1258321978s 62671870ns
$ ./utime
$ ./mtimens
stat.st_mtim for timmy is 1258321978s 0ns

To modify preserving nanoseconds, we use either utimensat or futimens:

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <utime.h>
#include <fcntl.h>

int main(int argc, char * argv[])
{
    struct stat s;
    if (-1 == stat("timmy", &s))
        return EXIT_FAILURE;

    int fd;
    fd = open("timmy", O_WRONLY, O_TRUNC | O_CREAT);
    if (-1 == fd)
        return EXIT_FAILURE;
    if (0 != close(fd))
        return EXIT_FAILURE;

    struct timespec times[2] = { s.st_atim, s.st_mtim };
    if (-1 == utimensat(AT_FDCWD, "timmy", times, 0))
        return EXIT_FAILURE;

    return EXIT_SUCCESS;
}

And now it works as expected:

$ touch timmy
$ ./mtimens
stat.st_mtim for timmy is 1258322326s 852774523ns
$ ./utimens
$ ./mtimens
stat.st_mtim for timmy is 1258322326s 852774523ns

Incidentally, POSIX.1-2008 considers the non-nanosecond-resolution functions and members to be deprecated, although since the nanosecond resolution functions aren’t universally available yet, a certain amount of autovoodoo is generally required…

Now we shall look at some Python. First, the old way:

import os

s = os.stat("timmy")

f = open("timmy", "w+")
f.close()

os.utime("timmy", (s.st_atime, s.st_mtime))

Now, to see if we can guess how the new way works:

>>> import os
>>> os.stat("timmy").st_mtim
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'posix.stat_result' object has no attribute 'st_mtim'

Mmm, nope. Time to consult the documentation. Nothing under stat, but there’s something interesting called stat_float_times:

stat_float_times([newvalue])

Determine whether stat_result represents time stamps as float objects. If newvalue is True, future calls to stat() return floats, if it is False, future calls return ints. If newvalue is omitted, return the current setting.

Uh oh. This can’t be good. Let’s look more closely at what happens when we run our code that uses stat.st_mtime and os.utime:

$ touch timmy
$ ./mtimens
stat.st_mtim for timmy is 1258324320s 762942258ns
$ python utime.py
$ ./mtimens
stat.st_mtim for timmy is 1258324320s 762942000ns
$ ./utimens 12345678901 111111111
$ ./mtimens
stat.st_mtim for timmy is 12345678901s 111111111ns
$ python utime.py
$ ./mtimens
stat.st_mtim for timmy is 12345678901s 111110000ns

What’s that, Lassie? Timmy has lost several significant digits of its sub-second mtime? Oh noes!

Yup, that’s right, Python’s underlying type for floats is an IEEE 754 double, which is only good for about sixteen decimal digits. With ten digits before the decimal point, that leaves six for sub-second resolutions, which is three short of the range required to preserve POSIX nanosecond-resolution timestamps. With dates after the year 2300 or so, that leaves only five accurate digits, which isn’t even enough to deal with microseconds correctly. Brilliant.

Posted in python Tagged: python

Migrated to Sabayon, in need of a EvroKorpus plasmoid

What I would give for a EvroTerm/EvroKorpus plasmoid right now!!! XD

Also, the stability of the Jamendo plugin in Amarok leaves a lot to be desired.

Sabayon is cool (for a temporary replacement for Gentoo).

hook out >> legal counciling, translating and sipping tea...
<!--break-->

links for 2009-11-15

avatar

A lovely little emoticon set from KDE.

I am of the opinion that the default emoticons on KDE are disgusting. Over time I’ve grown accustomed to them but lately I decided to do something once and for all. My solution was in the KDE forums – which had a different iconset (shouldn’t they be the same?) Sure, they didn’t have as many emoticons but I’m not an avid user. Anyways, here is a picture for those unaquainted with those iconsets:

… and of course you can download it here. KDE users can install it from System Settings, or even through Get Hot New Stuff. Users on other operating systems, it’s just a regular .tar.gz file so uncompress it and get the pictures inside and import it your own way.

Related posts:

  1. What is FTP?

It's Been Awhile...

Wow... It's been a long, long time since I updated anything about my life on here.  Mostly because stuff got really busy, and really interesting about April of this year.  I've documented all the crud that we've gone through, so no need to rehash that.

What I would like to say is that music is more a part of my life now than it ever has been.  I play my drums an average of about 30 minutes per day now, whereas before six months ago, I played them more like 30 minutes per month.  I've been spending a large amount of time learning how to properly track, mix, and master audio.  I've created a site for my studio, and am in the process of creating a site for something which I hadn't really seriously considered until two people started paying me to play my drums for their songs.

It's sort of a "remote drummer" type of thing.  Basically, musicians write their songs, record them the best they can, and then send them to me to play and record the drums for them.  Evidently, people think I can play much better than I think I can, and right now, we can certainly use the money.

So...  That's what's up.

Well...  Almost...    I've got a Christmas-based project I'm working on with a friend which is turning out to be one of the funnest and most professional projects I've ever worked on.  Talk about trial by fire.

Anyway...  As the project progresses, I'll let ya more in to what exactly is going on, but for now, I'm having the time of my life with it.

And that's about it for this time of the morning.  I've gotta go to choir in a few hours!

Posts for Saturday, November 14, 2009

Installing Go in Ubuntu Jaunty

Processor: x86
Operating System: Ubuntu Linux 9.04 Jaunty Jackalope

1. CONFIGURE THE ENVIRONMENT
Set the necessary variables in your .bashrc that is if you are using bash. Assuming you have a 386-descendant processor and you want to keep the installation clean in an external disk (/media/disk).

# Google Go Programming Language Settings
export GOROOT=/media/disk/go
export GOARCH=386
export GOOS=linux
export GOBIN=/media/disk/go/bin

2. INSTALL MERCURIAL.
Easy.

$ sudo easy_install mercurial

Install process,

Searching for mercurial
Reading http://pypi.python.org/simple/mercurial/
Reading http://www.selenic.com/mercurial
Best match: mercurial 1.3.1
Downloading http://mercurial.selenic.com/release/mercurial-1.3.1.tar.gz
Processing mercurial-1.3.1.tar.gz
Running mercurial-1.3.1/setup.py -q bdist_egg --dist-dir /tmp/easy_install-a3YaRd/mercurial-1.3.1/egg-dist-tmp-VyNqd2
zip_safe flag not set; analyzing archive contents...
mercurial.lsprof: module references __file__
mercurial.templater: module references __file__
mercurial.extensions: module references __file__
mercurial.i18n: module references __file__
Adding mercurial 1.3.1 to easy-install.pth file
Installing hg script to /usr/local/bin
	
Installed /usr/local/lib/python2.6/dist-packages/mercurial-1.3.1-py2.6-linux-i686.egg
Processing dependencies for mercurial
Finished processing dependencies for mercurial


3. CHECKOUT GO USING MERCURIAL

$ hg clone -r release https://go.googlecode.com/hg/ $GOROOT

Checkout process,

requesting all changes
adding changesets
adding manifests
adding file changes
added 4016 changesets with 16888 changes to 2931 files
updating working directory
1640 files updated, 0 files merged, 0 files removed, 0 files unresolved

4. BUILD GO

$ cd $GOROOT/src
$ ./all.bash

Build process,

--- cd pkg/exp/ogle
rm -rf *.[568vqo] *.a [568vq].out *.cgo[12].go *.cgo[34].c *.so _obj _test _testmain.go ogle
8g -o _go_.8 abort.go arch.go cmd.go event.go frame.go goroutine.go rruntime.go rtype.go rvalue.go process.go vars.go
rm -f _obj/exp/ogle.a
gopack grc _obj/exp/ogle.a _go_.8
8g -I_obj main.go
8l -L_obj -o ogle main.8
	
real	0m2.264s
user	0m1.496s
sys	0m0.136s
	
--- cd ../doc/progs
	
real	0m4.430s
user	0m3.144s
sys	0m0.444s
	
--- cd ../test/bench
fasta
reverse-complement
nbody
binary-tree
binary-tree-freelist
fannkuch
regex-dna
spectral-norm
k-nucleotide
mandelbrot
meteor-contest
pidigits
threadring
chameneosredux

It should end with the following lines,

--- cd ../test
0 known bugs; 0 unexpected bugs

5. ENJOY!

avatar

Aspire One / X.org screen blanking

When using X on my Acer Aspire One (110L) and playing videos with mplayer, X would occasionally blank the screen after a few minutes without any way out but to suspend/resume to get your X back. This didn’t only happen with mplayer, and it didn’t have anything to do with xscreensaver.Anyway, after some digging I found that adding Option "FramebufferCompression" "off"to the Device-section of my xorg.conf resolved the problem.
Another cool thing I found out while going through ssh manpages is that you can silence the motd on login simply by touching ~/.hushlogin.

I'm turning into a Lisp snob

Reddit and StackOverflow and other websites I frequent are filled to the brim with discussion of Google's Go. The code snippet on the front page is:

package main

import "fmt"

func main() {
  fmt.Printf("Hello, 世界\n")
}

First thoughts that ran through my head as I looked over the site:

  • Ugh, look at all that syntax.
  • Nice(r) type system (than C++ and Java). I'll stick with multimethods though.
  • Concurrency semantics, hmmm... Shared mutable memory between threads? I think I'll stick with Clojure for now thanks.
  • Where are the macros?
  • It has anonymous functions and closures and sort-of first-class functions? Good. Welcome to the 1960's.
  • len is a special operator? Sigh. (Programming language quality is usually inversely proportional to the number of special forms.)
  • Cool that they used Japanese in the example though. (That word is sekai, "world", obviously.)

Compared to a Lisp, this language looks indistinguishable to C, Perl, Python, Java etc. It looks like such a small incremental improvement (if it even is an improvement). Yet another imperative, for-loop-wielding, curly-brace-using, pointer-mangling, state-mutating, OOP language.

In fact via Reddit today I read this awesome post to a mailing list which compares Go with ALGOL68, and it gave me a would-you-look-at-that moment. Once you learn a few languages that are significantly different from ALGOL derivatives, all ALGOLish languages start to look eerily similar. Are we really stuck with ALGOL-derived languages being the only viable mainstream languages for all time? How much polish can we possibly apply to the same turd?

Then I realized, I'm turning into a Lisp snob. : ( Learning a Lisp apparently does spoil you for the rest of time. I am without a basis to judge whether this language will be a successful replacement of anything. All I know is I probably won't use it. Honestly I'm much more excited about new things on the horizon in Clojure. And I still have getting better at Haskell on my TODO list.

Posts for Friday, November 13, 2009

avatar

Gentoo Wallpaper

I just found this wallpaper I seem to have made in ‘06 (around August) and posted to the Gentoo Forums. It never went anywhere, but it would be a shame to let it go to waste, so here you go. Don’t ask for any higher resolutions though ;)

Download it in 800×600, 1024×768 or 1280×1024.

Linux audio player comparison (nit-picking)

Given my inability to use Amarok 1.4 and my lack of desire to use Amarok 2.0, I tried loads of music players and for now I've landed on aTunes.

It's not perfect. It's far from perfect. But it's the best of the bunch. These are the features I MUST HAVE for a media player and which aTunes possesses.

  1. Last.fm integration. aTunes has probably the best integration I've seen in a player, without going over-the-top and stuffing a whole web browser into the app.
  2. System tray icon, right-clickable with song controls in the menu.
  3. Commandline interface.
  4. Able to display CJK fonts. In Arch (or in Gentoo using the icedtea6-bin VM) CJK fonts are displayed as empty boxes, but in Gentoo using Sun JVM, it works fine.
  5. Tag editing. aTunes has a pretty nice tag editor for single songs or multiple at once.
  6. Amarok-like tree of albums/artists/genres/whatever I want. I want a single expandable and collapsable tree-list, not 3 panes I have to click between.
  7. Equalizer.
  8. Skins are nice; aTunes has these.
  9. "Collection" support and folder-watching/auto-updating when I dump music into ~/music. aTunes does this very well. Scanned a few thousand files fairly quickly, and does updates very fast.
  10. Amarok-1.4-like spreadsheetish playlist layout.
  11. Lightweight build process. No gstreamer. aTunes provides Mplayer and Xine backends and has few to no other dependencies (besides Java). The Mplayer backend didn't work out very well for me, but Xine works beautifully.

It also has some other nice bonuses, like the elegant way it uses the Album Artist tag for albums with multiple artists, the interesting statistics and bar graphs it can produce from your song listening history, playlist tabs, and so on.

Things I dislike about aTunes... well it's a Java app, so it takes a decade to start up. It also has horrid fonts and the widgets are clunky. But it's responsive once it's running, and I don't care how it looks as much as how sane the layout is. Searching is also clunky. But these aren't show-stoppers.

Here's a list of other players I tried, and why I didn't use them.

Amarok 1.4

  • I'd use this if I could. :( It compiles and runs on my Gentoo box but too much stuff is broken due to bit-rot.

Amarok 2

Banshee

  • It wanted to pull in about a billion and a half Gnome dependencies. This is not fun for a KDE user.

Songbird

So close. This is probably second place behind aTunes. It has a great plugin system, it's skinnable, the layout is extremely functional and compact and easy to use and customizable. But...

  • No system tray icon in Linux! This is a show-stopper. There's alltray but it doesn't let me right-click and have song controls.
  • It has a clumsy commandline interface which makes setting global KDE keyboard shortcuts annoying.
  • Bloat. Do you really need a full-fledged web browser in your media player?
  • XUL, ew.

gmusicbrowser

This is very customizable (almost absurdly so) and looks promising. However...

  • Still a bit beta-quality.
  • Crashed on me a couple times in the short time I used it.
  • Interface has a kitchen-sink feel to it. Too many tabs and widgets all over the place. I couldn't find a layout I liked.
  • Looks pretty good, but no real compelling reason to use this.
  • Written in Perl?

amaroq

  • Alpha-quality PyQt4 clone of Amarok 1.4. Looks promising. I will keep an eye on this.

MPD

Last time I tried MPD was years ago. If aTunes doesn't work out, I'll try this next. But aTunes has kept me going for a week now, and I have very few complaints.

If they do a complete rewrite for aTunes 2.0 and destroy the interface, I'll jump off a bridge.

Clojure: redirecting output to a file

In a scripting language, you often run scripts from a command line and print things to STDOUT. For long output you can redirect it to a file via shell-redirection ($ foo.rb > foo.txt).

But if you're using Clojure for ad-hoc scripting, you're probably sitting in a REPL, not running from a command line. REPLs generally don't have shell-redirection. Printed output just gets dumped into your REPL. This can be annoying. (After a good 10,000 lines of output into a REPL buffer, Emacs starts to lag.)

But you can write a macro to handle this easily enough.

(use 'clojure.contrib.duck-streams)
(defmacro redir [filename & body]
  `(spit ~filename (with-out-str ~@body)))

Then:

user> (redir "foo.txt" (foo) (bar) (baz))

Now whatever (foo) and friends would've printed to STDOUT will instead go to foo.txt. I've found this somewhat useful at work lately.

Edit: better version via Graham Fawcett:

(defmacro redir [filename & body]
  `(binding [*out* (writer ~filename)] ~@body))

Sleep Button in KDE 4 Workaround


If you noticed that pushing the sleep button does nothing in KDE 4 (as of this writing <=4.3.3), this is because of a bug in KDEs’ power management tool Powerdevil. It appears that in most cases Powerdevil does not recognize the Xorg servers’ XF86Sleep key. To fix this, you may be able to rebind the sleep key in the KDE control panel.

Add a New Input Action

Open System Settings then Input Actions. Then add a new Global Shortcut:

Name it ‘Sleep’ or whatever you like. Add a Comment if you wish and in the Trigger tab select your hotkey. Try setting the sleep key first. For me, setting the hotkey to the sleep key didn’t work because I believe that Powerdevil already has it bound. There have been others though that look to have sucessfully done so.

Note: If someone knows of a way to decouple the Powerdevil sleep key please let me know.

I bound mine to Scroll Lock (hope I don’t need it anytime soon) then in the Action tab entere the dbus command to suspend to ram:

qdbus org.kde.kded /modules/powerdevil suspend 2

If this doesn’t work, try ’suspend 1′. If you would like to suspend to disk:

qdbus org.kde.kded /modules/powerdevil suspend 4

Posts for Thursday, November 12, 2009

Loggerhead Init Script for Gentoo

I just set up a Bazaar repository server at work. Gentoo has no official ebuild for Loggerhead, so I installed it from Mark Lee's Bazaar overlay. Unfortunately, this does not ship with an init script for serve-branches, so I wrote one.

The script is /etc/init.d/loggerhead (mode 755):

#!/sbin/runscript
# Copyright 1999-2009 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2
# $Header: $

PIDFILE=/var/run/loggerhead.pid
LOGDIR=/var/log/loggerhead

depend() {
    need net
}

start() {
    ebegin "Starting loggerhead"
        start-stop-daemon --start --quiet --background \
        --make-pidfile --pidfile ${PIDFILE} \
        --exec /usr/bin/serve-branches -- --log-folder=${LOGDIR} \
        ${LOGGERHEAD_OPTS}
    eend $?
}                               
                                
stop() {                        
    ebegin "Stopping loggerhead"
        start-stop-daemon --stop --quiet \
        --pidfile ${PIDFILE}
    eend $?                 
}

This uses a single entry from /etc/conf.d/loggerhead:

LOGGERHEAD_OPTS="/var/bzr"

It seems to work. When I get the chance I may patch the ebuild to include it and suggest it to the maintainer.

How Round Is Your Circle?


As everyone knows, a point is that which has no part, a line is a breadthless length, and it is possible to cut a sphere up into a finite number of pieces, move them around and put them back together again to get two spheres identical in every way to the original sphere.

Unfortunately, in reality, points have a size, lines have a width, spheres aren’t spheres and they can’t be cut up into infinitely complicated pieces. How Round Is Your Circle, Where Engineering and Mathematics Meet by John Bryant and Chris Sangwin is an attempt by engineers to convince mathematicians that caring about real world issues can be interesting.

The book covers various practical issues, such as:

  • How to draw a straight line, and how to make a ruler
  • How to test how circular a circle is
  • How to measure area

In the process, it discusses all kinds of cunning gadgetry used in the olden days before digital computers and mass production, from steam engine linkages and pistons to slide rules and draughting devices. It also covers various physical demonstrations of geometric problems, and illustrates what happens when physical inaccuracies are ignored:

64 = 65

It’s certainly an interesting read, although I would have preferred more emphasis on the tools and gadgets used than on demonstrations of things they can be used to make. The maths isn’t particularly heavy, and shouldn’t put too many people off. Similarly, there’s nothing on the engineering side that would be inaccessible to anyone with no engineering background.

Posted in hardware Tagged: books
avatar

stackoverflow response that I absolutely loved


I absolutely love this quote:

“Any fool can write code that a computer can understand. Good programmers write code that humans can understand” (Martin Fowler, “Refactoring: Improving the Design of Existing Code”)

Check out this question/answer on stackoverflow.com.

Is Google evil?

Google has become quite ubiquitous in our modern world. Starting out as a pure search engine, the company added Email and online office applications as well as a maps and route planning software and many many more. Whenever Google sees a need for more or better software they seem to jump in trying to fix it.

Google is really into personalization: They offer to store your search history in order to improve future searches for example. Their contacts application transcends application borders to make finding your contacts simple (as long as everybody has a Google account).

On the other hand Google is being severely criticized for their monopoly on information: If Google drops your page from their index it's basically dead because people will not find it (given Google's marketshare). Google's company motto is "Don't be evil" but how serious can we take that statement? Are we too focussed on that one service to rule them all?

Let's look at how Google got where it is today. Back in the day there were many competing search engines (and meta search engines) but at some point everybody started using Google because it was the best product, it provided the best (most relevant) results, it was not as cluttered as the other offerings, it was simple to use but powerful.

From their very strong position in search they branched out: When GMail came out, most other services didn't offer as much storage and Gmail's interface is still one of the best (if not the best) webmail interfaces out there (and it supports real tagging).

Instead of relying on slow Java Applets, Google Maps ran quick in every browser. It had not just abstract maps but also the overlayed satellite photo view which looks really cool.

Looking back Google got into the position they are in by competing and winning. I'm not one of those "the market will fix everything, yay" kind of people, but if you think that markets are such a good thing, Google did play your game.

Well let's get back to the topic at hand, are they "evil"?

Looking at how Google seems to work and what kinds of software they release they are not evil they're just radical pragmatists, or even simpler: Engineers. The engineer sees a problem and seeks a technical solution to it. But many of the problems that technology creates (for example privacy issues) are not technological problems themselves and therefore cannot be solved by technology.

I've heard people joking about Germans based on the existence of the word "Technikfolgenabschätzung". That a word like that could just come from Germans cause they take everything to seriously. But technology assessment, as it's called in English, is often what Google lacks. Google's not evil, they just seem to look at the world through a purely technological filter and (please engineers, don't cry) most problems are not technical.

I do absolutely agree with people that advise others to reflect their media and service usage, to think about whether putting all their data into one company's hands is such a smart idea, to see whether other services might serve them better, but I also believe in using the best tool for the job.

It's kinda funny how people complain about Google building a monopoly when they run Microsoft's Windows on all their machines. Monopolies slap you in the face all over the place.

And Microsoft isn't "evil" either, they just produce an operating system that is not good enough for me to use. "Evil" and "good" are words that fit into fairy tales and religious texts but they usually do not apply to what people in the real world do because they are not absolute.

We all know the "evil genius" character from movies, but does that happen in the real world? Do people use an evil laughter and plan nasty things? No.

Everybody acts on what they know and think is right. Their assumptions on what is right, their priorities might be different than yours, but nobody acts "evil". If you look at other people's actions you might consider them to be evil, but that's because you compare them to your own set of rules and priorities. We as a society have agreed on a certain canon of things generally to be considered "good" but probably everybody could name a few circumstances where those rules don't apply: Many people would agree to killing a mass murderer "for the greater good" for example even though killing people is generally considered to be "bad".

Google isn't evil and I love many of their services but I'm always careful when it comes to giving others full access to my data (which is why I host my own mail server for example). The monopoly Google built is based on merit. You are concerned about that? Fix it, build something better.

My boss recently said that maybe the government should provide a search engine (obviously an uncensored one) and actually I think that is a great idea: Let's take all that EU money they burn in worthless projects every year and hire a bunch of smart people to run a not-for-profit search engine. Don't outsource it to companies that just try to milk the project but really set up your own group doing it. If your service is as good as (or even better than) Google's, the monopoly will be broken. But just telling people to use services that work worse for them won't help and isn't the right way.

Planet Larry is not officially affiliated with Gentoo Linux. Original artwork and logos copyright Gentoo Foundation. Yadda, yadda, yadda.