I used to think that the Labour party was deliberately attacking the higher education system in the UK. One of their first acts upon being elected into power in 1997 was to abolish the student grant, favouring loans instead and gifting graduates from low income families with £15-20K of debt before they'd even stepped into their professional career. The thing that galled me about how that was announced at the time was when this was credited as an 'enabler' for students from low income families. I come from a low income family, and unless your degree course is conveyor-belt in/out, there's a very good chance that you could leave university with nothing but debt before your course is over. By its final year, my course was less than a third of the size of the first.
Now, 'Lord' Mandleson, our current Business Secretary, is slashing budgets for higher education, and at the same time asking universities to preserve standards of education. I don't know how his brain works, but when the education system has been getting systematically destroyed by 'efficiency savings' for ten years, there simply isn't that much slack. In addition, he'd like this to be accomplished in part by reducing three year degrees to two years 'as a way of easing the funding crisis'! Knocking a third off degree times will result in wonderfully incapable graduates, and weaken the value of the system as a whole.
How about some honest investment? You know, where you put money into something and reap the rewards in the future? Like when your graduates get higher paying jobs as a result of being better educated, and then pay higher taxes as a result?
As I began, I used to think that the Labour government was attacking higher education. Now I just think they're a mob of self-interested, incompetent fraudsters who've lost all touch with the real world.
Wednesday 23 December 2009
Monday 21 December 2009
The Ultimate zsh Prompt
I cram a lot of information into my
The code behind this, to be stuffed in your
This uses only shell built-ins, so is suitably fast for running at every prompt.
One variant I used at work was to colour the prompt based on the
zsh
prompt. I could describe it, but a picture covers it better:The code behind this, to be stuffed in your
$HOME/.zshrc
, is:
autoload colors
colors
precmd()
{
PREV_RET_VAL=$?
if [ "$TERM" = "dumb" ]
then
PS1="%n@%m %% "
return
fi
if [ $EUID -eq 0 ]
then
USER_COLOUR=red
else
USER_COLOUR=green
fi
BASIC_COLOUR=cyan
# Basic prompt: 'user@host [history-number'"
PS1="%{${fg[$USER_COLOUR]}%}%n@%m %{${fg_bold[$BASIC_COLOUR]}%}%1~%{${fg[$BASIC_COLOUR]}%} [%!"
# Possibly append '/job-count', if the number of jobs is > 0
if [ $#jobstates -ne 0 ]; then
PS1="${PS1}/%j"
fi
# Possibly append '/error-status', if $? wasn't 0
if [ $PREV_RET_VAL -ne 0 ]
then
PS1="${PS1}/%{${fg_bold[red]}%}$PREV_RET_VAL%{${fg_bold[$BASIC_COLOUR]}%}"
fi
# Close off the brackets for the status section, and finalise.
PS1="${PS1}] %% %{${fg_no_bold[default]}%}"
}
This uses only shell built-ins, so is suitably fast for running at every prompt.
One variant I used at work was to colour the prompt based on the
DEPLOYMENT_STATUS
environment variables that the SAs set (one of 'prod', 'test', 'dev', 'cont'): red for production, purple for contingency, blue for test, and green for development, but that's not quite as useful at home. :-)
Sunday 6 December 2009
Readable Perl
Wednesday 2 December 2009
Corporate Mistrust
On Monday, my wife called me to mention that she could buy a heavily discounted copy of Microsoft Office 2008 for Mac through a scheme at work. The price was good enough to make it worthwhile, so I accepted her offer to buy me a copy. Within a few minutes, she emailed me the download link and serial number.
The installer asked for that serial number, which I carefully cut and pasted from the email. Within a few minutes, the office icons are in the dock, and the updater was asking if I wanted to grab the latest patches.
All good so far.
Then I launch Word, and it asks again for my serial number. Again, I copy and paste it into the dialog, expecting that, for whatever reason, I'll have to do this for Excel and PowerPoint, too. Except that the serial number isn't accepted. I try again. Again, failure. Exasperated, I opt for the clean uninstall option using the 'Remove Office' application installed as part of the suite. It doesn't work: 'Remove Office' spends literally hours searching for versions of office to remove, and when I eventually cancel, it tells me that it couldn't find any. What, not even in the bog-standard '/Applications/Microsoft Office 2008' folder? Why would it ever have looked there?
So I manually uninstall it, removing all of the fragments that the damn procedure scattered throughout
The first contact with customer support yields a different download link, and them re-sending me the original key. But the file from the alternate link is identical to the original. So, with the same key and the same file, what happens? That's right: another key validation failure.
So now it's back to customer support, this time providing them with a hardware signature, presumably so that they can generate a new key that's completely locked down to my laptop.
This whole experience has left me wondering why I spent any money on the product at all. The automatic assumption of "it's a copy!" has further damaged my already tarnished opinion of Microsoft as a company, and the textbook incompetence of their first-contact customer support is a frustrating waste of my time.
Really, I wouldn't (and haven't) had this problem with OpenOffice, which has the added bonus of being absolutely free. The only reason I want Office is just so that I can open documents sent to me without losing content, and also to produce Word files that I know will open properly for those ridiculous agencies that accept it as their only CV format.
Edit: 3rd December, 2009
Eventually, I got an obscenely long product activation code from Microsoft. It didn't work. The 'Remove Office' utility still doesn't work, so I'm left tossing shattered fragments of this sorry excuse for an office suite into the trash whenever I find them. Definitely falls into the 'worse than useless' category.
The installer asked for that serial number, which I carefully cut and pasted from the email. Within a few minutes, the office icons are in the dock, and the updater was asking if I wanted to grab the latest patches.
All good so far.
Then I launch Word, and it asks again for my serial number. Again, I copy and paste it into the dialog, expecting that, for whatever reason, I'll have to do this for Excel and PowerPoint, too. Except that the serial number isn't accepted. I try again. Again, failure. Exasperated, I opt for the clean uninstall option using the 'Remove Office' application installed as part of the suite. It doesn't work: 'Remove Office' spends literally hours searching for versions of office to remove, and when I eventually cancel, it tells me that it couldn't find any. What, not even in the bog-standard '/Applications/Microsoft Office 2008' folder? Why would it ever have looked there?
So I manually uninstall it, removing all of the fragments that the damn procedure scattered throughout
~/Library
and /Library
. Reinstalling yields exactly the same procedure.The first contact with customer support yields a different download link, and them re-sending me the original key. But the file from the alternate link is identical to the original. So, with the same key and the same file, what happens? That's right: another key validation failure.
So now it's back to customer support, this time providing them with a hardware signature, presumably so that they can generate a new key that's completely locked down to my laptop.
This whole experience has left me wondering why I spent any money on the product at all. The automatic assumption of "it's a copy!" has further damaged my already tarnished opinion of Microsoft as a company, and the textbook incompetence of their first-contact customer support is a frustrating waste of my time.
Really, I wouldn't (and haven't) had this problem with OpenOffice, which has the added bonus of being absolutely free. The only reason I want Office is just so that I can open documents sent to me without losing content, and also to produce Word files that I know will open properly for those ridiculous agencies that accept it as their only CV format.
Edit: 3rd December, 2009
Eventually, I got an obscenely long product activation code from Microsoft. It didn't work. The 'Remove Office' utility still doesn't work, so I'm left tossing shattered fragments of this sorry excuse for an office suite into the trash whenever I find them. Definitely falls into the 'worse than useless' category.
Wednesday 4 November 2009
orgtbl-mode
I've been using org-mode for some time for basic task planning, and occasionally for tracking time, but it always seems to have that bit more to learn. One of the really cool features with
Carsten Dominik's org-mode presentation at Google intrigued me with the possibility of using that table editor in non-org files, but I lacked the inclination to investigate further until I stumbled across this video showing the feature off by generating an HTML table from an
If you're still interested, you should watch that now. I'll do my best to explain how it's done when you're finished.
....
Creating an HTML table with
Quite simply,
You specify where the org table is going to appear with a pair of markers.
The first character there ('#') should be whichever comment character makes sense for your major mode. So you'll use '//' for Java, C and C++, '%' for LaTeX,
The
Next, you'll define (somewhere in the document) a section for you to specify the org table. It helps here if your language has a block-comment structure (like the above HTML style comment, or
(The reason I advise about block comments is that I was seeing problems generating the table unless
The
Once you've got the table above, with the cursor in the field where you want the table to go, type
Here, you can enter spreadsheet-like formulae. I'm still learning here, but a good answer to the prompt is:
...which will neatly place a total for the Cost field in that slot (and also place a formula definition under the table), so that it looks like:
On the
When you want to change your HTML table, just change the
org-mode
is its table editor: it makes it absolutely trivial to create and manipulate ASCII tables in plain text files (see here for more).Carsten Dominik's org-mode presentation at Google intrigued me with the possibility of using that table editor in non-org files, but I lacked the inclination to investigate further until I stumbled across this video showing the feature off by generating an HTML table from an
org-mode
table source.If you're still interested, you should watch that now. I'll do my best to explain how it's done when you're finished.
....
Creating an HTML table with orgtbl-mode
Quite simply,
orgtbl-mode
is an Emacs minor mode that embeds into whichever major mode you happen to be working in. Within the context of an org-mode
table within that document, org-mode
table commands apply. Elsewhere in the document, your major mode (java-mode
, cperl-mode
, etc.) is in charge.The Insertion Point for the HTML Table
You specify where the org table is going to appear with a pair of markers.
#BEGIN RECEIVE ORGTBL books
#END RECEIVE ORGTBL books
The first character there ('#') should be whichever comment character makes sense for your major mode. So you'll use '//' for Java, C and C++, '%' for LaTeX,
<!-- ... -->
for HTML and XML, etc. The reason for this simple: you don't want these markers polluting the textual sources that you're composing in whichever major mode you're in.The orgtbl
Source Table
Next, you'll define (somewhere in the document) a section for you to specify the org table. It helps here if your language has a block-comment structure (like the above HTML style comment, or
/* ... */
for C, or the comment
package for LaTeX).
<!--
#+ORGTBL: SEND books orgtbl-to-html
| Title | Cost |
|---------------------+-------|
| Programming Clojure | 30.00 |
| Programming Perl | 25.00 |
|---------------------+-------|
| TOTAL | |
-->
(The reason I advise about block comments is that I was seeing problems generating the table unless
#+ORGTBL:
was a the beginning of the line).The
#+ORGTBL
line includes the SEND
command (the counterpart to the RECEIVE
in the previous markup); the table name (to match up with the appropriate RECEIVE
: there could be many); and the translation function that takes the org-mode
table and produces the HTML, LaTeX, or whatever output.Once you've got the table above, with the cursor in the field where you want the table to go, type
C-u C-c =
. At this point, focus will be transferred to the minibuffer, which will look like this:
Field formula B4=
Here, you can enter spreadsheet-like formulae. I'm still learning here, but a good answer to the prompt is:
vsum(@I..@II)
...which will neatly place a total for the Cost field in that slot (and also place a formula definition under the table), so that it looks like:
<!--
#+ORGTBL: SEND books orgtbl-to-html
| Book | Cost |
|---------------------+-------|
| Programming Clojure | 20.00 |
| Programming Perl | 25.00 |
|---------------------+-------|
| TOTAL | 45. |
#+TBLFM: @4$2=vsum(@I..@II)
-->
@4$2
is the specifier for the cell where the total is to appear (row 4, column 2). Formulae are automatically updated as you move columns around, insert new ones, add new rows, etc., but when you're done editing, hit C-c C-c
on the formula line to update the table values.Generating the HTML Table
On the
#+ORGTBL:
line, hit C-c C-c
, and the HTML for your table will automatically be generated from the orgtbl
source and be inserted between the BEGIN RECEIVE
and END RECEIVE
markers.Updating the HTML Table
When you want to change your HTML table, just change the
org-mode
table, update the formula with C-c C-c
on the formula line, and then regenerate with the same keystrokes on the SEND
line, and your HTML table will be refreshed with the new state.
Sunday 18 October 2009
Goodbye lad.
It's been less than five months since I buried Garfield under his favourite tree at my mother's. Today, I gave him company, and set Jake's ashes down next to his.
Take care of the pup for me, old man.
Take care of the pup for me, old man.
Wednesday 14 October 2009
The Religious Persecution Complex
In the best traditions of throwing a tantrum to get their way, the Mormons (I keep typing 'morons') in California are upset because public support for their suppression of gay rights is waning. Enshrining the rights of those who think differently in law is a threat to their religious freedom, they whine. Furthermore, they have the audacity to portray themselves as the persecuted minority, "similar to the intimidation of Southern blacks during the civil rights movement".
Every time I try to think of an appropriately outraged response, my brain just shuts down. It simply can't deal with that level of stupidity.
Religions that kick and scream whenever they don't get their way are a common sight: the Catholics are upset because they were mocked by US comedienne, Sarah Silverman, who suggested that selling the Vatican and giving the proceeds to the poor would feed the hungry of the world. The Muslims get upset every time someone doesn't automatically yield to their demands for respect (seriously, why would a non-Muslim care about the traditions and taboos of Islam?) And then the Mormons, bat-shit crazy even by the high-bar set by standard Christianity, think that California legislation threatening to overturn their hate campaign is an attack on their religion.
I'm an atheist. I really don't care which gods or spirits or entities you worship. But when it infringes upon the rights of other people who wish to live their lives free of religiously-inspired bigotry, I start to have a problem. And this notion that every disagreement is an attack against your god or gods, or your freedom to practice religion: forget it. I don't have a problem with imaginary entities. But I do have a problem with stupidity, and if you think that your particular brand of stupidity is allowed because it's sanctioned by an invisible being, that doesn't give it any more weight.
In a close to this rant, you might be thinking of some suitably boring tirade that involves either quotes from whichever book you believe is the One, True, Unaltered Word of God, or threats of violence or hellfire. Save your fingers. If I am wrong, and there really is a god, I'll happily answer to him/her/it when it's all over: the supreme creator of the universe doesn't need your assistance in eliminating me.
Every time I try to think of an appropriately outraged response, my brain just shuts down. It simply can't deal with that level of stupidity.
Religions that kick and scream whenever they don't get their way are a common sight: the Catholics are upset because they were mocked by US comedienne, Sarah Silverman, who suggested that selling the Vatican and giving the proceeds to the poor would feed the hungry of the world. The Muslims get upset every time someone doesn't automatically yield to their demands for respect (seriously, why would a non-Muslim care about the traditions and taboos of Islam?) And then the Mormons, bat-shit crazy even by the high-bar set by standard Christianity, think that California legislation threatening to overturn their hate campaign is an attack on their religion.
I'm an atheist. I really don't care which gods or spirits or entities you worship. But when it infringes upon the rights of other people who wish to live their lives free of religiously-inspired bigotry, I start to have a problem. And this notion that every disagreement is an attack against your god or gods, or your freedom to practice religion: forget it. I don't have a problem with imaginary entities. But I do have a problem with stupidity, and if you think that your particular brand of stupidity is allowed because it's sanctioned by an invisible being, that doesn't give it any more weight.
In a close to this rant, you might be thinking of some suitably boring tirade that involves either quotes from whichever book you believe is the One, True, Unaltered Word of God, or threats of violence or hellfire. Save your fingers. If I am wrong, and there really is a god, I'll happily answer to him/her/it when it's all over: the supreme creator of the universe doesn't need your assistance in eliminating me.
Tuesday 13 October 2009
Keyboard and Mouse Unresponsive in GDM
A fairly major upgrade of Xorg at the weekend left my Gentoo Linux box in a state where mouse and keyboard input no longer worked at the GDM login screen.
Nasty.
First port of call was
So, a version mismatch between driver modules and the updated X server.
The solution?
Followed by:
The downside? The keyboard wasn't working. So I had to issue these commands from my Macbook Pro after
If you're stuck without
Nasty.
First port of call was
/var/log/Xorg.0.log
, in which I could see some suspicious lines:
(II) LoadModule: "mouse"
(II) Loading /usr/lib/xorg/modules/input//mouse_drv.so
(II) Module mouse: vendor="X.Org Foundation"
compiled for 1.5.3, module version = 1.4.0
Module class: X.Org XInput Driver
ABI class: X.Org XInput driver, version 2.1
(EE) module ABI major version (2) doesn't match the server's version (4)
(II) UnloadModule: "mouse"
(II) Unloading /usr/lib/xorg/modules/input//mouse_drv.so
(EE) Failed to load module "mouse" (module requirement mismatch, 0)
(EE) No input driver matching `mouse'
(II) LoadModule: "kbd"
(II) Loading /usr/lib/xorg/modules/input//kbd_drv.so
(II) Module kbd: vendor="X.Org Foundation"
compiled for 1.5.3, module version = 1.3.2
Module class: X.Org XInput Driver
ABI class: X.Org XInput driver, version 2.1
(EE) module ABI major version (2) doesn't match the server's version (4)
(II) UnloadModule: "kbd"
(II) Unloading /usr/lib/xorg/modules/input//kbd_drv.so
(EE) Failed to load module "kbd" (module requirement mismatch, 0)
(EE) No input driver matching `kbd'
So, a version mismatch between driver modules and the updated X server.
The solution?
emerge -1 x11-drivers/xf86-input-{evdev,keyboard,mouse}
Followed by:
/etc/init.d/xdm restart
The downside? The keyboard wasn't working. So I had to issue these commands from my Macbook Pro after
ssh
ing into my Linux box.If you're stuck without
ssh
access, hold down Alt-SysRq
and then press r
and then e
to kill the X server, then Alt-Shift-F1
to get back to a virtual terminal, and then log in as root. Not pretty, but I'd guess that it'd work.
Monday 12 October 2009
Avoiding e-Readers
There has been a great deal of hype about electronic books from Sony and Amazon but, as much as I like shiny new gadgets, I just can't get excited over these offerings. Fundamentally, I like replacements for old ideas to be better than their predecessors in every measurable way, but e-readers are more give-and-take.
On the plus side, you've almost eliminated the weight/bulk factor. You can buy books on-the-go, and have them almost instantly available for reading. The Kindle offers free Wikipedia access, which is handy for settling those pub debates. The text-to-speech feature could occasionally be useful if you just want to stare out of the train window for a while on the way to work rather than squinting tired eyes at text on a page.
But then, the negatives are huge:
Maybe that's it. The only books I'd be happy to have on an electronic reader would be ones that I don't care about; ones that are effectively disposable. But disposable and expensive don't sit well together.
How can e-readers get it right? They have to be better than 'old-style' books, and that involves both a significant drop in price and the ditching of the DRM millstone. Some unique opportunities arise in the case of corrections and updated versions, as well. Does your technical book have a new, updated edition? Maybe you could get it cheaper because you owned the previous one. Maybe you can wirelessly submit feedback or corrections back to the author/publisher. When you're part way through a series, waiting for the author to publish the next volume, wouldn't it be useful say, "I'm interested in this series" and automatically be notified when it's available?
Despite my dislike of the current state of affairs, there's lots of potential for the platform, but only if some draconian notions of control relax their grip on its throat.
On the plus side, you've almost eliminated the weight/bulk factor. You can buy books on-the-go, and have them almost instantly available for reading. The Kindle offers free Wikipedia access, which is handy for settling those pub debates. The text-to-speech feature could occasionally be useful if you just want to stare out of the train window for a while on the way to work rather than squinting tired eyes at text on a page.
But then, the negatives are huge:
- Sure, the battery life is impressive, but it's still a battery life that dead-tree just doesn't have to worry about.
- It's no big deal if you lose a book or drop it in the pool by mistake on holiday, but doing the same with these pricey devices is a blow, both from the perspective that it cost so damn much and that your entire holiday reading was on it.
- Digital Rights Management. This is wrong on so many levels it's hard to know where to start. Want to lend a book to your friend? Not allowed. Want to view it on a broader range of devices than the publisher of your book wants? Not allowed. Have you inadvertently bought something that shouldn't have been sold to you? No problem (for the publishers) -- your reseller can remotely remove the book from your library (the fact that this was done with George Orwell's 1984 is deliciously ironic).
- On a DRM-related note, you can only buy books for your reader from online stores affiliated with the manufacturer of your device, and it's not too difficult to envision a music industry-like scenario where a given title is only available on a given platform.
- (and this is the part that makes me think I'm getting old...) it just doesn't feel good. Some throw-away books I don't really care about, but others, I want to feel the paper under my fingers to get a feel for it (I have a copy of the Smalltalk 'Blue Book', printed in 1983, that exudes that fuzzy feeling)
Maybe that's it. The only books I'd be happy to have on an electronic reader would be ones that I don't care about; ones that are effectively disposable. But disposable and expensive don't sit well together.
How can e-readers get it right? They have to be better than 'old-style' books, and that involves both a significant drop in price and the ditching of the DRM millstone. Some unique opportunities arise in the case of corrections and updated versions, as well. Does your technical book have a new, updated edition? Maybe you could get it cheaper because you owned the previous one. Maybe you can wirelessly submit feedback or corrections back to the author/publisher. When you're part way through a series, waiting for the author to publish the next volume, wouldn't it be useful say, "I'm interested in this series" and automatically be notified when it's available?
Despite my dislike of the current state of affairs, there's lots of potential for the platform, but only if some draconian notions of control relax their grip on its throat.
Saturday 10 October 2009
Thank you, US Airways!
I spent some work-time in southern California about a month ago, involving a long day of travel from Glasgow to Philadelphia, and then from there to San Diego. With my entertainment loaded onto my iPod Touch, I was prepared.
At least, until I left it on the plane in Philadelphia on the way to the states.
Having realised almost immediately that I'd left it, I asked a US Airways representative if they could help out but, without a procedure to follow, there was nothing that could be done to get my iPod from the plane. It was mildly frustrating, but when it came down to it, it was my fault for leaving it! Still, I left my details in the hopes that it might turn up later.
Back home two weeks later, I got a call from US Airways asking if I had lost something, and when I confirmed the what and the when was told that they could take my postal address to return it to me. It took a while to get it across the Atlantic, but it finally arrived yesterday.
So thank you US Airways!
At least, until I left it on the plane in Philadelphia on the way to the states.
Having realised almost immediately that I'd left it, I asked a US Airways representative if they could help out but, without a procedure to follow, there was nothing that could be done to get my iPod from the plane. It was mildly frustrating, but when it came down to it, it was my fault for leaving it! Still, I left my details in the hopes that it might turn up later.
Back home two weeks later, I got a call from US Airways asking if I had lost something, and when I confirmed the what and the when was told that they could take my postal address to return it to me. It took a while to get it across the Atlantic, but it finally arrived yesterday.
So thank you US Airways!
Wednesday 7 October 2009
Regular Expressions in Erlang
My favourite general-purpose language is still Perl. It's not that it's the best at everything, but that it's good enough for just about anything (and better than most).
Probably as a side-effect of what many would consider this misguided affection, one of the first things I look at in a new language is its regular expression support. Mostly, this is a no-brainer: Perl 5 is the de-facto standard, and its regex engine is available as a library to link into your favourite C-binding language.
So I was disappointed initially when looking for advice on regular expressions in Erlang. The first hit on Google (at the time of this writing) is here, and indicates the
I was disappointed. I'm going to re-write my CTCS clone in Erlang, and it makes quite a lot of use of regular expressions to extract information from the various messages
But then, salvation! The re module, an (almost) mapping onto PCRE!
Suddenly, extracting groups from a string was easy:
The escaped backslashes are ugly, but the direct capturing of groups as a list is pretty sweet.
Probably as a side-effect of what many would consider this misguided affection, one of the first things I look at in a new language is its regular expression support. Mostly, this is a no-brainer: Perl 5 is the de-facto standard, and its regex engine is available as a library to link into your favourite C-binding language.
So I was disappointed initially when looking for advice on regular expressions in Erlang. The first hit on Google (at the time of this writing) is here, and indicates the
regex
module. Unfortunately, that module is really, really limited compared to the mind-bending flexibility of the Perl 5 engine.I was disappointed. I'm going to re-write my CTCS clone in Erlang, and it makes quite a lot of use of regular expressions to extract information from the various messages
ctorrent
sends throughout its lifetime. In this case, it was a bit of a show-stopper: there's absolutely no point in doing the re-write if it's going to be more difficult to create the new version than improve the old.But then, salvation! The re module, an (almost) mapping onto PCRE!
Suddenly, extracting groups from a string was easy:
regex_example() ->
Name = "Danny Woods",
{ match, [ Forename, Surname ] } =
re:run(Name, "(\\w+)\\s+(\\w+)", [{capture,all_but_first,list}]),
{ Forename, Surname }.
The escaped backslashes are ugly, but the direct capturing of groups as a list is pretty sweet.
Monday 5 October 2009
Another Sad Day
It's been almost six months since Garfield died. Every Garfield needs his Odie, and my Garfield's was Jake. We had to take Jake to his final visit to the vet's today: he was diagnosed with aggressive terminal cancer three weeks ago and given six months to live, but a turn for the worse last week pulled that date back to today. He was sixteen.
We got Jake as a pup in 1993, and he kept those puppy looks right up until the end. I visited him at my mother's yesterday, and gave him a hug and touched our foreheads together like we've done since we were both young. He was the best of dogs.
Sleep peacefully boy.
I will miss you.
We got Jake as a pup in 1993, and he kept those puppy looks right up until the end. I visited him at my mother's yesterday, and gave him a hug and touched our foreheads together like we've done since we were both young. He was the best of dogs.
Sleep peacefully boy.
I will miss you.
Wednesday 9 September 2009
Wastage
I stumbled across this amusing video of a cat drinking by dunking its head under a flowing tap. Even this light-hearted fun had some people in the comments bemoaning the wasted water of a tap being on for a few minutes of recorded video.
Then, with the subject of wasted water fresh in my mind, I switched tabs to this story (also from Reddit), and almost snorted my tea.
It was funny at the time.
Then, with the subject of wasted water fresh in my mind, I switched tabs to this story (also from Reddit), and almost snorted my tea.
It was funny at the time.
Monday 7 September 2009
Building MPlayer from SVN on Mac OS X Snow Leopard
I use the command line
But today I upgraded to Snow Leopard, distinctive from it's predecessor in being a truly native 64-bit operating system. And my SVN checkout of MPlayer stopped working, giving me a green video window and needing some effort to kill. So, to get MPlayer working, you're going to need the following:
...and, of course, your MPlayer sources. If you want FAAC support, get libfaac and build/install it using the same procedure as for
Now, you should have a native 64-bit
Success!
mplayer
on Mac OS. The only competition for this superb video player is VLC, but sometimes I like to just quickly launch something without a bulky UI getting in the way.But today I upgraded to Snow Leopard, distinctive from it's predecessor in being a truly native 64-bit operating system. And my SVN checkout of MPlayer stopped working, giving me a green video window and needing some effort to kill. So, to get MPlayer working, you're going to need the following:
...and, of course, your MPlayer sources. If you want FAAC support, get libfaac and build/install it using the same procedure as for
freetype
below.- Install the Apple Developer Tools using the graphical installer.
- Build
libpng
andlibjpeg
: ./configure --arch=x86_64
make
sudo make install
- Build
freetype
: ./configure --target=x86_64-Darwin
make
sudo make install
- Build
mplayer
: ./configure --target=x86_64-Darwin --enable-menu
make
sudo make install
Now, you should have a native 64-bit
/usr/local/bin/mplayer
:
danny@mirror ~ [1] % file /usr/local/bin/mplayer
/usr/local/bin/mplayer: Mach-O 64-bit executable x86_64
danny@mirror ~ [2] %
Success!
Thursday 27 August 2009
Why I Write My CV in LaTeX
For many people, even technical people, LaTeX is a relic. I recently had a chat with some colleagues who were looking to write documentation in a format that could be easily converted into other formats (PDF, HTML, etc.), which could maintain its own indexing and cross-referencing easily, and which would, of course, look good. I mentioned LaTeX and got some amused eye-rolling and comments about how no-one uses that anymore.
Even if that were true, I maintain my CV in LaTeX. Why? Simple:
Sure, it takes more time than knocking something together in Microsoft Word or OpenOffice Writer, but there's a distinct satisfaction to be enjoyed from tweaking and crafting something until it's exactly how you want it to be.
Definitely not for everyone, but if you've gone to all the trouble to develop a skill-set that you're proud of, you may as well take the effort to show it off in the best way possible!
Even if that were true, I maintain my CV in LaTeX. Why? Simple:
- It enables absolute control over what gets rendered, and where, on the page.
- There's MiKTeX for Windows, TeXShop for Mac and TeX Live for Linux. In other words, it's available for your favourite platform.
- The source files are plain text. This makes for easy versioning using your favourite version control system, and thereafter, comparing the differences between any two versions.
- Producing good looking PDFs is simple with
pdflatex
.
Sure, it takes more time than knocking something together in Microsoft Word or OpenOffice Writer, but there's a distinct satisfaction to be enjoyed from tweaking and crafting something until it's exactly how you want it to be.
Definitely not for everyone, but if you've gone to all the trouble to develop a skill-set that you're proud of, you may as well take the effort to show it off in the best way possible!
Monday 10 August 2009
ScotRail is Stuck in the 80's
My wife and I went on a shopping trip to Edinburgh yesterday, involving a train journey to Glasgow Central station, a short walk to Queen Street station, and another train ride to Edinburgh.
Now, for some truly bizarre reason, it's cheaper to get a return ticket to Central, and then a separate return ticket for the Glasgow–Edinburgh link, than it is to get a return from East Kilbride to Edinburgh. Go figure. But we skip over that little bit of insanity, and ask for one return ticket from East Kilbride to Glasgow Central, and two returns from Glasgow to Edinburgh. See, I work in Glasgow, so already have a season ticket. With a queue building up, we pay, bundle our tickets together and get on the train.
As it starts to pull away, we look through the tickets to see that the ticket office in East Kilbride has issued us with two return tickets for the East Kilbride to Glasgow link. Mildly irritating, but no big issue: we can just get a refund on the tickets that we're not going to use. We ask the conductor on the train, and get the response that he can't issue a refund, because he'd be out of pocket. We'd need to get the station at East Kilbride to do that, or possibly get it done at the ticket office at Central. He's a ScotRail ticket conductor, and these are ScotRail tickets, and he can't... Something in my head implodes from the stupidity. I thank him for his 'help' and he goes away.
Once in Central Station, we're told that we can't get the refund there: we'd have to get it from East Kilbride. The reasoning is that they didn't issue the physical tickets, so the ticket number wouldn't be on their system, only in the East Kilbride one. The most he can do is give us a form to fill out.
Now, this is our outbound journey. It's 13:00, and our home station closes at 17:00. Chances of making it back from Edinburgh in time? Approximately zero.
Both stations at East Kilbride and Glasgow Central are run by ScotRail. The conductor on the train is a ScotRail employee. It's a mere ten miles between the town and the city, and this is 2009. And ScotRail still don't have the facilities to provide refunds, except at the issuing station?
Embarrassing.
Now, for some truly bizarre reason, it's cheaper to get a return ticket to Central, and then a separate return ticket for the Glasgow–Edinburgh link, than it is to get a return from East Kilbride to Edinburgh. Go figure. But we skip over that little bit of insanity, and ask for one return ticket from East Kilbride to Glasgow Central, and two returns from Glasgow to Edinburgh. See, I work in Glasgow, so already have a season ticket. With a queue building up, we pay, bundle our tickets together and get on the train.
As it starts to pull away, we look through the tickets to see that the ticket office in East Kilbride has issued us with two return tickets for the East Kilbride to Glasgow link. Mildly irritating, but no big issue: we can just get a refund on the tickets that we're not going to use. We ask the conductor on the train, and get the response that he can't issue a refund, because he'd be out of pocket. We'd need to get the station at East Kilbride to do that, or possibly get it done at the ticket office at Central. He's a ScotRail ticket conductor, and these are ScotRail tickets, and he can't... Something in my head implodes from the stupidity. I thank him for his 'help' and he goes away.
Once in Central Station, we're told that we can't get the refund there: we'd have to get it from East Kilbride. The reasoning is that they didn't issue the physical tickets, so the ticket number wouldn't be on their system, only in the East Kilbride one. The most he can do is give us a form to fill out.
Now, this is our outbound journey. It's 13:00, and our home station closes at 17:00. Chances of making it back from Edinburgh in time? Approximately zero.
Both stations at East Kilbride and Glasgow Central are run by ScotRail. The conductor on the train is a ScotRail employee. It's a mere ten miles between the town and the city, and this is 2009. And ScotRail still don't have the facilities to provide refunds, except at the issuing station?
Embarrassing.
Sunday 21 June 2009
Other Uses for the FreeBSD Daemon?
A friend of mine has been visiting from the Czech Republic over the weekend, an infrequent thing that inevitably results in visits to East Kilbride's pubs and nightclubs that we'll later regret. Some special strangeness last night came in the form of a sex-toy vending machine in the male toilets in Shenanigans in East Kilbride's town centre. Who would have thought that the FreeBSD daemon (right) was moonlighting as a purveyor of kinky goods?
Saturday 20 June 2009
A Minimal Java Applet in Clojure
Clojure's ahead-of-time compilation features allow it to operate seamlessly with Java classes, but one quirk that I noticed when writing a Java applet was that the security environment in applets tends not to be too happy with reflective method calls. Overriding an applet's
The problem here is that the
This can be overcome with type-hints, letting the compiler know what the given 'g' is so that it can generate the code for a direct call, keeping the applet security manager happy.
A minimal applet might therefore look something like this:
Note the type-hint,
If you're interested, the associated HTML file to run this in
Notice that
paint
method like this is therefore a no-go:
(defn -paint [instance g]
(.drawString g "Hello from Clojure!" 50 50))
The problem here is that the
.drawString
method is reflectively invoked against the given Graphics
object.This can be overcome with type-hints, letting the compiler know what the given 'g' is so that it can generate the code for a direct call, keeping the applet security manager happy.
A minimal applet might therefore look something like this:
(ns djw
(:import (java.awt Graphics2D))
(:gen-class
:name DJW
:extends java.applet.Applet))
(defn -paint [instance #^Graphics2D g]
(.drawString g "Hello from Clojure!" 50 50))
Note the type-hint,
#^Graphics2D
in the signature of the paint
function.If you're interested, the associated HTML file to run this in
appletviewer
or in a browser looks like this:
<object classid="java:DJW.class" type="application/x-java-applet" archive="applet.jar,clojure.jar" width="200" height="100" codebase=".">
</object>
Notice that
clojure.jar
is in the archive
field of the object
tag, along with applet.jar
(containing the precompiled class
files for the Clojure applet).
Tuesday 16 June 2009
UK to get 'World Class' Broadband?
I had to laugh at this article on the BBC: a new 50p tax on fixed lines in the UK to enable 'digital Britain' providing, among other things (up to) 50Mbps by 2017. A special highlight from our wonderfully inept Prime Minister:
"Britain is going to lead the world. This is us taking the next step into the future to being the digital capital of the world. -- Gordon Brown
Step back a bit... having 50Mbps broadband by 2017 is going to make us the 'the digital capital of the world'? Is he aware that South Korea is aiming for 1Gbps by 2012?!? (in case you're wondering, that's 20 times faster, five years earlier). Japan has 100Mbps now.
I sometimes wonder exactly which planet Gordon Brown is living on.
"Britain is going to lead the world. This is us taking the next step into the future to being the digital capital of the world. -- Gordon Brown
Step back a bit... having 50Mbps broadband by 2017 is going to make us the 'the digital capital of the world'? Is he aware that South Korea is aiming for 1Gbps by 2012?!? (in case you're wondering, that's 20 times faster, five years earlier). Japan has 100Mbps now.
I sometimes wonder exactly which planet Gordon Brown is living on.
Monday 8 June 2009
Clojure: First Steps into Compilation and Class Generation
I've never quite understood class generation in Clojure. The documentation is a little terse, and working examples seem to be hard to come by. So here's my walkthrough.
First, I'm assuming that we'll be creating Clojure objects from Java, calling methods on those Clojure objects from bog-standard Java code. This, I think, is the most likely scenario for the budding Clojure hacker who wants to write part of an existing Java system in Clojure.
First, you'll need to have obtained your
Next, in whatever environment you're using, add the full path to
In the directory you're building from, create the directory structure for your package, Java-style. I'm going to be putting my Clojure code in
In
This leaves two functions to implement in the Clojure code:
Note the dash in front of the function names (this is the default
Compiling the Clojure Code to
Create a directory called
Since
And that's it.
This has been a lot of effort so far. But the good news that this allows Java to talk to Clojure-generated class files without having any idea that Clojure was the source language. The Java code in
And that's it.
The Java can be compiled just as with any other. Remember that the
With
By now, you've defined a class definition in Clojure, prompted instance creation from Java, initialised the object in Clojure, handed it to a thread in Java, and printed out a message in Clojure. That's a fair amount of bouncing around, especially for such a trivial example, but hopefully you've found it useful for what you need.
First, I'm assuming that we'll be creating Clojure objects from Java, calling methods on those Clojure objects from bog-standard Java code. This, I think, is the most likely scenario for the budding Clojure hacker who wants to write part of an existing Java system in Clojure.
Environment
First, you'll need to have obtained your
clojure.jar
, either from the snapshots here, or from the Subversion repository and building yourself.Next, in whatever environment you're using, add the full path to
clojure.jar
to your CLASSPATH
. Also add ".
" and "classes
" (both relative paths, not absolute). You'll see why later.The Clojure Code
In the directory you're building from, create the directory structure for your package, Java-style. I'm going to be putting my Clojure code in
org/djw/sample.clj
, so org/djw
will have to exist beforehand.In
sample.clj
, most of the magic's in the namespace declaration.
(ns org.djw.sample ;; 1
(:import (javax.swing JFrame)) ;; 2
(:gen-class ;; 3
:name org.djw.DJW ;; 4
:extends javax.swing.JFrame ;; 5
:constructors {[String] [String]} ;; 6
:init initialise ;; 7
:implements [Runnable] ;; 8
:state fiddlyBits)) ;; 9
- 1. This is your Clojure namespace. It doesn't mean much from Java-land.
- 2. You can import any classes used by your Clojure file here.
- 3.
:gen-class
allows the compiler to generate Java bytecode files. - 4. This is the fully-qualified name of the Java class you want to emit. The Clojure
compile
directive generates quite a few class files that your Java code doesn't need to know about: the one in:name
is an exception: it's what your Java code willimport
- 5. If your class subclasses something other than
Object
, name it here, as normal - 6. I want to have a
String
constructor that calls theString
constructor inJFrame
. - 7. The initialiser function for new instances. I lack imagination, so have called it
initialise
here. - 8. Horribly, my new class is both a GUI element (a
JFrame
) and aRunnable
. Since you can implement many interfaces, this appears in a literal vector. - 9. The
initialise
function gets to attach some Clojure-side state to the object being created (you'll see that in a bit). The:state
specifier creates a final instance method to access that state from Java.
This leaves two functions to implement in the Clojure code:
initialise
and run
(the latter required by Runnable
).
(defn -initialise [message]
[[message] (ref {:message message})])
(defn -run [instance]
(let [message (:message @(.fiddlyBits instance))]
(println message)))
Note the dash in front of the function names (this is the default
:prefix
from :gen-class
). Also, note that the function specified by :init
in :gen-class
didn't get an instance to play with, whereas run
(an instance method), does. This instance is the object that the method was invoked against, effectively this
from Java.initialise
has to return a vector of two elements: the first consists of the arguments to pass to the superclass constructor. The second is the state that should be attached on a per-instance basis.run
is a Runnable.run
implementation, and just prints out the message that the instance was created with. Note that the state is accessed with (.fiddlyBits instance)
, returning the ref
-wrapped Clojure map, dereferenced with '@' as normal and with the :message
key used to look up the associated value.Compiling the Clojure Code to .class
Files
Create a directory called
classes/[package-name]
, in my case classes/org/djw
. Why classes
? Well, that's what Clojure's global *compile-path*
variable is by default, so is the root where the compile
command emits .class
files. That's why you added it to your CLASSPATH
above (you did do that, didn't you..?)Since
clojure.jar
is on your CLASSPATH
, you can start a Clojure REPL with just 'java clojure.main
'. You can now compile the ./org/djw/sample.clj
like this:
danny@mirror Desktop [10] % java clojure.main
Clojure 1.1.0-alpha-SNAPSHOT
user=> (compile 'org.djw.sample)
org.djw.sample
user=>
And that's it.
classes/org/djw
will now contain org.djw.DJW
(as per gen-class
's :name
field). It'll also contain a bunch of other .class
files: don't delete these, they are required! Clojure creates a .class
file per function (including unnamed functions), as well as another for initialisation.The Java Code
This has been a lot of effort so far. But the good news that this allows Java to talk to Clojure-generated class files without having any idea that Clojure was the source language. The Java code in
Test.java
to use it might look like this:
import org.djw.DJW;
public class Test
{
public static void main(String [] args)
{
DJW djw = new DJW("Hello");
new Thread(djw).start();
}
}
And that's it.
DJW
is the class name, and a new instance is obtained just as with any other class. It faithfully implements Runnable
, as specified in the Clojure code, and it can be duly run from a Thread
created from Java.Compiling and Running the Java Code
The Java can be compiled just as with any other. Remember that the
classes
folder must be in your CLASSPATH
for javac
to see the definition of DJW
.With
clojure.jar
, '.'
and classes
in your CLASSPATH
, you can just run as you'd expect:
danny@mirror Desktop [30] % javac Test.java
danny@mirror Desktop [31] % java Test
Hello
danny@mirror Desktop [32] %
By now, you've defined a class definition in Clojure, prompted instance creation from Java, initialised the object in Clojure, handed it to a thread in Java, and printed out a message in Clojure. That's a fair amount of bouncing around, especially for such a trivial example, but hopefully you've found it useful for what you need.
Sunday 24 May 2009
Goodbye, Old Man
I buried Garfield's ashes today, under his favourite tree (claw marks included) at my mother's house. He went in the best way possible, but I've shed more than a few tears since his death.
He did well.
Rest in peace, old man.
He did well.
Rest in peace, old man.
Clojure on FreeBSD 7
Getting a native Java on FreeBSD 7 is not straightforward. It involves going through the instructions at freebsd.org/java and manually fetching packages that have generally since been superseded by point-versions (which you can't use or the ports system won't install them). Once they're all in
Finally, success:
/usr/ports/distfiles
, a make install
in /usr/ports/java/jdk16
will, several hours of hard compilation later, enable you to checkout a fresh copy of Clojure.Finally, success:
Friday 22 May 2009
Switching between Header and Implementation Files with Emacs
I often find myself working with C or C++ code that follows the pattern of parallel source/include directories, probably with lots of layers in between (e.g.
If the counterpart file is missing, it won't be created unless the function is invoked with a prefix argument (
There's probably some extension to CC mode or the like that already does something like this, but in the absence of reading the manual, this works well for me.
project/include/some-controller/some-aspect/something.h
and project/src/some-controller/some-aspect/something.cpp
). Quickly switching between both saves on keystrokes and sanity, so I wrote this little chunk of elisp, bound to ctrl-alt-g
(for no particular reason other than the combination is easy to mash):
(defun djw-c-toggle-impl-header-view (create-if-nonexistent)
(interactive "P")
(let* ((mode (if (string-equal (file-name-extension (buffer-file-name))
"h")
:switch-to-implementation :switch-to-header))
(toggle-tags (list (cons "/Include/" "/Src/")
(cons "/include/" "/src/")))
(from-fn (if (eq mode :switch-to-implementation)
#'car #'cdr))
(to-fn (if (eq mode :switch-to-implementation)
#'cdr #'car))
(source-file-name (buffer-file-name))
(case-fold-search nil)) ;; I want case-sensitive matching throughout this block.
(dolist (pair toggle-tags)
(setf source-file-name (replace-regexp-in-string (funcall from-fn pair)
(funcall to-fn pair)
source-file-name)))
(setf source-file-name
(replace-regexp-in-string
(if (eq mode :switch-to-implementation)
"h\$" "cpp\$")
(if (eq mode :switch-to-implementation)
"cpp" "h")
source-file-name))
(message (format "Looking for %s" source-file-name))
(if (or (file-exists-p source-file-name)
create-if-nonexistent)
(find-file source-file-name)
(message (format "Can't find '%s'" source-file-name)))))
If the counterpart file is missing, it won't be created unless the function is invoked with a prefix argument (
C-u C-M-g
).There's probably some extension to CC mode or the like that already does something like this, but in the absence of reading the manual, this works well for me.
Wednesday 13 May 2009
Sad Day
My cat died today.
His name was Garfield, and he was 19. I got him as a kitten when I was 13. I came downstairs this morning to find him on his favourite cushion on the couch; it was the only time that he didn't raise his head to meow/croak back at me.
He had a good life. His last day was spent sunning himself in the back garden; his last meal was his favourite roast chicken; and last night, when I was working at my computer, he came up for a cuddle and a purr.
As the vet said to me a few months ago, "You don't last that long without good bits". Garfield was made with the best.
Goodbye, old man.
His name was Garfield, and he was 19. I got him as a kitten when I was 13. I came downstairs this morning to find him on his favourite cushion on the couch; it was the only time that he didn't raise his head to meow/croak back at me.
He had a good life. His last day was spent sunning himself in the back garden; his last meal was his favourite roast chicken; and last night, when I was working at my computer, he came up for a cuddle and a purr.
As the vet said to me a few months ago, "You don't last that long without good bits". Garfield was made with the best.
Goodbye, old man.
Tuesday 28 April 2009
Setting Colours with JavaScript
Something I thought would be easy turns out to have lots of different answers, none of which are particularly convenient: how do you compute a colour value in JavaScript, and then update some element on the page to use that colour?
Most solutions I've seen involve some kind of hard-coded hexadecimal conversion table, which is more than a little clunky. So here's another approach. Here, the value used to compute the colours is a ratio, with a value from 0.0 and up (normally in the 0.0 to 2.0 region, but theoretically unbounded at the top). I want to use red to indicate a ratio of 0.0, green for ratios of 2.0 or more, and a smooth range of colours in between.
So, onto th JavaScript (yes, I have the ratio conveniently in a hash table):
The magic is in knowing that colours are typically represented as a red-green-blue triplet, with a byte in each channel (range 0-255). Blue doesn't feature in our range, so it's always zero. So, assembling the necessary triplet is just a matter of getting the clamped value for the right fields, and then OR-ing them together in a bitwise fashion after putting the red and green values in the correct place with the appropriate shifting. Then the
Most solutions I've seen involve some kind of hard-coded hexadecimal conversion table, which is more than a little clunky. So here's another approach. Here, the value used to compute the colours is a ratio, with a value from 0.0 and up (normally in the 0.0 to 2.0 region, but theoretically unbounded at the top). I want to use red to indicate a ratio of 0.0, green for ratios of 2.0 or more, and a smooth range of colours in between.
So, onto th JavaScript (yes, I have the ratio conveniently in a hash table):
var ratio = parseFloat(data["ratio"]);
var red = 255 - (ratio * 255); // calculate
var green = ratio * 128;
if (red < 0) red = 0; // clamp
if (green > 255) green = 255;
var colour = ((red << 16) | (green << 8) | 0).toString(16);
while (colour.length < 6) colour = '0' + colour;
document.getElementById("ratio").style.color = '#' + colour;
The magic is in knowing that colours are typically represented as a red-green-blue triplet, with a byte in each channel (range 0-255). Blue doesn't feature in our range, so it's always zero. So, assembling the necessary triplet is just a matter of getting the clamped value for the right fields, and then OR-ing them together in a bitwise fashion after putting the red and green values in the correct place with the appropriate shifting. Then the
toString(16)
returns that integer in base-16, which happens to be what makes sense in CSS land. Finally, pad the string out with zeroes on the left (the string needs to be six characters long, then slap on the '#' to have it make sense in CSS land and you're done.
Sunday 15 February 2009
Java Brain Damage
I don't generally buy into bashing Java, a trend all-too-fashionable among dynamic language programmers (and even C++ programmers, for some reason). But I just stumbled across something when writing Clojure that annoyed me: with a
Now, I remember the days whereadd made sense on a
JFrame
, getLayout
doesn't return the object set by a previous setLayout(aLayout)
call. Why? Because the layout actually gets set on the JFrame
's content pane, and that's not what getLayout
is looking at.Now, I remember the days where
JFrame.add
triggered a runtime exception: "Don't use add
! Use getContentPane().add()
instead!" or something like that. Somewhere down the line, someone appears to have had the great idea of changing that so that JFrame
by deferring to the content pane, but symmetry has been broken in the process. Nice!
Sunday 8 February 2009
Smalltalk: Why it's NOT Good
There's been a fair buzz in the Smalltalk blogs (here and here) about generally positive Smalltalk-applicability comments made on StackOverflow.
Now, I like to think of myself as a generalist, and a bit of a language geek. In my various jobs I've written a great deal of Smalltalk (VisualWorks), Java, C++, C and Perl, but have also spent a fair amount of fun time working with Squeak, SBCL Common Lisp, Clojure and Erlang. I'm a huge fan of Linux and Unix, and believe that the shell is a vastly under-used resource.
This is where I personally find Smalltalk to be annoying.
Don't get me wrong: it's an amazing language and development environment. With something like Eclipse, you write some arcane text files and--after running them through a compiler that you didn't really need the IDE for anyway--you have your application. With Smalltalk, the IDE is the application: you just iteratively refine it until it becomes what you want to ship. Awesome stuff.
But that same self-contained structure is what hurts it. When I write C, I tend to use Emacs, enhanced with a bunch of add-ons. I use the git source control system. I use
However, my choices don't stop them from using Eclipse, and the master source repository being Subversion. Furthermore, when I need to write Java or Perl, I can use the vast majority of the tool stack that I've accumulated just by switching buffers in Emacs. Simply put, I get to transfer most of my skills between tools.
In Smalltalk, it's all-or-nothing. Shell tools are useless on the image. You can't edit code in your favourite editor (well, you can, but unless you're using GNU Smalltalk, it's inconvenient to the point of being unusable). On top of that, you need a special, integrated source control system that's most likely dictated by your vendor, with migration being difficult-to-impossible.
For me, this single-mindedness it what stops Smalltalk being my favourite language (well, that and a single-threaded virtual machine and non-native GUI widgets). For now, Perl retains that crown (despite it being fashionable to bash it these days), with Clojure looking very, very interesting as a possible successor...
Now, I like to think of myself as a generalist, and a bit of a language geek. In my various jobs I've written a great deal of Smalltalk (VisualWorks), Java, C++, C and Perl, but have also spent a fair amount of fun time working with Squeak, SBCL Common Lisp, Clojure and Erlang. I'm a huge fan of Linux and Unix, and believe that the shell is a vastly under-used resource.
This is where I personally find Smalltalk to be annoying.
Don't get me wrong: it's an amazing language and development environment. With something like Eclipse, you write some arcane text files and--after running them through a compiler that you didn't really need the IDE for anyway--you have your application. With Smalltalk, the IDE is the application: you just iteratively refine it until it becomes what you want to ship. Awesome stuff.
But that same self-contained structure is what hurts it. When I write C, I tend to use Emacs, enhanced with a bunch of add-ons. I use the git source control system. I use
diff
and patch
to throw experimental code snippets at colleagues, and they do the same with me.However, my choices don't stop them from using Eclipse, and the master source repository being Subversion. Furthermore, when I need to write Java or Perl, I can use the vast majority of the tool stack that I've accumulated just by switching buffers in Emacs. Simply put, I get to transfer most of my skills between tools.
In Smalltalk, it's all-or-nothing. Shell tools are useless on the image. You can't edit code in your favourite editor (well, you can, but unless you're using GNU Smalltalk, it's inconvenient to the point of being unusable). On top of that, you need a special, integrated source control system that's most likely dictated by your vendor, with migration being difficult-to-impossible.
For me, this single-mindedness it what stops Smalltalk being my favourite language (well, that and a single-threaded virtual machine and non-native GUI widgets). For now, Perl retains that crown (despite it being fashionable to bash it these days), with Clojure looking very, very interesting as a possible successor...
Saturday 3 January 2009
Factorials with Clojure and SBCL
I've been playing with Clojure for a few days now, and have to admit being really impressed. It's almost as flexible as Lisp, and has simple and complete access to the massive Java class libraries. It's also fully dynamic, unlike the largely static Java language that it shares a platform with.
One of the somewhat contrived examples used to show off dynamic languages is calculations of factorials: this quickly differentiates languages that are more considerate to the machine than the programmer, as the explosive growth of values produced by the function quickly overflows the register-bound
So I tried this out in Clojure, and hacked out this in a few minutes:
I was impressed.
Curious about how a more traditional Lisp would cope, I tried this largely equivalent code out in SBCL:
which, on the same machine, performed the same calculation in under 13 seconds. WOW.
The purely iterative version:
performed almost identically.
Pretty awesome. :-)
One of the somewhat contrived examples used to show off dynamic languages is calculations of factorials: this quickly differentiates languages that are more considerate to the machine than the programmer, as the explosive growth of values produced by the function quickly overflows the register-bound
int
and long
types in C and Java.So I tried this out in Clojure, and hacked out this in a few minutes:
(defn factorial [x]
(loop [current x
accum 1]
(if (= current 1)
accum
(recur (- current 1)
(* current accum)))))
loop/recur
offers optimisation similar to tail call optimisation, allowing you to avoid blowing the stack with large numbers. Trying this out with 100,000, my dual core Macbook Pro chugged away for just over four minutes to produce a number almost half a million digits long.I was impressed.
Curious about how a more traditional Lisp would cope, I tried this largely equivalent code out in SBCL:
(defun factorial (x)
(labels ((helper (current accum)
(if (= current 1)
accum
(helper (1- current)
(* current accum)))))
(helper x 1)))
which, on the same machine, performed the same calculation in under 13 seconds. WOW.
The purely iterative version:
(defun factorial (x)
(do ((current x (1- current))
(accum 1 (* current accum)))
((= current 1)
accum)))
performed almost identically.
Pretty awesome. :-)
Subscribe to:
Posts (Atom)