Saturday, May 31, 2008

Rant against Firefox version checks (and solution)

I'm not particularly fond of Firefox's XUL+js architecture, but I'm more than willing to live with this unusual choice if it allows people to easily write Firefox extensions to customize their experience.

But did you ever notice that most of the plugins break every time you update Firefox? I thought that APIs which change this fast must reflect a development model where refactoring is frequent. I'm glad when teams decide that code quality is more important that backward compatibility, but my enthusiasm doesn't seem to be widely shared.

As a side-note, I think that the developers of the Linux kernel have found a great way to please everyone. They maintain backward compatibility for their external interface, the system calls which all Linux applications use, while allowing themselves to refactor their internal interface at will. This is unfortunately not enough since drivers depend on this internal interface, just like Firefox plugins depend on the browser's internal XUL interface. But instead of requiring driver developers to update their code at every release, the kernel team encourages driver developers to commit their code to the kernel tree and to let the kernel developers update the drivers themselves.

Today, I've upgraded Firefox from version 3 beta 5 to version 3 RC1, and as usual it broke many of my plugins. Now, the shocking part is that there isn't supposed to be any change at all between those two versions! So if the internal API didn't change... Could it be that the plugins were broken by something bogus?

What happened is that Firefox's extension mechanism requires each plugin to specify a range of versions on which it is supposed to work, and they're not supposed to pretend that they work on versions which haven't been released yet. What a bad idea! This means that every single Firefox release will break every single plugin except those that cheat and pretend to know the future. This in turn means that every single plugin needs a maintainer to diligently follow Firefox's release schedule and to bump the range of his plugin every time. How boring! I'd rather have those people working on new extensions.

So in case your favorite plugin's maintainer is too bored to do this, here's how to do it yourself.

First, find the install.rdf file associated with your plugin:

bash grep 'em:name' ~/.mozilla/firefox/*.default/extensions/*/install.rdf output ~/.mozilla/firefox/5edzcuns.default/extensions/{e4a8a97b-f2ed-450b-b12d-ee082ba24781}/install.rdf: em:name="Greasemonkey"

Inside the file, just increase the value of em:maxVersion to your current Firefox version, or to some absurdly large version number if you're bold enough not to fear the future.

Thursday, May 29, 2008

Gmail's fullscreen mode (and solution)

Gmail has a so-called "fullscreen mode" which ought to please me: it dedicates all of the browser's screen real estate to the message I'm reading. No pesky sidebars at all. Ain't it great? Well, it turns out I'm rather difficult to please.

What should I do once I'm done with the message? I will either want to delete it, to archive it, or to read my next message. But I can't do any of those in fullscreen mode! How come there's no button to come back to my inbox?

Okay, so this is a trivial problem with a trivial solution: just bookmark Gmail's inbox and be done with it! But of course, I wouldn't be telling you about this if I had settled for the trivial solution.

Here is my solution: widest gmail. It's a bookmarklet, so don't bother clicking on it now, it won't lead you anywhere. But if you put it in your toolbar, and click it once you're in Gmail's conversation mode, it will toggle the fullscreen mode on and off.

Monday, May 12, 2008

Rant against sidebars (and solution)

I don't browse fullscreen.

Let me repeat that, in case it isn't absolutely clear. My internet browser is never maximized. I have a few windows sprinkled about my screen, the more the merrier, until I run out of space to put them. Thanks to you, web designers (but do continue reading even if you're not a web designer as I have a goodie for you at the end), this occurs as soon as I launch firefox.

But it's not your fault. I know how deeply you care about your audience. I know you are worried about the resolution of your website, that's why you gather statistics about our screen resolutions before exclaiming: "thank god nobody runs at 640x480 anymore, but I still need to cater to those poor saps who are still stuck with 800x600". So you build a website spanning those entire 800 pixels and I need to maximize my browser to see it and I get unhappy and I double my resolution, possibly buying a new screen to accommodate the change. And now your statistics tell you that even less people are stuck at 800x600 and you build an even wider website and I buy an even wider screen and I want to slap you silly for misunderstanding my contribution to your statistics.

Your assumption that your audience is browsing fullscreen is wrong. But assume for a moment that you realized your awful mistake and began measuring the size of the browser window instead of measuring the resolution of the screen. Then your statistics would still confirm that browsers are usually larger than what your site needs, and you will still greedily increase its size. This is because I came to your website looking for a piece of information, which I will continue to desire despite the size of your website. Your statistics will report the sizes at which your audience was reluctantly browsing your site, not the sizes at which they would prefer to browse it.

Now suppose that, in your infinite kindness, you decided to redesign your site so that it would scale automatically to fit each viewer's browser size. Would this make me happy at last? I would have hoped so, but most websites today do scale to fit my browser's size, and I'm still not happy.

The source of my annoyance is sidebars. Their width is usually small and fixed, leaving the bulk of the space to the actual contents. The problem here is that "small" is measured according to the web designer's standards, and the web designer is browsing fullscreen on a very large screen. This means that in my browser, those annoying sidebars will take up two thirds of the space and the contents will be so squished that I will need to resize my browser despite the website's struggling efforts to accommodate my size choices. At least when the websites had a fixed width I could scroll those sidebars out of the way.

Anyway, Gmail uses two sidebars, and I wrote a script to get rid of the right-hand one. That's the "goodie", "solution", constructive part of this rant.

The sidebar I'm removing contains mostly text ads, but that's not the reason I'm removing it. In fact, if you came here looking for a way to remove those adds, I'd advise you not to. Well, sure, go ahead and remove the ads if they bother you, but if the sidebar doesn't bother you as well then don't get rid of an entire bar just to get rid of a few ads. The sidebar does contain a few useful things; I've never used its "New window", "Print all", nor "Expand all" buttons, but the bar does occasionally contain golden suggestions. It once parsed the text of the message I was reading, discovered that it was an invitation, and provided me with a button to add the event to my calendar. It even got the time, place, and event description right! I bet that Google will find more cool ways to understand our messages and provide us with smart buttons, but I'll miss out on them if I remove this sidebar. Oh well, I guess my hatred is greater than my enthusiasm.

Here is the script: wider gmail. It's a bookmarklet, so don't bother clicking on it now, it won't lead you anywhere. But if you put it in your toolbar, and click it once you're in Gmail's conversation mode, it will toggle the right-hand sidebar on and off. And if you'd rather have the bar hiden by default, here is a Greasemonkey script which should do the trick.

Friday, February 29, 2008

Gmail signature bookmarklet

update: As of November 2008, gmail now has a setting to place your signature above the quotation if you want to. Thanks Google!

---

When you reply to a message, Gmail creates a text editor with your cursor at the top, a giant quotation containing the message you're replying to, some useless double-colon separator, and your signature.

Gmail's default cursor location encourages top-posting, which I don't really mind. The problem is that the signature is bottom-bosted, so people tend to write a second signature above the giant quotation. A few wasted characters, not a big deal.

The real problem is that I know that Gmail has stuffed my signature down there at the very bottom, so I'm moving it back up every single time I reply to an email. Were I in Vim, this would be just another few wasted characters ("GD``p"), but I'm not and I grew annoyed. So I automated it.

Here it is: uppersig. It's a bookmarklet, so don't bother clicking on it now, it won't lead you anywhere. But if you put in in your toolbar, and click it immediately after hitting "reply", it will move the signature and refocus the editor.

Ok, so having to click a button everytime I reply to an email is also annoying, which is why I used to have a Greasemonkey script to do this automatically, but Gmail's dynamic javascript code is making this difficult.

edit: by the way, Google often changes internal Gmail details which break my script, but I fix it everytime, so just bookmark a fresh copy if you ever find that my bookmarklet stopped working. Last updated 2008-09-19.

Sunday, April 29, 2007

Of comonads and nightclubs

In this awesome explanation of monads, you have been building space stations under the master directive that all stations must output their astronauts in space suits. We're going to build nightclubs. In winter. Under the master directive that clients must take off their wintercoats before dancing.

We are especially concerned about the entrance procedure. There is a sequence of things which clients need to perform before they are allowed in. Maybe it's a private nightclub and you need to authenticate yourself. Maybe it's a high profile nightclub and you need to leave your limo keys to the valet. Maybe it's a paranoid nightclub and you need to be inspected for weapons. At one place I've been if you arrive early enough, they give you two empty glasses when you enter, and during the course of the night you can order exactly two free drinks. Maybe it's a rave and you need to buy enough drugs to scare sleep away for the entire night. Maybe there's a cover fee, and maybe it's lady's night so a girl's entrance procedure will be different from a guy's. And most importantly, you need to check your wintercoat at the coat check.

Now imagine you are working for a nightclub entrepreneur who had so much success so far that he plans to build a whole slew of different kinds of nightclubs. In consequence of his first success, he considers himself an expert of dancing floor design; he won't let you play near those. Nightclub entrances, on the other hand, they're all yours.

At the Entrance Parts Supermarket, you discover that entrances are made out of modular parts. There is a cover fee part, a parking part (valet not included), and even a case of part which can distinguish between guys and girls. There are pre-assembled kits, for common combinations such as a counter for both the cover fee and the coat check.

Your boss doesn't really understand the point of modular design. He insists that he will only pay for parts which make sure that clients check out their wintercoats. No amount of argumentation will convince him that a single nightclub entrance will only ever need a single coat check module, and that it's okay for the other modules not to repeat the same job. So you end up buying a bunch of reusable entrance parts which you can't even compose since once free of their wintercoats, clients won't be able to go through further wintercoat-removing steps.

Okay, so what did you do in the spacesuit example when you ended up with a whole bunch of incompatible space stations? You built a bind robot to extract the astronaut out of his spacesuit, feeding the astronaut into the next space station. Our bind robot will do exactly the opposite: in so far as a spacesuit can be thought of as a really expansive and overkill wintercoat, our bind robot (=>>) will put a client back into a suit, feeding the suited client into the next entrance part.

This system allows us to use any ordinary modular entrance part, provided it is available as a pre-assembled kit with a coat check included. It also allows us to use those fancy new parts which don't need to be grafted with coat checks, because taking clients out of their coats is part of their raison d'ĂȘtre. For instance, crooked nightclubs might want to use the all-in-one cover fee + coat check counter not as a pre-assembled kit, but as a simple coat check counter with crooks hired to steal any valuables from the coats. A nightclub featuring a lady's night might want to avoid the embarrassment of having to ask a "borderline" client what his/her gender is. Because of the (ridiculous) convention of placing a coat's zipper handle on one side for girl coats and on the other side for guys, a special coat check counter could provide a heuristic. At a beach-themed nightclub, clients could be required to put on swimwear, and it would make sense to provide lockers for clients to store both their street clothes and their wintercoats.

Monads were kinds of spaces, and different monads required different kinds of spacesuits with different procedures for astronauts to take them on and off. Some special space stations had a built-in ability to put astronauts into suits. A monad-specific robot named "bind" allowed ordinary and special space stations to intermingle. That was the whole point of monads: a framework to elegantly integrate special functions with the rest of our computation.

Comonads are kinds of snow, and different comonads require different kinds of wintercoats with different procedures for clients to take them on and off. Some special entrance parts have a built-in ability to take clients out of their wintercoats. A comonad-specific robot named "bind" allows ordinary and special entrance parts to intermingle. That is the whole point of comonads: a framework to elegantly integrate special functions with the rest of our computation.

Both monads and comonads are needed because they handle different categories of special functions. And bimonads, actually called arrows, can handle either kind, as well as brand new special functions which no monad nor comonad has ever dreamt of...

Saturday, April 28, 2007

I understand comonads

Upon discovering the incredible coolness of monads, I decided that the closely related concept of comonads had to be at least as interesting. Unfortunately, far fewer people seem to be interested in this little underestimated cousin, at least not enough for anyone to post a spacesuit metaphor for them. But as always, when proper documentation is lacking, it suffices to ask the source.

haskell class Monad m where haskell (>>=) :: m a -> (a -> m b) -> m b haskell return :: a -> m a haskell haskell class Comonad w where haskell (=>>) :: w a -> (w a -> b) -> w b haskell extract :: w a -> a

Okay, so this time the source didn't help much, since there are just types and no code. That's because monads and comonads are abstract classes (although Haskell probably uses a different terminology), whose implementation details are left to subclasses.

Yet types alone can say a lot. If I gave you a mysterious function whose type was (a -> a) and which worked for any possible type a, it would be pretty clear that I gave you the identity function. If I gave you a mysterious function whose type was (a -> (a -> b) -> b) and which worked for any possible types a and b, it would be pretty clear that I gave you a function which applies its second argument to the first. And that's exactly the kind of behaviours which the mysterious return, extract, (>>=), and (=>>) must exhibit, except they must deal with the extra m and w stuff as well.

Let's pause to ponder the apparent futility of a function which applies its second argument to the first. It is exactly like function application, but its arguments are in the opposite order. Why would anyone work backwards like this? Many people, in fact.

python abs(int("-42")) ruby "-42".to_i.abs

So "binding a value to a function" is just functional-programming jargon for the standard object-oriented way of chaining computations together. What is incredible with monads and comonads is that each different m and w provides a slightly different behaviour for this dot operator. Who knew we could need more than one?

A concrete monad or comonad implementation of a binding arrow, thus, simply applies its second argument to the first, and deals with the extra m or w stuff as well. Monad and comonad users must also deal with the extra m and w stuff, because they are the ones writing the (a -> m b) and (w a -> b) functions which (>>=) and (=>>) expect. Fortunately for them, return and extract can be used to lift their ordinary (a -> b) functions into a suitable form.

haskell lift_m :: Monad m => (a -> b) -> (a -> m b) haskell lift_m f a = return (f a) haskell haskell lift_w :: Comonad w => (a -> b) -> (w a -> b) haskell lift_w f a = f (extract a)

The big difference between using monads, comonads, or the ordinary dot operator is the kind of non-lifted functions which can be used. For the ordinary dot operator there are none, for monads there are functions which can stuff something extra along the return value, and for comonads there are functions which can extract something extra out of the input value.

A spacesuit example should follow soon.

Friday, November 10, 2006

Cumulative DSLs

I like first class constructs. Every time the language gurus out there manage to find an old thing they overlooked, a thing which wasn't first-class yet, and promote it to a first-class citizen, the programming world gets better. At least for those who aren't prevented from using the language by company rules.

It's not an issue of fairness at all. I'd like everything to be first-class, but not for the same reason that I'd like everyone to be allowed to vote, think, speak and live respectably. In fact it's quite the opposite: I'm being completely selfish.

I don't want the language designers to be involved in my design decisions. I want all the power for myself, because the solutions I dream up are fabulously elegant and who do they think they are, thinking in general-purpose terms and leaving the actual programmers that do the real work trapped with their domain-specific problems.

So I (sometimes) end up reinventing the wheel by writing my own domain-specific language. But my languages are small and boring and what I really wanted was all the general-purpose stuff plus this one new weird kind of method dispatch. And while we're at it, also throw in a new kind of literal for this custom type I always use. And a new module system which handles versions. And this new scoping rule for variables whose name begins with $$. If my general-purpose language had first-class dispatchers and first-class literal parsers and first-class module-resolution rules and first-class scoping rules, then I could implement all of this at my leisure. So what's stopping people from implementing a general-purpose language in which every single thing would truly be utterly and completely first-class?

I have an hypothesis: language designers are doing their best, but getting it right is simply really really hard.

Saturday, October 28, 2006

Monads as Universe Helpers

To see if the grass is greener on the other side of the programming fence, I've been accumulating a scaffolding of knowledge about functional programming which recently culminated in my understanding of the really cool and deep concept of monads.

Monads chain computations in a special way. There are many types of monads, and each chains its computations in its own special way. One of them chains computations in good old procedural fashion, keeping results around inside good old assignable variables and running computations according to our good friends the flow constructs; if, while, for and collaborators. Another familiar face is the layer-based architecture, the bureaucratic tower in which each floor's computation feeds the layer immediately above it. Another monad lets any computation abort part of the remaining computational chain by throwing an exception, and yet another generalizes the idea to the also-quite-cool concept of continuations.

Now, given that I already knew these useful models, was it really worth it to bang my head against the wall until monads finally made sense? Well, walls as just as rigid you would imagine them to be, so I would not recommend doing so if you're only looking for a new tool to add in your ever-growing bag of programming tricks. I believe the concept is more of a philosophical tool, actually. Or perhaps a mathematician's cute pet.

Remember what happened last time philosophers played with the concept of computability? Their fellow physicists had experimented with the world and made very impressive predictions about the behaviour of thrown balls and sliding blocks and complicated pulley systems. What do you know, it seems the world might be computable after all; determinism enters the scene and turns us all into emotionless mechanical contraptions.

The mechanical contraptions cried and argued against their fate, ridiculed defenceless pulley systems so they would feel better about themselves, and actively looked for escape clauses in their contract with the universe. Then one triumphal morning, quantum mechanics defeated the crooked determinism beast by invalidating science's dream of perfectly accurate predictions. What a relief; we could now choose to be emotionless lottery contraptions if we so wished. But of course what we really wanted all along was free will, not unpredictability.

Enter monads. And markov chains. You are an anthropomorphic dot moving in a large graph representing the universe. At each time-step, you must choose one of your node's outgoing edges and follow it to a new node. Some universe implementations may be deterministic or random about this and you won't be left with much of a choice. Some implementations might be kind enough to give you free will. But the gods who subcontracted the construction of the universe to our firm aren't quite sure which it will be yet, so we better make our implementation modular in that respect.

We also better make sure that our implementation can scale well, because universes tend to grow astronomically large. For one thing, we definitely don't want to fill up drive after drive with nodes and edges, so we will need to use a trick. We will only keep track of your current node and compute your possible outgoing edges, on the fly, whenever we need them. The architecture of our universe is thus composed of three parts: a function mapping nodes to possible actions (Laws), a current node (Creature), and that thing which chains executions of the functions using either free will or not (Monad). Yes, a free will monad; I told you this stuff was deep.

Sunday, August 20, 2006

Lisp is Low-Level

Lisp is built out of an amazing idea: like everything else in the machine, code is also data. This is true in any language, but Lisp's syntax makes the code especially easy to manipulate as data.

This feature allows new language constructs to be implemented as program transformations, without touching the core language. Given this uncommon ability, how could any language ever beat Lisp in terms of level of abstraction? Any missing functionality, it would seem, can only be temporarily missing, awaiting a sufficiently dedicated macro writer. Surely, Lisp must be the most high-level language ever designed!

Well, I think not. Lisp is low-level.

Macros may allow you to write programs more abstractly, but writing macros themselves is more painful than needed. A macro is manipulating a raw syntax tree, devoid of meaning, especially since it may contain new macro constructs it has never heard of. How can it be expected to do the right thing?

The only hope, for the poor macro, is to leave the new constructs alone. Thus, some constructs shadow the influence of other constructs, effectively preventing a class of independently developed language extensions from being used together. That's a shame, and that's a direct consequence of Lisp's macro facilities being too low-level tools.

What would a high-level macro system look like? Personally, my bet is on more advanced reflection mechanisms. In an upcoming post, I'll explain how reflection and macros are interrelated, and provide a much needed example to illustrate the kind of things which Lisp cannot do.