Saturday, September 27, 2008

Strategy vs. Tactics: The first presidential debate


Chess is a battle; Go is a war.
In the first presidential debate last night, I think Obama made an excellent point about the difference between a strategy and a tactic. Perhaps it's sometimes hard to tell the difference; it's a matter of scope.
If you compare: invading Iraq verses the troop surge, then the invasion is the strategy and the troop surge is the tactic.
If you compare the troop surge verses force dispersal then the troop surge is a strategy and the force dispersal is a tactic.
Some might say that if you compare the games of chess and checkers, that checkers is the tactical game and chess is the strategic game.
Then again if you compare chess with the game of Go, you might think, as has been said many times, that "Chess is a battle and Go is a war."
Near the end of the debate, Obama made a huge point about strategy verses tactics, that the strength of our military is dependent on the strength of our economy.
Here's a very paraphrased version of what he said. I've trimmed for coherence in writing verses speaking I've cut out things that aren't relevant to my point. Since I've changed it so much, I won't even put quotes around it. See the transcript for exactly what Obama said:
China is active in regions like Latin America, and Asia, and Africa. The conspicuousness of their presence is only matched by our absence, because we've been focused on Iraq.
We have weakened our capacity to project power around the world because we have viewed everything through this single lens, not to mention, look at our economy. We are now spending $10 billion or more every month on Iraq.
There has never been a country on Earth that saw its economy decline and yet maintained its military superiority.
The troop surge might have turned out to be a fine tactic in the context of a bad strategy. It's penny wise, pound foolish. So it's not a matter of making the right moves, it's the game you choose to play. Obama is right about the strategy, and Obama is right about which table we're sitting at, who we are sitting across from, and about the size of the board and the shape of the pieces.

Tuesday, September 2, 2008

Does Google's Chrome help with Mashups?


Google's new web browser, Chrome sounds like it's been nicely designed with security in mind, but their announcement and cute comic book don't say anything about mashups security. There are some interesting browser-side approaches to making javascript mashups less scary. I hope they've considered mashups in the design of their separation system.

If anyone has pointers to this topic, let me know.

Monday, September 1, 2008

Drubiquity: a proof-of-concept Ubiquity interface for Drupal


I've been pretty impressed with Ubiquity, a Firefox extension that combines web APIs with the command-line and some natural language processing, so you can compose together applications using javascript in a very unix-y way.

As an experiment and a weekend hack, I wanted to create an interface to Drupal, the awesome software that I run my blog on. I have something pretty cool working, but sadly, I can't really recommend using it, because the REST API that I integrated with has some serious problems. Maybe I'll get around to re-implementing this with the Services API system, but it's late Sunday and I haven't gotten around to that yet, so I thought I'd post. Anyone is welcome to pick this up and make it work for real :)
Read on for a screenshot and the source code.

Right now, what my ubiquity command does is queries the API of a Drupal site for the list of users, and matches that list against what the user types. So if the user types "drubiquity ijones", they'll get two users on my site, me and a test user.

The preview function uses the API to fetch the picture of the user that you're currently looking at and insert it asynchronously, as shown below. Then if you hit "enter" it will bring you to the blog for that user.

It should be simple to extend for some more uses. For instance, you could implement something like the "twitter" command to post a brief blog entry for a Drupal site, or the preview function could list recent blog entries or more info about the user. Leaning more toward the mashup idea, you could map user locations, or insert photo album pictures into an email, or click on an image and blog about it, etc. The fun thing is that lots of sites use Drupal, so anything you build with Ubiquity can be leveraged across all those sites.

Here's a screenshot and the source code in case anyone is interested. I should warn you that I'm not exactly a Javascript programmer, so your mileage may vary. I'll post again if I end up re-implementing this with the services API.

// ------------------------------------------------------------
// * Site specific things to modify:
// ------------------------------------------------------------

//change the command to something about your site:
const cmdName = "drubiquity";

//If you modify this, you are the author:
const currentAuthor = { name: "Isaac Potoczny-Jones", email: "ijones@syntaxpolice.org"};

//Set this to your home page for documentation purposes.
const currentHomepage = "http://www.syntaxpolice.org/";

//This should point to the root of your drupal site:
const sourceURL = "http://www.syntaxpolice.org/index.php/";

// This should point to the root of your rest / JSON API. If it's
// correct, you should be able to go to jsonURLRoot/help and see
// instructions for what's available.  You must install this pluggin:
// http://drupal.org/project/restapi

const jsonURLRoot = sourceURL + "?q=/=/";

//end site-specific modifications


// Dynamically match input to the list of usernames
var noun_type_user = {
  _name: "user name",
  userList: null,

  callback: function (users) {
        noun_type_user.userList = users.value;
    },

  suggest: function( text, html ) {
        //fetch the list of users and suggest them.
            var jsonURL = jsonURLRoot + "user/name.json";
            if (noun_type_user.userList == null) {
                jQuery.getJSON(jsonURL, noun_type_user.callback);
                return [];
            }
            if (text.length < 2) return [];
            
            var suggestions = [];
            for ( i = 0; i < noun_type_user.userList.length; i++ ) {
                var userName = noun_type_user.userList[i];
                if (userName.match(text, "i") && userName != "" )
                    suggestions.push(CmdUtils.makeSugg(userName));
            }
            return suggestions.splice(0, 5);
  }
} //username type


CmdUtils.CreateCommand({
  name: cmdName,
  homepage: currentHomepage,
  author: currentAuthor,

  contributors: ["Isaac Potoczny-Jones"],
  license: "BSD3",
  description: "Some commands to interact with Drupal."
           + " Requires the Rest API extension on the target site.",
  help: "Type '" 
            + cmdName + "' and you should get some hints.  You can search for users "
            + "by typing a few letters of their username. You should be presented "
            + "with a preview picture if they have one on file. Hit enter for the "
            + "given user, and you'll be taken to their blog.",

  takes: { "user": noun_type_user},

  preview: function( pblock, userName ) {
            //displays user's image if available.
            var prevMessage = "View blog for user: " + userName.text;
            pblock.innerHTML = prevMessage;
 
            getUserRec (userName,
                        function(user){
                            var picURL =  sourceURL + user.value.picture;
                            pblock.innerHTML = ""
                                + prevMessage;
                        });
  },

  execute: function( userName ) {
            //looks up user's uid and constructs blog URL from that.
            getUserRec (userName,
                        function (user) {
                            var blogURL = sourceURL + "?q=blog/" + user.value.uid;
                            Utils.openUrlInBrowser (blogURL);
                        });
        }
})

// looks up the user record given the user name object. callback takes user record
function getUserRec (userName, userCallback ) {

    var userNameSt = userName.text;
    var jsonURL = jsonURLRoot + "user/name/" + userNameSt + ".json";
    var prevMessage = "View blog for user: " + userNameSt;
    
    jQuery.getJSON(jsonURL, 
                   function(user){
                       userCallback (user);
                   });
   
}

Wednesday, July 23, 2008

A Range of Bikes for Commuting


Folks often ask me for bike buying advice, mostly for commuting, so I thought I'd drop in a few links here to bikes that look good to me for commuting on. Not that I've tried them all, but if you try them and like them, and they're in your price range, I think you'd be just fine buying them :)

You definitely have to test ride a few bikes to see what you like. Try a bike with flat handlebars and a bike with road handlebars.

Commuter bikes sorted by price:
  • $250 - 2007 Schwinn Sierra GSD Comfort Bike. Name brand components, nice price. Not sure how I feel about disc breaks on such a cheap bike since you can buy just the breaks for that price.
  • $300 - 2008 Mongoose Crossway 350 Comfort Bike. I think my brother got one of these and likes it.
  • $350 - Mongoose Kaldi Double Commuter Bike. Similar to above. I like the fenders for Portland commuting.
  • $490 - Trek SU 1.0 Urban. I think some folks at work have these SU bikes and like them.
  • $550 - Trek SU 2.0 Urban.
  • $865 - Jamis Aurora. Has road bike handlebars. Would be great for commuting or longer distance multi-day rides. You could learn a lot on this bike. I'd like to own one :)
  • $1700 - Trek Portland. A beautiful looking bike, good for Portland commuting. I would also be happy to own one of these.

    Other quick tips:
  • There are Performance shops all over the country. In Portland, Bike Gallery sells Trek bikes, and Veloche on Hawthorne sells the Jamis. Others probably sell them too.
  • For Portland, the awesome website By Cycle is Google Maps direction that puts you on nice bike routes. Very good for trip planning :)
  • Here's some general bike commuting advice.
  • Buy lights and a helmet.
  • You could buy a used bike on Craigslist, but unless you know what you're doing (or bring a friend along who knows what they're doing), you might end up with a dangerous bike that I would recommend against riding in traffic.
  • Saturday, June 28, 2008

    Costa Rica: Pura Vida


    For our first anniversary, and our honeymoon, Anna & I went to Costa Rica. We just got back on Wednesday. It was an incredible time. Costa Rica is a beautiful country, and everyone we met was very friendly and outgoing. I think my favorite thing was all of the cute or amazing animals. Also, lots of the restaurants were hardly even "inside". They were mostly open to the air, although covered, so sometimes frogs or kittens would be hanging out with you while you were eating.

    I had heard a lot of good things about Costa Rica, and I can wholeheartedly echo them all! Here are some photos below. Don't worry, we've already taken out repetitive pictures and cropped them :)

    We went during the rainy season, but it actually didn't rain on us except when it didn't really matter. When we were in the hot springs, it started pouring, but it was really warm rain, and it just made the experience of the hot waterfalls all the more intense. On the colder side of things, we also went whitewater rafting, and that was the only other time it rained while we were actually doing anything, so again it didn't really matter. We did get kinda lucky that it didn't rain while we were on the beach and stuff.

    Some random surprising facts about Costa Rica: Intel's microprocessor facility in Costa Rica is responsible for 20% of Costa Rican exports and 4.9% of the country's GDP. They actually make a lot of money from high tech and from tourism.

    After fighting and winning a civil war, a rebel force abolished the army elected an assembly, wrote a constitution, gave Blacks and women the right to vote, and then the rebel junta stepped out of power and Costa Rica became a democracy. Wow.

    On our first few days, we were in La Fortuna exploring the rain forest, relaxing by the pool, watching the live volcano erupt, and riding some ziplines. We also visited some hot springs which were awesomely beautiful and relaxing.

    Our first day in San Jose, we went to a butterfly garden. The tour guide seemed very into the butterflies. When she would pick one up, she would gently blow on it, and when Anna asked, she said it was just to say "hi" and to relax the butterfly.




    We flew out of San Jose to La Fortuna on this little airplane. No security, no bar codes on the boarding passes. No flight attendants.


    Here's me with some Iguanas. This tree was covered with them. Costa Ricans call them "Tree Chickens" because they taste like chicken. By the way they love telling you this, I must have heard that joke like 5 times. They don't eat them anymore because they're protected, and the population is therefore increasing again.


    In La Fortuna, we went on a boat on the Rio Frio and saw lots of animals, including several howler monkeys, this cayman alligator, as well as lizards who could walk on water, lots of birds, etc.

    The howler monkeys make a really cool, really loud noise that sounds like a huge gorilla or something, but they're really pretty small. Wikipedia says they are the loudest land mammal.

    La Fortuna has a big, active volcano, and we got to watch it erupting a bit, especially while we were doing these zipline rides. By the way, the ziplines were fun, but I wouldn't recommend them as being particularly interesting. They were billed as a kind of canopy tour, but really they were more like an amusement park ride.

    A pair of Macaw parrots lived in our hotel and this one beat me at foosball. Apparently they mate for life, according to wikipedia. These two were never apart whenever we saw them. Also, one of our cab drivers lived nearby and said he is in a battle with those parrots and they hate him. They did have a LOT of attitude.


    After La Fortuna, we flew back to San Jose, and then drove to Manuel Antonio where we toured the national park. This was the most amazing animal viewing we did. Our guide had this tripod-mounted monocular or telescope or something, and he would spot tiny or distant animals and set up the telescope to let us view them.

    Most of the time, we would try to spot whatever it was, a tiny frog or group of bats or something with the naked eye, and we couldn't see it because these animals all have great camouflage. Several of these pictures were taken through that lens, which is why they have such high resolution.

    This is a three-toed sloth, which are hard to spot and very slow moving. Anna actually spotted one on her own at our hotel one day. Mostly, they look like a ball of fur, but with the telescope, we could actually get a look at its face. I still can't actually figure out what a sloth looks like altogether because all of the photos of them just looked photoshopped to me. Like, where is that arm coming from?!

    This frog was tiny and hard to see :)

    Lounging Lizard:

    One of my favorite animals was the capuchin monkey. They were so cute and curious, and they were kleptomaniacs, but they didn't steal anything from me. A few days later, we got to watch an entire troupe move through the forest while we were kayaking, and they came over to check us out, so we got to see them really close. It was really fun to watch them jump around between the trees and run across the ground. We also saw a little baby all alone following the big group, but a minute later, mom came over and baby climbed up on mom and they zoomed off.

    Their faces look so old and wise and mysterious, but then they act like little kids. Some stupid tourist was feeding some of them (which is really bad for them) and she was teasing them with a banana. When he didn't get the treat, one of the monkeys ran over to someone's backpack and reached into the pocket and tried to grab something, but someone chased him away.

    This was while we were on the beach, and there are all kinds of things trying to get you and your stuff. We saw that monkey try to grab something, we saw a raccoon trying to get into a bag, not to mention the thieves that we didn't see but were warned about approximately 100 times. Also, there was a poisonous tree on the beach with a sign saying not to sit under it. In Spanish, this tree is called manzanilla de la muerte, or "little apple of death". The Iguanas can actually eat the fruits, though.
    Here's a beach photo. We swam in the warm waters a lot. This was on the pacific side, by the way, not the Caribbean.

    Our hotel in Manuel Antonio was so beautiful. Definitely the nicest place we've ever stayed in. The room even had an etched window. Most everywhere we went, except San Jose, looked this lush. Costa Rica is kinda like Portland in that it gives the impression that if you stopped fighting off the plants, they would just take over.

    After Manuel Antonio, we drove back to San Jose in a rented car. This was about 200km, and the roads were insane. They were really winding, and everyone wanted to pass everyone all the time. Also there were features like dirt roads, and the roads didn't have names mostly.

    Then there were these insane "bridges" that weren't originally meant for cars but for trains, where you'd have to wait for 10 minutes to cross because there were folks crossing in the other direction. To the left, you can see that they're building a new bridge, which is like half done, but it looked better already than the one I was using!

    There were lots of school kids out in uniform and lots of people on bikes in all kinds of weather. These two look like they're having a great time.

    So that's it!

    Lots of love,
    isaac & anna

    Saturday, May 10, 2008

    OpenID Patterns: The Good, The Bad, and The Ugly


    In looking at how my blog does OpenID login and comments, I was really wishing that it did what I would expect: When someone wants to post a comment, all I care about is their identity (which is mostly just to show that they're not impersonating someone else), and whether or not they are a spammer.

    The Good

    OpenID and Captcha is all you need to comment
    So ideally, my blog could just ask for a commenter's OpenID and have them answer a captcha. Unfortunately, Drupal's current OpenID implementation has the same problem as others I've seen, which is the obsession with people having an "account".

    A site's OpenID implementation should not require an account, password, confirmation of email, and an OpenID. I was excited when I realized that Blogger has the right interface for commenting; an OpenID login, and a captcha (don't tell any spammers what it says!):

    The Bad

    I need your Google password to continue
    I was all ready to give Blogger all kinds of props when I noticed that their login screen asks me for my Google password. Now, Blogger is actually owned by Google, but not everyone knows that, and so this looks like a phishing attack.

    You should not give out your Google password to a third party web site. It's just a bad idea. One simple example why: Email can often be used to reset your password to another web site that has your private information in it. It's bad enough that sites like Twitter ask for your Google password when creating an account. Google shouldn't make people think it's OK to give third party sites your password by using a login screen like this. That's bad.

    The Ugly

    I have descended into a no-man's land on LiveJournal
    I saw that LiveJournal supports OpenID and I thought I'd try it out. Their comment system looks fine, but then the integration with everything else is just a mess. I'm logged in, but I don't have an "account". The first thing I see when I log in is a link that says "Update your Journal" but when I click it, I get an error message saying that I don't have one. I can configure my account, and it says that it has emailed me instructions (I never got them) and gave me a helpfully random URL as my blog, which, when I click it, is just an error. LiveJournal makes OpenID look broken and hard to use :(

    I admit, LJ gave me fair warning, "Our OpenID consumer support is very new. That is, external users logging in with their identity here will find some rough edges while we work on smoothing it all out."
    This has been the state of affairs for months, though, and I'm surprised that they can't at least give me a link to somehow create the right kind of account.

    If you know of any other really elegant uses of OpenID or OpenID design patterns, please email me, and I'll post whatever I collect later. You can also leave a comment on Reddit. You are welcome to leave a comment below, but you'll have to create an account and answer a captcha. Sorry about that ;)

    Monday, May 5, 2008

    feed-cli: A Really Simple way to generate RSS feeds


    I've just released feed-cli: a Really Simple way to generate RSS feeds from the command line. This program is implemented in Haskell :)

    feed-cli generates RSS 2.0 feeds based on command line arguments. Use it to create and update feeds from shell scripts, build scripts, cron jobs, CGIs, or other programs instead of using a library.

    Some Examples
  • create an empty feed:
    ./feed-cli new-feed -tTitleOfFeed -d"Feed Description" \
      -o/tmp/feed.xml  -lhttp://www.syntaxpolice.org
    
  • add an item to that feed
    ./feed-cli new-item -t"entry of the day" -d"This is a description of this feed item." \
       -u/tmp/feed.xml  -lhttp://www.syntaxpolice.org
    
  • pipe a command into a feed item
    ls -l | ./feed-cli new-item --pre --pipe-mode -t"directory contents" \
      -u/tmp/feed.xml  -lhttp://www.syntaxpolice.org
    
    You can get feed-cli from Hackage, the Haskell Package system, along with it's dependencies, xml and feed. Or you can use my darcs repo. It also requires ghc 6.8, but that's not for any deep reason. If you have cabal-install installed, you should be able to "cabal install feed-cli".

    The idea is to make generating feeds as simple as possible, so feel free to package it for your favorite OS :)

    It should be pretty simple to support atom feeds as well, since the feed library already does that. I'd like to extend the feed library itself with more functionality along the lines that feed-cli implements - adding feed items, limiting the number of items in a feed, etc. Simple feed transformers. I think this is what Sigbjorn had in mind when he wrote the feed library.

    Thanks to my company, Galois for releasing xml & feed.

    Try it out and leave a comment or send an email and tell me about how you use it and whether there are more features that you need.
  • Tuesday, April 22, 2008

    Securing User-Centric Mashup Applications


    Here is a little whitepaper written primarily by Sigbjorn with help from me and Eric from Galois

    Abstract
    Having the ability to easily combine together information from a number of disparate input sources into a greater whole is a touted benefit of 'Web2.0' mashup applications. They have great promise as flexible, and user-tailored ways to both disseminate and collaborate on information on the web, but with today's web technology, face a number of security risks when being asked to also aggregate restricted information sources. This paper introduces the domain and what these risks are, along with suggested mashup application architectures that are more secure.

    Monday, April 21, 2008

    Play


    Lately I've been thinking about the concept of "Play".

    When I first learned the game of Go, I was just messing around and it was really fun. I was playing. Then I got a little bit better at it and started caring if I was winning. It stopped being quite so fun. It started being Serious.

    Sometimes we can do the same thing at home as we do for our work and it's fun and playful. I think a lot of open source developers have that experience.

    Play is when it's almost like the result doesn't matter. It doesn't matter if you win or lose or if your program gets used by a bunch of people, or if you sell the product.

    Play is when it's almost like standards don't matter. You'll be as organized about things as you want to be. You'll use good coding conventions if you feel like it. You'll write detailed documentation if it doesn't take too much time. You'll play Go with someone who is not at your level just for kicks, even though it doesn't help you to get better.

    Play is going for a walk in the woods not to get exercise or to get enlightenment, but just because you enjoy it.

    In fact, despite what I said above, you can care about the end result if that's part of your joy, and you can care about the standards if that's part of your joy. When I was talking to my friend Dylan about this, he said that he races better when he's just playing. That's also why some people love their jobs.

    You know you are Playing when you find Joy in the action itself and those other pieces are incidental. Go Play :)

    Wednesday, April 9, 2008

    A Dissident Camera that Can't Be Confiscated?


    The rise in the use of cell phone cameras and cheap, high-quality video gear has been a really interesting development for journalism. Citizens routinely video or photo monitor the behavior of police at protests, for instance, and upload them to the Internet.

    One problem with this is that the camera or digital media can be confiscated as the the police in Portland allegedly did (and this is apparently common, as noted here, here, here, here, and here).

    But with the combination of cheap, high quality cameras with cheap, portable WiFi devices, this social problem can be overcome with a technical solution. It should be pretty easy to build a camera device which uploads video via wifi directly to a site like youtube or a server outside of repressive countries.

    A simple solution that could work almost right now without any hardware modification would be to write some shell scripts for the Nokia N810 or similar. These devices have everything you need: A built-in video camera, a wifi card, and it's based on Linux, so you can program for it, and there are already ssh clients available.

    Here are a few problems with that: Internet is not available everywhere in most cities (but cell phone connections are), and the Nokia doesn't have a very high quality camera.

    A more complex but flexible solution would be to add a bluetooth card to an existing high quality camera (here's a video camera and a still camera that already have bluetooth). The Bluetooth card could transmit photos to an Internet-enabled cell phone in your pocket like the Palm Treo, which could upload it to youtube or what-have-you. Similarly, a video-enabled cell phone like that Treo could upload the video directly to youtube.

    Remember, you don't have to stream the video at viewable speed, just fast enough so that if it gets confiscated you will have already uploaded it.

    One problem with that approach is that Bluetooth is kinda slow. It also requires three devices (camera, phone, and Internet server), and the camera and phone would require specific programming which isn't necessarily that easy to do since they're often based on proprietary platforms.

    Anyway, this is bound to happen eventually, and maybe has already? Does anyone sell a digital video camera with an "upload to youtube" feature built-in?

    Friday, April 4, 2008

    My Commute


    My commute starts in the relaxed, low key South East of Portland where I ride through neighborhood streets that have more cyclists than cars. On my way home along this road, the view of blinking bike tail lights is always an inspiration.

    I meet the most cyclists while crossing the river, though. There are two lanes here on the bridge and the faster cyclists take the left lane. It gets tight further on, and not everyone gives the pedestrians the space they need.

    Once I hit downtown, it's mostly car traffic. There are bike lanes, but the construction can be distracting and disruptive. Throughout the downtown commute, I can easily keep up with the cars. There are so many cars that they slow each-other down, but lots of them don't know this.

    The downtown part of my rides finishes at the top of a hill, and then I get to bomb down it, across the commuter train tracks and past the train stop. Most days, when the weather is nice, instead of turning left to get onto the train, I turn right up another hill.

    This hill is surprisingly steep. This is probably the hardest part of the ride. The people here seem to have bigger cars than downtown, and they are more distracted. I'm not moving particularly fast here so they zoom by me.

    Then it's relief: This hill ends in a beautiful park. Often I won't see any cars for the entire trip through the park. In a few minutes, I'll ride past an awesome view of the mountain, weather permitting.

    It's a steady climb from here to the top of the hills. The steepest part is fortunately pretty short and I like to tackle it quickly to get it over with. Pretty soon, I'll ride by the zoo and often hear loud bird sounds or something.

    I like the downhill part of the ride because I don't have to take it too slowly. There's no traffic, just a bike lane beside the highway. Very occasionally, I'll be going faster than the cars on the highway, but not usually.

    Then I hit the suburbs. The drivers here are extremely distracted, impatient, and driving large cars. The streets are designed to direct the traffic onto particular streets, so I have learned to avoid those streets and ride over the speed bumps.

    At the end, I ride across the train tracks again. I could have taken the train all the way in, but this was much nicer :)

    Sunday, January 27, 2008

    Capitalism for the Poor?


    Recently I heard an interview with Muhammad Yunus where he talked about his new book, Creating a World Without Poverty. Yunus is one of the pioneers of microcredit, which is the "extension of very small loans (microloans) to the unemployed, to poor entrepreneurs and to others living in poverty". He also won the Nobel Peace Prize for his work.

    The idea is that these microloans help poor folks to get out of debt patterns and to bootstrap making money. These loans are often given to collectives of women, who are more likely to pay them back.

    It's such a cool idea, and very exciting that it apparently works. I don't really know anything much about Yunus or his work, but I'm really interested to read his new book and see what big ideas he's been having.
    Bill Gates has recently talked about a new kind of "creative capitalism" from businesses to help improve the lives of the poor. (Maybe Gates is trying to redeem himself.)

    Most of his examples sound very similar to what Yunus was talking about in the interview. Maybe they're working together? We can all hope it's not a new kind of "Embrace, Extend, and Extinguish" tactic.

    Tuesday, January 1, 2008

    Equality operators in PHP and Haskell


    Sometimes, developers at my company work in PHP, and I noticed the following chart posted up on the wall nearby. This chart shows by example the semantics of two different equality operators in PHP:
    PHP: Loose comparisons with ==
    TRUE FALSE 1 0 -1 "1" "0" "-1" NULL array() "php" ""
    TRUE TRUE FALSE TRUE FALSE TRUE TRUE FALSE TRUE FALSE FALSE TRUE FALSE
    FALSE FALSE TRUE FALSE TRUE FALSE FALSE TRUE FALSE TRUE TRUE FALSE TRUE
    1 TRUE FALSE TRUE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE
    0 FALSE TRUE FALSE TRUE FALSE FALSE TRUE FALSE TRUE FALSE TRUE TRUE
    -1 TRUE FALSE FALSE FALSE TRUE FALSE FALSE TRUE FALSE FALSE FALSE FALSE
    "1" TRUE FALSE TRUE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE
    "0" FALSE TRUE FALSE TRUE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE
    "-1" TRUE FALSE FALSE FALSE TRUE FALSE FALSE TRUE FALSE FALSE FALSE FALSE
    NULL FALSE TRUE FALSE TRUE FALSE FALSE FALSE FALSE TRUE TRUE FALSE TRUE
    array() FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE TRUE TRUE FALSE FALSE
    "php" TRUE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE
    "" FALSE TRUE FALSE TRUE FALSE FALSE FALSE FALSE TRUE FALSE FALSE TRUE


    This means you can get the following non-transitive behavior:"-1" == TRUE, TRUE == "1", but "-1" != "1"
    The following chart for the strict equality operator is a lot more sane. Notice that it has the expected TRUE values along the diagonal.
    PHP: Strict comparisons with ===
    TRUE FALSE 1 0 -1 "1" "0" "-1" NULL array() "php" ""
    TRUE TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
    FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
    1 FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
    0 FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
    -1 FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
    "1" FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE
    "0" FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE
    "-1" FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE
    NULL FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE
    array() FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE
    "php" FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE
    "" FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE


    In Haskell, the situation is quite a bit easier. Lots of these comparisons just aren't allowed by the compiler, which I show as "-":
    Equality in Haskell ==
    True False 1 0 -1 "1" "0" "-1" [] "Haskell" ""
    True True False - - - - - - - - -
    False False True - - - - - - - - -
    1 - - True False False - - - - - -
    0 - - False True False - - - - - -
    -1 - - False False True - - - - - -
    "1" - - - - - True False False False False False
    "0" - - - - - False True False False False False
    "-1" - - - - - False False True False False False
    [] - - - - - False False False True False -True-
    "Haskell" - - - - - False False False False True False
    "" - - - - - False False False -True- False True


    Here I use the empty list instead of the empty array. Once you get past the types, the only real difference with the PHP is that "" == []. That's because a String is a list of Chars in Haskell. List is probably the best comparison with array in PHP in this case, but it doesn't match exactly.
    Basically, we have three types represented here:
    Bool: True, False
    String or [Char]: "1", "0", "-1", "", "Haskell", []
    Integer: 0, 1, -1
    And Haskell doesn't have a NULL constant, as far as I know.
    The PHP philosophy seems to be that comparing a number to a string is so important that it created these really strangely behaved operators to do it conveniently.
    If you need to compare a Num with a String in Haskell, what do you do? The obvious and wrong thing to do is this:
    > read "1" == 1
    True
    
    The "read" function parses the "1" (a String value), tries to turn it into a Num, and then compares them.
    But what happens if that input isn't actually a numeric value:
    > read "that's no number" == 1
    *** Exception: Prelude.read: no parse
    
    Owch! The Haskell runtime threw an exception instead of returning "False". That means the program crashes, so you can't just do it the "easy" way. Instead, you need to parse the String value, and if the parse fails, then return False, but if it succeeds, then go ahead and compare the result of the parse.
    Here's the code I came up with after some discussion on #haskell. Maybe someone can come up with something better:
    numEqString :: Integer -> String -> Bool
    numEqString n s =
      case reads s of
        [(n2, "")] -> n2 == n
         -- parsing worked, so safe to compare
        _ -> False -- parsing failed
    
    Now we can do something like this:
    > numEqString 1 "1"
    True
    > numEqString 1 "that's not a number"
    False
    
    Our function works, and it's not too hard to use at all. I personally like it better than being able to say "1" == 1. You could come up with a better name for it, though.
    What I don't like is that it was hard to write numEqString. It's not at all obvious how to write it, and the line:
    [(n2, "")] -> n2 == n
    looks like total magic unless you understand reads. What it really means is something like "If it parses successfully, and the parse is unambiguous, and there is no text left over, then compare n2 to n."
    This would all be a bit easier if there were a maybeRead function, which I've seen defined by at least two projects:
    maybeRead :: Read a => String -> Maybe a
    maybeRead s =
      case reads s of
        [(x, "")] -> Just x
        _         -> Nothing
    
    That's something that maybe we should consider adding to Text.Read, but it's definitely still not as easy as it is in PHP.
    Anyone picking up PHP can easily write "1" == 1. In Haskell, there's no built-in function to do this, and implementing one requires strong familiarity with the parsing libraries.
    We should learn from PHP here. Maybe this is an important pattern to support, and if so, we should have a convenient way to do it, probably in a standard library (not as a built-in language feature). There's no reason that it should be hard in Haskell. We can make it easy, and we can make it safe.
    Thanks to folks on #haskell for discussion on this post.
    There's a discussion thread on Reddit about this article.