Archive for July 2007

Irrefutable patterns for the ignorant

A few days ago was the first time I ever saw some code like this:

~[arg] <- getArgs

I hadn’t come across irrefutable patterns (also called lazy patterns) in Haskell before and was of course curious. This is an attempt at illustrating the difference between lazy and non-lazy pattern matching. Here’s some code not using lazy pattern matching:

main = do
    putStrLn "Before"
    [arg1] <- getArgs
    putStrLn "After"
    putStrLn arg1

Running it, without giving it any argument, results in:

Before
pattern: user error (Pattern match failure in do expression at pattern.hs:9:4-9)

Here’s almost the same code, but with lazy pattern matching:

main = do
    putStrLn "Before"
    ~[arg1] <- getArgs
    putStrLn "After"
    putStrLn arg1

Running it, again without providing any argument, results in:

Before
After
pattern: pattern.hs:(7,7)-(11,16): Irrefutable pattern failed for pattern [arg1]

Identity as a transformer: IdentityT

Here’s a somewhat “silly” transformer. I’ve been thinking about it for a few days but didn’t find the time for it until tonight. Never ever having written even a monad on my own I did find the thought of a transformer more than a little daunting. As so often before I don’t really have any novel ideas and DonS had already done the same thing (thanks to chess in #haskell for helping me find it). After seeing his code I cleaned mine up (and copied some things I had “saved for later”). Here’s the resulting monad transformer:

newtype IdentityT m a = IdentityT { runIdentityT :: m a }

instance (Monad m) => Monad (IdentityT m) where
    return = IdentityT . return
    m >>= k = IdentityT $ runIdentityT . k =<< runIdentityT m
    fail msg = IdentityT $ fail msg

instance (MonadIO m) => MonadIO (IdentityT m) where
    liftIO = IdentityT . liftIO

instance (Functor m, Monad m) => Functor (IdentityT m) where
    fmap f = IdentityT . fmap f . runIdentityT

instance MonadTrans IdentityT where
    lift = IdentityT

I’ll get back to what I think it might be useful for in a later post.

Why is constant improvement such an alien concept in large organisations?

Warning! This is a bit of a rant. If you don’t like reading that sort of thing you should probably stop now.

This started the other day when I tried paying my first credit card bill. I ended up ringing LloydTSB’s phone bank just to sort it out. After being held by the hand through the whole ordeal I tried to point out to the lady that these steps were far from obvious and that they should do something about it. She responded with “it’s really quite easy once you know how to do it”. I pressed on and received a “one can always click around a bit and find out how to do it”. I wasn’t very impressed. I suggested that they ought to add a recipient automatically for settling credit card bills. The lady at LloydsTSB replied, with a very serious voice, “we can’t assume that our customers want to pay their credit card bills over the internet”. I responded, with slight disbelief in mine, “then they don’t have to click that particular link”. She didn’t seem to be convinced their customers possess enough intelligence for that though.

Of course I know it was futile trying to push through improvements by talking to what can’t be called much more than a helpdesk. However, I couldn’t let go of it so easily. I sent the following message through the internet banking system:

I just paid my first credit card bill. I decided to do it over the internet. Given the instructions on the back of the bill I thought it’d be easy, “select ‘Transfers & Payments’ and follow instructions on screen”.What I ACTUALLY had to do was:

  1. Choose the account I wanted to pay the bill from.
  2. Select ‘Transfers & Payments’.
  3. Choose to add recipient.
  4. Type in ‘lloydstsb’ and search for the company.
  5. Go through a rather long list of companies and find the one that matched my credit card type and number.
  6. Enter a reference, which in fact was my credit card number.
  7. Go back to ‘Transfers & Payments’ fro the account I wanted to pay from.
  8. For the rest I could follow instructions on screen.

Far from as easy as the instructions on the bill suggest. I actually gave up and rang PhoneBank for help.

Suggestions for making it easier:

  • automatically add a recipient for the credit card, or
  • add more complete instructions on the back of the credit card bill

I hoped that this route might actually lead me to someone with at least an iota of intelligence within the company. Earlier today they dashed my hopes by sending me this email:

Dear Mr Magnus

Thank you for your e-mail about paying a bill online.

I’m sorry to hear that you had difficulty setting up a payment on your account. I have listed below step by step instructions on how to set up and make payments to your credit card.

  • Select the account you wish to pay the money from, this will be highlighted in blue,
  • Select ‘Transfers and Payments’ from the options menu on the left hand side,
  • Choose the option ‘Click here to add new recipient to your list’ located in the middle of the web page,
  • Select ‘pay a bill’,
  • In the ‘Find company’ field enter the card type that you hold, for example, if it was Lloyds TSB it would be ‘LTSB’ or ‘LLOYDS TSB’,
  • Choose the relevant card from the list and you can check you have the correct one by matching the first six digits of the card number.
  • Enter 16 digit reference number on the front of the credit card,
  • Re-enter the number,
  • Click ‘Add to my list’.

The payment arrangement should now be set up. To pay your credit card, click on the relevant option from the ‘Transfers and Payments’ screen.

The resulting rant lasted the better part of my lunch hour. It’s a good thing I have patient and understanding colleagues :-)

Being somewhat involved in OpenSource and therefore used to constant improvements of “products” I find it very strange when I come across a commercial company that displays such a dis-interest in improving their customers’ experience. Doesn’t it make financial sense for a bank to improve their online banking system? Are they not interested in making their customers satisfied and make sure they stay? I mean it’s not like LloydsTSB is the only bank in England. I wouldn’t be too surprised if they all are equally bad at this sort of thing, but I refuse to give up. I intend to have relationships with companies, especially with companies that play an important role in my life, like banks. I believe in constant improvement, on all levels in life, and I’ll keep on bringing that into the relationships I have.

I heard somewhere that a hero sees the world, not as it is, but as it ought to be. If that’s true then I plan on being a hero for the rest of my life.

LVM rocks!

I knew my persistence with using LVM would pay off one day. Despite the little mishap I had last year :-)

For a shiny new install of 64-bit Debian I chose to let the installer partition up my entire harddisk and instructed it to use LVM. This morning I noticed that the root partition was down to only 25% free space and during an upgrade it ran out of space. Not really a good thing. So, shut down the machine and out with the extra harddisk I’ve been putting off sticking in the machine. Here’s what I did after booting:

  1. Create a single large partition and make it of type Linux LVM (8e) using cfdisk.

  2. Prepare the new partition for use with LVM:

    # pvcreate /dev/sdb1
    
  3. Add the new ‘physical volume’ into the ‘virtual group’:

    # vgextend mainvg /dev/sdb1
    
  4. Extend the ‘logical volume’ where root lives:

    # lvextend --extents +50%LV /dev/mainvg/root
    
  5. Then it turns out that Ext3 has no problem with extending a mounted filesystem so the last step was easy, but a little nerve wrecking since it was my root partition:

    # resize2fs /dev/mainvg/root
    

All done!

Continuing with continuations

As I alluded to in my previous post on continuations I was interested in them due to a suspicion that they might offer a nice way of dealing with an issue that came up with some code I was writing.

I spent some time the other night playing with continuations and I re-used the problem from an earlier post. There are a few differences this time around though, the main one is that there is no need to create a list of characters instead the goal is to string together the reading and the processing using continuations.

First off I should mention that I stuck -fno-monomorphism-restriction in my source file. Without it I got a lot of complaints from GHC.

I started with the most basic set of functions, one for reading a character and one for writing a character. I want the type to be ContT r IO Char for the former and ContT r IO () for the latter, but as always I don’t actually need to tell the compiler the exact types. Basically they correspond to getChat and putChar that way:

cpsGetChar = liftIO getChar
cpsPutChar c = liftIO $ putChar c

Running these two in sequence is done like this:

runContT (cpsGetChar >>= cpsPutChar) return

Running it just once isn’t enough though and I was pointed to the function forever by Cale in #haskell. It wasn’t actually in the version of GHC I have on my system (it’ll be in 6.7) so I had to define it:

forever x = x >> forever x

Using that it’s now easy to keep on reading an writing individual characters forever:

runContT (forever $ cpsGetChar >>= cpsPutChar) return

Since that doesn’t ever terminate it isn’t really what I want. Instead I want the two functions to be “mutually recursive with continuation”, for that I need to control the continuations more than return allows, I need to use callCC. First I started by rewriting the functions from above using callCC. That turned out like this:

cpsGetCharCC = callCC $
    \ k -> liftIO getChar >>= cpsPutCharCC >>= k
cpsPutCharCC c = callCC $
    \ k -> (liftIO $ putChar c) >> cpsGetCharCC >>= k

Running it should produce the same result as when using forever above:

runContT cpsGetCharCC return

Basically both functions defer calling the actual continuation until a call to the either cpsPutCharCC or cpsGetCharCC has been made. Still this never terminates, but rewriting the first function to terminate is simple:

cpsGetCharCC = do
    c <- liftIO getChar
    if c == 'q'
        then return ()
        else callCC $ \ k -> cpsPutCharCC c >>= k

Now, one thing that’s possible to do here, but which would be difficult to accomplish when mapping the handler on a list is to terminate in cpsPutCharCC. All that is required is to rewrite the handler similarly to what I just did with cpsGetCharCC:

cpsPutCharCC c = do
    liftIO $ putChar c
    if c == 'x'
        then return ()
        else callCC $ \ k -> cpsGetCharCC >>= k

Yes, there isn’t really that mcuh to all of this. Sorry if I’ve disappointed. The same functionality could be achieved using two regular mutually recursive functions:

mutGetChar = do
    c <- getChar
    if c == 'q'
        then return ()
        else mutPutChar c

mutPutChar c = do
    putChar c
    if c == 'x'
        then return ()
        else mutGetChar

And at this point I won’t do anything more except explain why I kind of like using continuations. It’s all about flexibility and composability. Say that we have a library for dealing with events and a developer wants to use it to do some processing of said events. I suspect that something that allows writing code like

runContT (setupWorld >>= receiveEvent handleEvent) postProcessWorld

is to prefer over a set of straight-forward functions, some which take “callbacks”. But hey! I might be wrong :-) I’m planning to see for myself!

Beginning to look at continuations

I’ve always thought of continuations as a really brilliant solution looking for a problem. I know there are numerous problems it solves, but I mean it on a personal level. I’ve never had a problem where continuations was the solution. Recently I’ve come to believe that it might be because I don’t understand them very well :-)

I just spent a couple of hours reading about continuations and thought I’d offer a little reading guide for other curious people. If I were to start over I wish I had found the Wikibook chapter on Haskell/Continuation passing style first, I think it fully debunk the myth that continuations is something complication. After that it’s easier to tackle the article titled Talk:Haskell/Continuation passing style. The Haskell Wiki article on continuations is indeed right in saying that Wikipedia’s decription of continuations is rather good. I suspect that the chapter on Continuations found in All About Monads will be an excellent refresher in a few weeks when my brain has managed to suppress what I’ve so painstakingly pushed into it tonight.

This little research expedition happened because I’ve had a nagging suspicion that continuations might offer a more elegant implementation for some code I’ve been toying with for a little while now. Well, we’ll see.

RyanAir is a bus company

RyanAir’s planes now have advertisements on the outside of the over-head compartments further confirming my feeling that they’re a bus company.

I still mourn FlyMe’s demise!

Midsummer in Sweden

This year, just like last, we spent midsummer in Sweden. As always we had a great time, this year despite the weather. Highlights were the possibly sunniest day which we spent taking the old train between Anten and Gräfsnäs and the two nights we stayed in Trönningenäs just outside of Varberg. I also think that everyone visiting Göteborg should eat at Restaurang Räkan on Lorensbergsgatan, the food is simply heavenly!