Notes on new Macbook Pro

8 Mar 2010 In: mac

So, I got a Macbook Pro from $new_company (about which more later) and, as I believe is traditional, here are some notes about the experience.

Setup

Very nice: no rebooting, things just work from more or less the moment you take it out of the box. (Compare with recent Samsung N140 netbook, whose Windows 7 install wanted several reboots to install all the additional antivirus and other crap it comes bundled with.)

Apps

My parents, brother, gf, and her mother all have macbooks. And you have to admit that out of the box they come with a rather well thought out set of applications for real people. But of course everyone has different needs, and I immediately installed the following:

(Some are payware, eek! I'm trying them out for 30-day trials, and may get some through work, others I might be persuaded to pay myself, but lets see)

  • Adobe Creative Suite 4 (Payware, but a friend works for Adobe, which made it seem very much worthwhile, if only for PhotoShop and InDesign)
  • Launchbar (App Launcher. Very nice, and apparently shinier than QuickSilver, but Payware)
  • Things (Task tracker, seems quite nice. Payware)
  • The Hit List (ditto, trying now after finishing the Things trial)
  • Omnigraffle (really nice design tool. Payware)
  • OmniOutliner (outlining tool. Payware)
  • Miro (torrent client and video player, Free)
  • VLC (Video player. Free)
  • VirtualBox (Virtual machine host. Free)
  • VMWare Fusion (Virtual machine. Payware)
  • Skype (VOIP client - yes, proprietary but family and work use it. Free)
  • Telephone (VOIP client without a stupid phone-like UI, yay! Free)
  • Google Chrome (Because Safari still sucks. Free)
  • Firefox (ditto)
  • OpenOffice (Office applications. Now native, so starts quickly unlike the old NeoOffice, yay! Free)
  • TextMate (Text editor. Though why would I want anything other than Vim. Payware)
  • MacVim (Text editor. Free)
  • Cha-Ching (Money manager. Crashed, not impressed. Payware)
  • Moneywell (ditto. Seemed more stable at least. Payware)
  • GitX (Git client. Shiny. Free)
  • The Haskell Platform (Much easier to install than last time I tried on ubuntu... Free)
  • Picasa (Photo manager, though I guess iPhoto is OK. Free)
  • Songbird (Music player. Free)
  • Tweetie (Twitter client. Free I think, I'm certainly not paying for one anyway)

Then some file storage apps:

  • DropBox (Remote file storage. Free)
  • BackBlaze (constant remote backup. Without any options. Yay. Bought immediately after free trial, as I really like the idea of not losing all my data, even though I'm too stupid to manage my own backup).

And some toys for the menu bar:

  • SlimBatteryMonitor
  • MenuMeters
  • MacFuse + fuse-ext2 (to copy my data from external ext3 disk)
  • Growl (Grrr!)
  • Candelair (to attempt to fix apple remote breakage in snow leopard)

And miscellaneous bits and bobs:

  • X-code and brew
  • Xiph-qt, to allow iTunes to play .ogg files

I've not had time to play with all of these, but the overall quality is pretty good. Installation isn't too bad, though I don't think it's intuitive to "just drag the icon to the applications folder". You can tell it's not intuitive by the fact that every .dmg has a different background image with a large arrow graphic trying to point it. Or by the fact that my parents launch the one or two applications they've downloaded by opening them from a .dmg that they have permanently mounted... sigh...

Ah, there's even an article about this.

But OK, once you know how to do it, it's easy enough. Though I'd like apt, and should look at MacPorts. I did install brew which is nice for command-line apps.

Hardware

Hardware is nice. Mostly. It doesn't run too hot. I like the lighting on the keyboard. Actually the keyboard is much nicer than I'd expected (One of the few really excellent things about the thinkpad was its keyboard, so I wasn't really expecting all that much from these odd scrabble-keys, but I'll talk about that in more depth shortly).

The new Mac trackpad is OK. I think I'd rather have a button - it's not as if you can click anywhere on the trackpad to click, it has to be near the bottom, so why not just have a button? On the other hand, tap-to-click works fine. Not having physical buttons means that there isn't a middle-mouse button. Which is inconvenient in an ubuntu VM, as it means there's no obvious way to do copy-paste in an xterm... I worried about this for a while, then came up with the obvious workaround instead -- use gnome-terminal... (who needs unicode, colours, or stability anyway?)

The trackpad seems insensitive too for "click; move; click" sequences. OK, this is mainly a problem for FreeCell and DiceWars, but annoying in any case.

The screen is nice too. Oh, the screen: this was largely why I had a sudden wild shift from a 12" supposedly "ultraportable" thinkpad to a 17" MBP... I decided that I couldn't see enough on a small screen. OK, so the 17" is massive... embarrassingly so when you take it out on a train for example, but it does make for a great machine to work on.

There are a couple of things I'm not so pleased with though: though the machine doesn't run hot (like my thinkpad) it does tingle in a semi-electric-shock way when the power adapter is plugged in (a quick google suggests this is a fairly common problem). Also the headphones, while loud and crisp, seem quite noisy. When you have the phones plugged in, every time a noise occurs, the headphone socket then hisses for half a minute, then switches itself off again.

Keyboard

The most annoying thing about the keyboard is the odd positioning of the keys. The # key is hidden away in Alt-3 (but without anything written on the keyboard to hint that). Oddly under my ubuntu VM (with keyboard set to Apple MBP) it insists on RightAlt-3. PgUp/PgDn are Fn-Up/Fn-Dn, which is reasonable but unexpected. Also, there's only one delete key, which behaves badly in Terminal.app until you set the appropriate options correctly (why isn't that the default?). Yet, there are some oddities taking up precious keyboard real estate:

  • WTF is the "±§" key for?
  • Why haven't they moved the Caps Lock key? OK, it's not entirely useless, but it has very few use-cases: shouting on IM, or writing COBOL. It should be somewhere towards the top-right of the keyboard, and not in a place that's easy to mistakenly press it. OK, so no other manufacturer moved CapsLock anywhere else, but Apple have supposedly "thought" different about the rest of the layout, so why not this?

Also, instead of Ctrl being at the very bottom-left (a lovely position, which means you can press Ctrl without using a finger -- I use the pad of my left hand) the "Fn" key is there instead. The Thinkpad shares this idiocy. I've recently been using a 5-year-old HP laptop as a backup, and despite it being underpowered and falling apart, the 2 things I really liked, after using a thinkpad, were a) the Ctrl key being in the right place, and b) that it had a trackpad. (The Thinkpad just had a clitmouse. After 2 years I learnt how to use it, but still found it hateful. Never again. But I digress.)

Media keys

Another oddity is the media keys. (Where by "oddity", I'm struggling for a politer word than "batshit crazy clusterfuck").

I never really got media keys working satisfactorily under Linux, where they mainly just spoke to the foreground application. So I was hoping that a bit of Apple shiny would go a long way. Now I'm not a usability expert, but I'm hoping that my expectation for what a Play/Pause button should do might not be entirely insane or impractical:

  • if any running app that knows about play/pause declares it is "playing" then it should pause
  • else if the currently focused app knows about play/pause, then it should start playing
  • else launch the "default media app" and start playing (or, even better: don't do anything)

What actually happens is this:

  • The Play/Pause event is sent to every open app. So any app that cares about it will:
    • start playing if paused
    • pause if playing
  • also, if iTunes is closed, then it will open

So, if you have VLC open playing a video, and somebody calls you, you press the Pause button. Then:

  • VLC stops playing
  • Oops, looks like you had Miro open, so it now starts playing
  • Looks like you didn't have iTunes open, you must have wanted that, right? So iTunes launches and then starts playing too...

But that's OK. You can press the Pause button to stop the chaos right?

  • Now Miro stops playing
  • iTunes stops playing
  • VLC starts playing again

Apparently this is breakage from 10.6 Snow Leopard, having been sane till 10.5. There is speculation that Apple are trying to make 3rd party apps look bad by not responding "properly" to the media buttons, while Apple's own apps do. If that's true, then it's not working. I don't blame VLC. I don't blame Miro. Dear Apple, you fucked up, please sort it out.

(This isn't just me: various threads including http://discussions.apple.com/thread.jspa?threadID=2122639 are whining about this idiocy. The thread mentions a cunning back: if you have QuickTime open, it acts as a prophylactic against iTunes opening. This is apparently because the 'loginwindow' process has an exception hardcoded into it not to launch iTunes if it detects QT running. So not in the slightest bit insane then.)

I had the same issue with the remote. Yet it's shiny, but if it only wants to play with iTunes and the useless Front Row (where by useless, I mean doesn't play my videos, which is what I expected it to) then it's basically a shiny paperweight. Thanks Apple. I've installed Candelair, which may possibly help improve this but I've been disinclined to test properly.

Power Management

When the machine resumes from sleep, the external monitor shows static "snow" for half a minute or so. That's not particularly impressive. A quick google suggests that older models had similar problems, perhaps this resurfaces every now and then? I should probably check to see if it's going to be a deteriorating hardware issue.

On the other hand, it actually resumes. From sleep-to-ram. And hibernate-to-disk. Every time. Yes, every time!

(Can you tell I've been using Linux-on-laptop for the last 5 years?)

And though I don't think I'm getting anywhere near the 8 hours advertised, I am to be fair running with a bright screen, a couple of VMs and so on.

Windowing

This is a mixed bag. Mainly, the windows are less useful/pretty than Gnome: you can't set windows to Keep-on-top. You can only resize using the bottom-right control, no Alt-to-drag option. No ability to maximize a window (ok, so the need for this doesn't come up as often as I'd expected, but when it does, it's annoying), and new windows don't get tiled in pretty places as with gnome. Also, more often than I remember happening under Linux, popup windows get placed underneath the windows that created them. Meh.

I'm not sure whether I like Finder better than Nautilus. It seems to be possible to use drag-to-copy into a folder of folders (rather than into one of the subfolders). On the other hand "type-to-jump-to-filename" seems to not work consistently (I haven't quite figured which state is inhibiting this).

When you're using 2 monitors, Mac's single menu bar (on the top of the screen instead of top of window) becomes a royal pain, as you have to move the pointer from the external screen back to the laptop's one to use it. I guess I should learn the command keys to do this without mouse. Also, if you put the second screen immediately above the first, you suddenly change the menu bar from being "a mile high" to being a thin strip of stuff that you have to target. So don't do that (or learn those key combinations).

Spaces... wasn't sure how to move apps between spaces. But the F8 view is quite cute when you work out what it is (I was confused by it showing me what looked like a 4x2 view instead of a 2x2 one... but of course I have external monitor plugged in... d'oh.) Very cute is that Expose works while you're in the thumbnail view.

Date/Time applet in menubar is underpowered. Compare to the Gnome one that has a month calendar view, world date-time and weather. This one can't even be configured to show the date in the menu bar. It's a prime candidate for replacement with something actually useful.

The only thing I can think of that might explain the weak Date/Time applet is that they are trying to sell you on the Dashboard one? If so, it doesn't work. The Dashboard is a wonderful combination of ugly, confusing, and useless. Every time I opened it (by accident, mostly) I would struggle to either get it to do anything useful or, even better, close again. Luckily there is a 'defaults' setting that prevents it from opening again. Phew. Bizarre.

Virtual Machines

For dev work, I was planning to use VirtualBox, which has the nice feature of being free. It has a few problems though:

  • networking seems to be hard to setup to work usefully.
  • Swallows some keys. Particularly Ctrl-Arrow to move between spaces, and Command-Space for launchbar.

VMWare solves these, though, oddly, it actually seemed slightly harder to setup. Installing the "guest additions" had to download a large package, and instead of running it itself expected me to read the system documentation to know what to do with it (I just ran the perl script from terminal, which works fine... Not sure why the README file couldn't have just said that). Still, minimizing friction between guest and host working is probably worth paying for.

Conclusion

There are some niggles, and some outright baffling brokenness, but actually I'm quite enjoying working on a Mac.

I've been prototyping ways for customers to import data into our system. While some kind of JSON/XML structured data would be easiest for us, our customers like spreadsheets. That's cool of course, and it's easy to mock up imports like this.

Product Price Category
Brie £ 2.00 Dairy
Chablis £ 5.00 Wine

Of course not all data fits nicely with this kind of very flat structure. For example multiple values. We could do something like

Product Price Tags
Brie £ 2.00 Dairy,Cheese,Food
Chablis £ 5.00 Wine,Alcohol,Drink

but it might be nicer for the customer to organize like this instead:

Product Price Tags
Brie £ 2.00 Dairy
    Cheese
    Food
Chablis £ 5.00 Wine
    Alcohol
    Drink

The problem is that we can't really parse the table row by row, simply. We have to remember where the definition started, and finalise when the item has been completely defined (which will happen when the next item starts). I started prototyping this in Perl, and ended up with some typical imperative code like this:

    my (@products, $product);
    for my $r ( @rows ) {
        my $row = get_row_data($r);
        if ($row->has_product) {
            push @products, $product if $product;
            $product = Product->new( 
                product => $row->product, 
                price   => $row->price 
            );
        } else {
            die "No product" unless $product;
        }
        $product->add_tag($row->tag);
    }
    push @products, $product;

This is quite yucky. See how I'm repeating the push @products line: first time when seeing a new product, and second when we've exited the loop. Then we have the multiple assignments to $product and so on.

All of which seems to work OK, but I really dislike this logic, and am beginning to think it's a perfect example of an imperative antipattern. So, let's see if we can do it more elegantly. Thinking about how I'd do it functionally, in Haskell, I'd use groupBy on each row, connecting the rows up until the next row has a non-blank product.

So let's try that in Perl! We'd want something like:

    my @items = groupBy sub { 
            my (undef, $next_row) = @_;
            ! $next_row->has_product
        },
        map get_row_data->($_),
            @rows;

    my @products = map {
        my $row_1 = $_->[0];
        Product->new(
            product => $row_1->product,
            price   => $row_1->price,
            tags    => [ map $_->tag, @$_ ],
        );
        } 
        @items;

That has less repetition, and less accidental complexity. First we group the lines. Then we turn the groups of related rows into new objects. Job done.

The groupBy function is interesting: notice how the callback to it takes 2 arguments ($this_item, $next_item), though as we don't care about the current row, only the next one, we're just doing (undef, $next_row).

groupBy is also problematic though, in that it doesn't exist in any of the normal places I'd have expected (List::Util, List::MoreUtils etc.). So let's create it. The ideal way might be to translate Haskell's definition from the Prelude, but given that Perlish lists don't have lazy semantics, and we then have to implement span etc., let's just write a noddy imperative version for now:

    sub groupBy {
        my ($fn, @elems) = @_;
        my @groups;
        my $a = shift @elems;
        my $b;
        my @group = ($a);
        while ($b = shift @elems) {
            if ($fn->($a, $b)) {
                push @group, $b;
            } else {
                push @groups, [@group];
                @group = ($b);
            }
            $a = $b;
        }
        push @groups, [@group];
        return @groups;
    }

This of course has much of the unpleasantness I was complaining about before, but at least it's encapsulated, and allows us to use groupBy neatly. (And we can always come back and clean up the internals later).

(And yes, I know I could define groupBy (&@) so that I could omit the 'sub' around the block like with map/grep. But this is more annoying than useful, as I can't then simply call: groupBy \&function.)

How would you tackle this task? Is there even an elegant way to do it imperatively?

Github language statistics

15 Dec 2009 In: Uncategorized, perl
I enjoyed Aldo Cortesi's rather interesting post about language statistics on github. He's done some good analysis, and there are some interesting nuggets of information to be had about Perl, Haskell (though fewer, as there are only 18 projects that made his criteria) as well as other languages.

Of course, there is some silliness there too: you can bash Perl for many reasons (and if you've read my blog before, you might know that I do too), but there are some gems of forced interpretation:

C and Perl projects show a marked decline in activity over their first year. I suspect that the Perl result is due to the fact that it becomes harder and harder to contribute to a Perl codebase, the bigger it gets. The C result is more of a mystery.
I'm not sure that the premise is true -- perhaps Perl projects are more limited in scope, for example. And Modern Perl is a quite different beast from the Matt's PERL script archive of yesteryear. But the punchline is priceless ;-)

Here's what I read with my biased interpretation of his results ;-)

  • Far fewer Perl projects than Ruby/Python so far. Github is a Ruby community effort, so it's unsurprising that it would dominate here.
  • Median contributors for Perl is above average. This is substantiated by the total contributors for long running projects being comparable with Ruby/Python.
  • Perl projects seem to have many, small commits. This would seem to be a good thing, and rather in keeping with the Git Way. (/me shuffles embarrassedly at the sight of his own, rather monolithic git commits...)
  • While Aldo suggests Perl projects are "significantly more "top-heavy" than those in other languages, with a smaller core of contributors doing more of the work," one could also hypothethize that Perl projects are good at attracting and retaining a strong core team. This certainly seems to be the case with long running, active projects such as Catalyst and Moose.
So, thanks Aldo for taking the time to do this fascinating analysis (though I'm sure you won't mind if I draw some slightly different conclusions than you ;-P)

Undone - sci-fi comedy on BBC radio

12 Oct 2009 In: Uncategorized
Just a quick post about this cute scifi-ish radio show, Ben Moor's Undone. Someone pointed me at it a couple of years ago, noting that the protagonist Edna Turner reminded him of the lovely Sally Sparrow from Blink. I loved the wordplay, the surreal alternate reality (for example, "Los muchachos que necesitan la insulina, Mexico City's top diabetic mariachi band"), and the choice of music. And now I discover there was a 2nd series, and even a 3rd which will be broadcast in November.

Though I like the offbeat parallel reality more when it's low key (the series finales are more high-fantasy end-of-the-world stuff) the story arc is shaping up quite interestingly, as the whole premise of the scenario is put into question (hopefully this won't affect the surreal punning).

You can catch up on iPlayer — though, bizarrely, they haven't stacked previous episodes, so you'd only catch the series 1 ending now, bad BBC! And while I'm knocking Auntie, why don't the microsite or the iPlayer programme information give any details on the music used? The manic choral theme is fantastic, and the incidental music is also excellent. Any clues?

Beans pt2: docs, tests, and more types

9 Oct 2009 In: perl

A couple of comments on the first post suggested that I look into the command-line bookkeeping application ledger, or indeed, its Haskell version hledger. They look very interesting, but rather hard to wrap my head around. So though I'm going to bear them in mind for later, I'll carry on doing these sketches till I understand the problem space better, at which point, perhaps I'll either a) steal some ideas from them, or b) realise they are undoubtedly the way forward and start using them.

I used Module::Starter to retrospectively turn this project into something I can release as a distribution to CPAN, with docs, tests, a Makefile, and so on.

module-starter --module=Beans --mi --author=osfameron --email=osfameron@cpan.org

This also creates some skeleton docs, so I've gone in to add a few actual notes (mainly just pointing at this blog), and to delete a few sections that module-starter puts in by default:

AnnoCPAN
(I don't think this is particularly useful, and don't see the need to advertise it from my module, as it's already linked to from search.cpan.org)
CPANratings
This is useful, but again, it's already linked to from search.cpan.org. Why should I link to it from my module? Should I also include buttons to pimp it on reddit and digg?

Then I added some tests in t/01-basic.t, for example:

my $item = Beans::Item->new(
    name     => 'Mortgage',
    value    => 500.00,
    due_date => '2009-10-01',
    comment  => 'Home sweet home',
    tags     => [qw/ mortgage foo bar /],
    );

ok $item, 'Object created successfully';

is $item->name,  'Mortgage',      'Name ok';
is $item->value, 500,             'Value ok';
is $item->due_date->month, 10,    'Date ok';

All very noddy stuff, but it helped me find a bug in the version I'd blogged earlier! I hadn't told the date fields to use the coercion I'd set, so the above code failed, complaining quite rightly that '2009-10-01' isn't a DateTime object!

So I amended the date fields like so:

     has due_date  => ( isa      => DateTime, 
                        is       => 'rw', 
                        required => 1, 
                        coerce   => 1,
                      );

and all was again well. Yay for failing tests! This brings me to a suggestion from John Napiorkowski to use MooseX::Types::DateTimeX to get my coercion for free. This does indeed work, and I've changed the code to use it, though it doesn't by default use DateTime::Format::Natural — we'll come back to this later.

While we're looking at types, let's fix the crufty implementation of tags. We've currently got an ArrayRef[NonEmptyStr], but really, we don't want an Array, because we want to be able to:

  • Check whether a given tag is active
  • Enable a tag (without duplicating it if it's already present)
  • Delete a tag.
These aren't so much features of arrays as of hashes, or, even better, sets. I was about to enter a rabbit hole of implementing using a hash and ::Meta::Attribute::Native::Trait:: when Stevan suggested Set::Object and its wrapper MooseX::Types::Set::Object.

This changes our code to:

    has tags      => (
                       isa      => 'Set::Object',
                       is       => 'rw',
                       accessor => '_tags',
                       coerce   => 1, # also accept array refs
                       handles => {
                           tags       => 'members', # random order
                           add_tag    => 'insert',
                           remove_tag => 'remove',
                           has_tag    => 'member'
                         },
                     );

which we can test like so:

is_deeply [ sort $item->tags],
    [qw/ bar foo mortgage /],     'Tags ok'
        or diag Dumper($item->tags);

ok $item->has_tag('foo'),         'Has tag foo';
ok $item->has_tag('bar'),         'Has tag bar';
ok $item->has_tag('mortgage'),    'Has tag mortgage';
ok ! $item->has_tag('baz'),       'Nonexistant tag baz';
$item->add_tag('baz');
ok $item->has_tag('baz'),         'Now has baz';
$item->remove_tag('foo');
ok ! $item->has_tag('foo'),       'Now lost tag foo';

Set::Object's members function returns the contents in hashed order (i.e., effectively random), but given that we know our "objects" are actually strings, I'd prefer them in sorted order, which would simplify the is_deeply test above. We could do this as a method instead, or possibly use around to sort the results, but we'll come back to this soon!

Coin Tricks

19 Sep 2009 In: haskell
Derren Brown recently claimed he would predict the UK lottery numbers, live on television, and then explain how he did it. It's doubtful that he did either — the alternative explanation that he'd actually committed a massive and improbably daring fraud is vastly more likely than the bullshit he spun out, as it's actually possible... I'm certain (or at the very least hopeful) that the actual "reveal" will come later on in the series... but in the mean time, he did show one very cute mathematical trick for winning a coin game. This is the game:
  • Player 1 chooses a 3-coin combination, say Tails,Tails,Heads
  • Player 2 chooses another, for example Heads,Tails,Tails
  • We now throw a series of coins until one of the players' combination is thrown in order.
You might think, given that any 3-coin combination is as likely as another (will be thrown with a 1/8 probability) that there's nothing to choose between them. But notice that I didn't say you threw 3 coins at a time! For example, if we throw.
  1. Tails
  2. Heads
  3. Tails ... at this point we've thrown 3 coins, and matched no combination
  4. Tails ... and now player 2 has won: coins 2-4 read Heads,Tails,Tails
If you look at these 2 combinations, you'll see that Player 1 will win if the sequence goes: Tails,Tails,(any number of Tails), Head. Player 2 will win in any other situation, i.e. 75% of the time. The combination the hapless participant chose (Heads,Heads,Heads) is even worse, losing 87.5% of the time! It's about time for me to try modelling a problem in Haskell, so let's try this one! I find the docs for System.Random rather confusing, but found some inspiration from a haskell-cafe post about random coin throws and encouragement on #haskell from Luke30, Twey, and others. I won't go through the code in detail this time, but here are some key things to note:
  • the randoms function returns an infinite stream of random things. In this case I'm using it as a stream of Bools, like [False, True, True, False, True, ...] and then converting them into [Tail,Head, ...].

    lilac pointed out that I could create an instance of Random for Coins, I'll have a look at that soon.

  • I'm using tails (nicely overloaded vocabulary ;-) to iterate the infinite sequence of coin flips, stopping when one of the players' sequences matches.
And here's the code:
import System.Random
import Control.Monad
import System.IO
import Data.Maybe
import Data.List

data Coin = Head | Tail
    deriving (Eq, Ord, Show)

-- Derren Brown's coin game.
-- The second winner has chosen a combination that will win
-- significantly more often

main = do
    g1 <- guess
    let g2 = counter g1
    putStrLn $ "Player 1 chooses " ++ (show g1)
    putStrLn $ "Player 2 chooses " ++ (show g2)
    coins <- coinFlips
    let winner = take 1 .  catMaybes .
                 map (win g1 g2) $
                    tails coins
    putStrLn $ "Player " ++ (show winner) ++ " wins!"

guess = do f <- coinFlips
           return $ take 3 f

counter [a,b,_] = [rev b, a, b]
    where rev Head = Tail
          rev Tail = Head

win g1 g2 l | g1 `isPrefixOf` l = Just 1
            | g2 `isPrefixOf` l = Just 2
            | otherwise         = Nothing

-- modified from
-- http://www.haskell.org/pipermail/haskell-cafe/2005-April/009687.html
coinFlips :: IO [Coin]
coinFlips = do g <- newStdGen
               let bools = randoms g
               let coins = map bool2coin bools
               return coins
      where bool2coin True  = Head
            bool2coin False = Tail

I've recently been trying to improve my rather disorganized personal finances. While I previously "managed" by leaving the bulk of the money in my account and hoping that I'd calculated my expenses versus salary roughly correctly, this is suboptimal for answering various questions like "Do I have enough money to go on holiday?" or "Have I forgotten to pay the electricity bill for the last year so that I now have to pay it back?"

Clearly there is a better way. Checking off expected payments, knowing what's due in the next week/month/etc., and siphoning off the excess into a linked savings account for example. But this requires being alert and accurate... Failing that, I could always use a computer ;-)

Spreadsheets

Surprisingly perhaps, the humble spreadsheet is an instrument of frustration for this kind of task. I'm currently using Google Docs, which is underpowered, but not really much more hateful than Excel or OpenOffice.org.

It's particularly bad at being a working document. Moving an item (to reschedule the date, for example) involves:

  • inserting rows
  • cutting and pasting
  • deleting the original rows
  • changing the date
  • "filling" down the subtotal column (because spreadsheets don't have the concept of a calculated column).
Worse yet, because spreadsheets don't know about dates, adding regular payments (bills, weekly spends etc.) is an exercise in manually creating them, and then visually auditing the sheet to make sure you put them in the right places.

Other apps

Of course dedicated apps for personal finances do exist. I looked at the Linux ones, Gnucash, Kash, etc. Mostly they crashed. Sometimes they ran but utterly confused me. And then crashed.

There are also web based ones. My initial survey of these suggests that they are prettier and easier to use, but perhaps clunkier, or tied into specific bank systems.

Beans means...?

So, being a programmer, the obvious answer is to attempt to roll my own. As a learning exercise ;-) Though I'll attempt various sketches of this in Haskell, I'll start off by trying to implement it in Modern Perl: specifically the Moose OO framework, using various niceties such as the pretty declarative syntax of MooseX::Declare.

Every project needs a good name! But this is just a learning exercise, so for now I'll call it "Beans". I'm hoping this will end up an ongoing series of posts, please feel free to follow along at the Beans git repo if you like, or to suggest improvements, for example:

  • a better project name
  • shinier ways to do this in Moose and Modern Perl
  • how this would be more elegant in Haskell/Lisp/etc...
As always, these posts will be fairly rough drafts, I'm hoping to tidy this up into a more finished article soon, so any comments are really welcome to help me polish it up!

Declarative code

In this installment, I'll just define a data type for a line item in Beans. You can see the current code as of this evening, but let's look at it in detail here. We'll start off by using Moose, and declaring our class:

use MooseX::Declare;

class Beans::Item {
    ...
}
Now we'll want to declare some fields for our class. For example, every payment made or expected will have a value:
    has value     => (
                       isa      => Num,
                       is       => 'rw',
                       required => 1,
                     );
What's that? Perl has types?! Yes indeed, Moose gives us types, and automatically validates against them. So when we try to create our object, we'd find that:
    my $obj = Beans::Item->new( );                # fails, value is required!
    my $obj = Beans::Item->new( value => "foo" ); # fails, "foo" is a string
    my $obj = Beans::Item->new( value => 10.00 ); # OK! 10.00 is a number
I've cheekily missed out a few details though... Num isn't a Perl builtin keyword, so we'll need to import it. In fact, while we're at it, let's pull in Str too, so that we can add a couple of string fields at the same time:
    use MooseX::Types::Moose     qw/ Num Str /;

    has name      => ( isa      => Str, 
                       is       => 'rw', 
                       required => 1,
                     );

    has comment   => ( isa => Str,         
                       is  => 'rw', 
                     );
Now we also want to be able to add the date of the expected payment:
    use MooseX::Types::DateTime  qw/ DateTime /;

    has due_date  => ( isa      => DateTime, 
                       is       => 'rw', 
                       required => 1, 
                     );
This means we could do
    my $obj = Beans::Item->new(
        value    => 10.00,
        due_date => DateTime->new(
            year  => 2010,
            month => 6,
            day   => 30,
           ),
      );

        # but I'd prefer to be able to do
        due_date => '2010-06-30',
And we can indeed make this prettier using coercions!
    use DateTime::Format::Natural;
    my $dt = DateTime::Format::Natural->new( prefer_future => 1 );
    coerce 'DateTime'
        => from 'Str'
            => via { $dt->parse_datetime($_[0]) };

    # We'll need to tweak the due_date declaration too...
    # (coercions don't run unless you ask for them)

    has due_date  => ( isa      => DateTime, 
                       is       => 'rw', 
                       required => 1, 
                       coerce   => 1,
                     );
While every line item will have an expected due_date, we'll also want to keep track of what date the item was actually paid, if it has been paid. As this is uncertain, we use the Haskell-inspired Maybe type. (As far as I can see, this isn't actually as powerful as Haskell's Maybe monad, it's more like a nullable type).
    use MooseX::Types::Moose     qw/ Num Str Maybe /;

    has paid_date => ( isa => Maybe[DateTime], 
                       is  => 'rw',
                     );
Once we've got this type, we can calculate a boolean field based on it. The paid field will be true if paid_date contains anything, and false otherwise. Let's try defining it like this:
    use MooseX::Types::Moose     qw/ Num Str Maybe Bool/;
    has paid      => ( isa => Bool,        
                       is  => 'rw', 
                       lazy => 1,
                       default => sub { 
                            my $self = shift; 
                            defined $self->paid_date 
                          },
                     );
The default code block gets called the first time we ask for the field. There are a couple of problems with this of course - once it's set, if we change the paid_date, this field won't change. We'll come back to this later, but here are some ideas:
  • change it into a method
  • use immutable objects, so that setting this lazily on first request is always the right thing to do
  • use triggers to update paid when paid_date is set (and vice versa! setting the boolean flag could set the payment date to today's date, for example).
Now, though we specified that the name should be a string, nothing is preventing us from passing the empty string ""... but Moose allows us to define a better type constraint here too!
    use MooseX::Types -declare => [qw/ NonEmptyStr / ];
    subtype NonEmptyStr
        => as Str
            => where { length $_ };

    # so now we can modify this definition to:

    has name      => ( isa      => NonEmptyStr, 
                       is       => 'rw', 
                       required => 1,
                     );
> Finally, let's add a list of tags. Just for now, we'll define these as an array of non-empty strings:
    use MooseX::Types::Moose     qw/ Num Str Bool Maybe ArrayRef /;

    has tags      => ( isa      => ArrayRef[NonEmptyStr], 
                       is       => 'rw', 
                       default  => sub { [] },
                     );
Notice how we're using default again to set the default value to an empty array.
That's it for now! Next time around we'll maybe look at parsing and displaying line items, and start to do useful things with them.

YAPC::EU::2009 Lisbon writeup

13 Aug 2009 In: perl
The Portuguese team did a fantastic job with this year's Perl conference YAPC::EU::2009 in Lisbon just last week.

What I learnt

  • Lots of companies -- Cisco, Opera, the canton of Geneva, among others -- are proud to use Perl.
  • There is lots of work going on in the "Enlightened" Perl movement. Including ways to make deployment and packaging of Perl apps easier. You can join the EPI for a mere GBP 100/year (whether you're UK based or not) and can pay by regular direct debit.
  • Perl is "Alive and kicking, and stronger than ever". (OK, that's marketing speak. But Barbie's talk on the statistics of Perl backs it up: for example there are between 30 and 50 new authors uploading their first module to the CPAN every month!)
  • As always, the hallway track is one of the best parts of any conference - the chance to meet up with old friends and colleagues, and make new contacts with the very clever people in the Perl community and find out what they're doing.
Cool modules mentioned included:
  • Moose, MooseX::Declare, etc.
  • TryCatch
  • XML::Pastor
  • autodie
  • autobox, and MooseX::Autobox
  • Regexp::Grammars (very cool indeed, from the Damian - Perl6 like rules, in Perl 5)

Logistics

Joel++ booked a flat (via waytostay.com) for 5 of us. This was an excellent idea, reasonably priced, and a beautiful flat, great for socializing in. Highly recommended, especially for a group of friends/colleagues.

Organizers

The Lisbon team did a fantastic job! Among their several innovations, were such delights as:
  • Coffee/lunch all the time: this was very bare bones, but none the worse for it. There was food any time you wanted it, which meant that you never had to worry about leaving a talk late etc. This was especially useful given that the campus wasn't right next to large restaurants. (But even in central venues, if 300 people go out to lunch, they will be gone for 2 hours, realistically).
  • The Quizz show, hosted by Damian Conway at the conference dinner. (One of the orgas)++ had hacked some PS2 games controllers, and great fun was had by all. (Me and Polettix came 2nd, but the important thing is that we answered a Buffy question first, to the horror of davorg and Greg).
  • Moderators in the auditorium (Wendy++ and Liz++).
  • "Special" namebadges with an information booklet inside.
  • sapo.pt sponsored a lovely chillout area with wonderfully comfortable beanbags.
They set a worryingly high standard especially as...

YAPC::EU::2010 will be in Pisa

I seem to have got myself involved in this... but it's going to be great. Look forward to seeing you all there!

Functional Pe(a)rls and Foose

I gave the fourth version of my Functional Pe(a)rls talk, which went down quite well. Thanks to everyone who showed up, especially for putting up with me being late...

I've been apologizing for some time for not having actually sat down and released modules for all the techniques I'm playing with here. But James Laver finally pestered me enough, and we've created a new project: Foose (working title, in homage to Moose) which will aim to bring neatly packaged functional goodness to Perl. (See also irc:irc.perl.org/#foose)

Moving on

I attended YAPC in the last week of employment with Thermeon Europe, who kindly paid for my time there. It's been a crazy 2 years (almost), and I'll miss the guys there, but I'm excited to be moving on to consulting, initially on some Perl development and documentation projects.

If you're interested by anything you've read in this blog and would like to hire me, then please do get in touch ;-) I'm mainly interested in teleworking or contracts in Northwest UK, but happy to discuss!

While we were discussing how to promote the Italian Perl Workshop, and the planned training on Moose, I noted that there weren't any articles on Moose (Perl's modern OO implementation, inspired by CLOS, Smalltalk and Ruby) on perl.it. Lordarthas of course told me "well volunteered!"... oops.

I pointed out that I don't really know Moose, and we eventually agreed that I would "just" translate Jay Kuri's nice new Gentle Introduction.

Now, there is a reason why translators almost always translate into their native language. I can write in Italian reasonably well, but translating into it was a much harder task. While you're writing something yourself, you tend to route around phrases you don't know how to express, choose different words, simplify structures, etc. But translation implies some degree of fidelity to the source, and I found this incredibly hard going. I whined on #perl.it and, in true Open Source JFDI style, larsen asked "Huh? Why are you translating that?" and did it himself! Yay, larsen++!

So my volunteering ended up being limited to making a few corrections/suggestions, along with lordarthas, dree, and dada. Opensource translation and review (using wiki/email in this case, but a git repo or similar could work just as well) can have a fast turnaround, and pick up many errors/nuances that a lone translator would have to work really hard on to do by themselves.

An interesting problem with the technical translation was deciding which phrases to leave in English, and which to translate. Looks like "coercion" is staying in English (followed by an explanation) instead of using the Italian "coercizione". And the title is surprisingly hard to translate, as none of the words for "gentle" map well into Italian. Though it's less cute than the original, the least awful alternative seems to be "Una breve introduzione" (a brief introduction).

The final translated article is now on perl.it!.

Bids for YAPC::EU::2010 - Pisa and Kiev!

3 Jul 2009 In: perl

Organizing a conference is hard, let's go shopping! For the first time I'm officially helping, not just for the Italian Perl Workshop this year, but possibly for YAPC::EU::2010 too. I've been working with the perl.it guys on the proposal to host the European Perl Conference 2010 in Pisa.

We submitted the bid on Monday, and it's just been announced that the teams competing are: Pisa (us) and Kiev in Ukraine. Wow! YEF have actually published both our bids on that link, which is fantastic for transparency.

It also means that we can read their bid... and it's a good one! Looks like we've got some competition. Of course our Pisa bid is excellent too - in any case, the next couple of weeks till we find out who won are going to be a nailbiting time!

About this blog

Osfameron's blog on Haskell, Perl programming, stuff.


Categories