Standard ML vs. Python
Posted by postfuturist on 2012-01-19 23:03:49

I've been interested more and more in functional programming languages. Recently, I read through the first 6 chapters of Learn You a Haskell and I was intrigued but the lazy everything and functional purity seem a bit extreme for my tastes at the moment. Not long after I was taking a peek at Standard ML and recognized some similarities with Haskell, mainly the syntax. I also noticed that it is strict (as opposed to lazy) and impure functional. That happens to match my tastes at the moment as I've been into Clojure which is also strict (though sequences tend to be lazy by default) and impure functional. I'll get back to Haskell. For the moment, it's too many new things at once, so Standard ML seems like maybe a good stepping stone to Haskell.

I found a great free book by one of the designers of Standard ML: Programming in ML. I went through the first 12 or so chapters of that book easily. I almost didn't make it through the first chapter, because the author introduces some rather confusing code examples and speaks about them at a high level. Just trying to figure out what the code does without knowing how Standard ML works is very confusing. It's like an introduction to Chinese textbook starting with an analysis of a Chinese poem without translation. The good news is that you can skip that chapter. After the confusing high level analysis in the first chapter, the book systematically builds up the language piece by piece so normal folks like me can understand it.

I don't know why, but the language really strikes a chord with me. It is statically typed, but type-inferred, so you rarely need to specify types in the actual code. This makes a lot of sense to me. When I write code in a dynamic language, like Python, I know what the types are 99% of the time anyways. It's nice to have the compiler figure those out and then let me know when I've violated that. That said, the type system isn't quite as flexible as Python's duck typing so it takes a bit more work.

I wrote a simple version of Conway's Game of Life using a 5 by 5 grid stored as a Standard ML Vector which is, nicely, an immutable data structure. You can see the code here: https://github.com/deliciousrobots/sml_life. The code is sloppy and not optimized. After getting it working in Standard ML, I translated the code to Python 2.7 to compare the experience.

Code length: The SML version is 66 lines and the Python version is 58 lines. Neither one has comments, and some of the lines in both are blank. Python has a slight edge here, and the syntax is flexible enough in both languages that both solutions could have been shortened quite a bit (at the expense of clarity). SML's pattern matching syntax which isn't available in Python helped it stay competitive in this measurement, but idiomatic SML has the "end" on its own line, and lot of code blocks start with a "let" on its own line, too.

Functional purity: The SML version is completely functional. The language allows "mutation" by having a mutable "ref" type as well as arrays which have mutable cells. But normal variables are only bound once, and immutable. For the grid itself, I used the Vector type which is immutable. To calculate 100000 generations of the grid, I used a tail-recursive function in SML. When I first translated the code to Python, I forgot that Python doesn't optimize for tail recursion, so it blew the stack pretty quickly, I had to change it to an imperative loop, reassigning the same internal function variable.

Efficiency: The algorithm I use is pretty naive, but similar between the two implementations. That said, I Vector in the SML code and therefore had to use Vector specific functions like Vector.map. In the Python code, I just used a list and the builtin map function. That was nice, but when it came time to optimize the Python code, I couldn't really do anything to make it run faster. I tried replacing the list with an "array" of long integer using the "array" from Python's standard library, but the map function just outputs a normal list, there's no special "array.map" function which will process an array to an array. I ran the SML code with MLton, an optimizing compiler. 100000 iterations takes 0.7 seconds. CPython 2.7.2 does the same work in an embarrassingly long 26.8 seconds. My attempts at giving the code some type hinting by using the typed arrays from Python's standard library did not make the code any faster. It was actually a bit slower. I think the typed arrays in Python are probably more for memory consumption than algorithmic speed. PyPy 1.7 fared much better than CPython, and completed the benchmark in 6.4 seconds, a full 4 times faster than CPython, but still 9 times slower than MLton.

Ease of Development: This was close. I had to be a little more careful about type issues with the SML code. While writing the SML code, I would write some code, the compiler would complain, and I'd fix it, and without fail, when the compiler was happy, the output of the code was correct. I never had a runtime exception although they are certainly possible with SML. While writing the Python version, I rarely had compilation or syntax errors, but I runtime errors from mistyped variable or function names or other problems like the stack overflow I mentioned earlier. Python doesn't care if you reassign a variable to a different type than it was intially, it just explodes later when the code tries to do an invalid operation on the object. All in all, it felt like a similar experience, seeing the compiler error in SML and the uncaught exception in Python. I feel like in a larger codebase the compiler error right now is much mor helpful than the uncaught exception 10 minutes from now when I run the unit tests, or 2 hours later after I've written new unit tests that break, or 2 weeks later when the code breaks in production because of an edge case the tests didn't catch.

Final thoughts: I really like Python for getting things done. The duck typing and completely dynamic nature means that you have to test very carefully and not take anything for granted. After dealing with very large production systems written in dynamic languages, I'm ready for a little type-safety built into my programming languages. Standard ML is pretty nice to code in, despite the sometimes oppressive type system. It is fairly terse and powerful. Where functional features like the lambda syntax feels tacked on and insufficient in Python, it seems very natural and designed in SML. Python doesn't really have great support for immutable data structures and its a slow language. However, Python has an infinitely larger ecosystem so its pretty easy to get things done with it. Standard ML seems nearly dead, which is a shame. But as far as the ML family of languages go, I think Ocaml and Haskell are alive and kicking. Stay tuned.

Essential Firefox Addons For Privacy
Posted by postfuturist on 2011-12-31 00:58:27

Most people have no idea of how closely everything they do is tracked on the internet. A web browser, like Firefox, is called a User Agent. In other words, the browser acts on the user's behalf. Web browsers, by default, are not acting in the user's best interest and are generally bending over backwards to share more information with websites visited than is necessary.

First of all, when performing a search on a search engine, the search query is generally stored in the URL of the search results. When you click on a link from one site to another, your browser sends a bit of information in the request that identifies the URL that the request was linked from. This is called the REFERER header (yes, it is misspelled). Since your search results page contains the search you performed, your web browser is volunteering this information, so the web site you visit knows the search text you performed to find their page. It's not required to send that information, but your web browser, acting as your agent, gladly gives that information. You can change this default behavior with a Firefox plugin called RefControl. After installing it, you must change the default settings to actually block that information. I set the default to block 3rd party REFERER information which blocks links between different web pages, like the search engine link example.

Web pages contains bits of code and assets housed on separate servers which exist just to track your movements on the web for purposes of targetting advertisements etc. Usually this slows down page load times and allows ad networks (and Facebook, Twitter, Google, etc) to track your movements around the web. There's a plugin to block that, too, called Ghostery.

I think the default browser behavior is terrible regarding privacy, and these addons give users control over how much information they want to share.

Get Postgres Working on Ubuntu or Linux Mint
Posted by postfuturist on 2011-12-13 23:57:38

Warning: this post may contain profanity. I'm a programmer, I don't like dealing with sysadmin type things.

I love PostgreSQL, the baddest-ass relational database on the planet. For real.

Ugh, but it's super confusing to set up if you are used to just setting up MySQL users with passwords and then connecting to the database with said username and password you set up. Postgres has some odd defaults that make this less than straight forward.

Install PostgreSQL server on your machine:

sudo apt-get install postgresql-9.1

Create a database user, enter "somepass" or whatever you want for the password when it asks (answer no the create roles question):

sudo -u postgres createuser -D -P someuser

Create a database owned by that database user you just created:

sudo -u postgres createdb -O someuser somedb

For "fun" let's try to connect to the database with the "psql" interactive postgres shell:

psql -d somedb -U someuser -W

And the payoff:

Password for user someuser: 
psql: FATAL:  Peer authentication failed for user "someuser"

Oops. Your password didn't work, asshole.

Instead, you need to open up this file /etc/postgresql/9.1/main/pg_hba.conf in a text editor (with root privileges, of course) and change this line:

local   all             all                                     peer

to this:

local   all             all                                     md5

Now, restart the postgres server:

sudo service postgresql restart

Now you can connect:

psql -d somedb -U someuser -W

And this is what you get:

Password for user someuser: 
psql (9.1.1)
Type "help" for help.

somedb=>

You're welcome.

Ubuntu 11.10 on Lenovo Ideapad Z360
Posted by postfuturist on 2011-10-15 11:31:57

Suspend / Resume: I've had this Lenovo Ideapad Z360 for maybe most of a year and it's been a little bit of a pain running Linux. The kernel support for suspend/resume functionality has been buggy, meaning about 1/3 of the time suspending the laptop would put it into a battery-sucking catatonic state forcing me to power it down, losing any running programs. What a pain. In the year 2011, people don't close programs and shutdown laptops when they are done using it. Except for me. Until Ubuntu 11.10, that is, which ships with the fresh-off-the-git-repo Linux 3.0 kernel. UPDATE: Just kidding. In accordance with Murphy's Law, suspending the laptop failed after I wrote this post.

Backlight Control: Another issue with this laptop is backlight control. It doesn't work, out of the box. Fortunately, you can easily remedy this by adding a boot parameter of "acpi_backlight=vendor". Read more about how to do that here.

Turning Off Screen: OK, I've got bad news. For me, when the screen gets turned off when the laptop is not being used, it doesn't turn back on. It does turn back on when resuming from suspended state, but not just the normal power-saving functionality of having the screen turn off after a period of non-use. It is easy enough to disable this functionality. Open the Screen configuration program, and set "Turn off after:" to "Never". In the Power settings, I have it set to suspend after a period of disuse when not plugged in. This saves more power than just turning off the screen anyways. When the laptop is plugged in, I don't really want the screen to turn off anyways, since I might be watching Hulu.

Upgrading 11.04 to 11.10: Bad news: it takes a long time (but you already knew that). Good news: nothing broke. Let me repeat that: Nothing. Broke. OK, you get it. Of course, this is just my experience, yours may be different. I even upgraded my desktop workstation at work. When it got to the end of the install and required a reboot, I paused my VirtualBox VM's, rebooted, fired up the VM's and kept working like nothing had happened. The only annoyance was that the system default monospace font got set to something ugly, which made the terminal ugly until I changed the terminal settings to use a different monospace font.

Banshee: Still crashes periodically and silently. I like Banshee a lot, but I don't like that it randomly disappears. One second it's playing mp3's in the background, the next there's no music, no popup telling me the program has crashed, nothing. It's not running at all. It's like the program decides to exit all on its own. When I reopen Banshee, there's no apology for crashing, it starts up like nothing weird happened. This happens about once every other day. It has happened to me once since the upgrade to 11.10, about 1 hour after the upgrade and reboot. I understand that Banshee is built on top of a lot of different libraries for decoding audio, reading tags, etc and that its stability relies in part on the stability of the underlying libraries, but I think they should put some effort into catching these segfaults and at least handling the crashes in such a way that I can collect information and submit a bug report.

Everything else: Works great. So far. I'll add other issues if I find them.

No Longer Deluded
Posted by postfuturist on 2011-05-27 19:05:14

I've never been so ashamed as I am of the things I used to believe.

I used to think that the world we live on, Earth, and all the living creatures were created magically in a few days by an all-powerful deity floating over the water several thousand year ago. Why in the modern age of scientific knowledge did I believe something contradicted so easily by mountains (yes, literal mountains with regards to geology) of evidence to the contrary? Easy. I was taught those things because that is what is written in a certain sacred text. I was a child, too, so I believed the things that were told to me by adults.

I used to believe that I possessed an immortal soul that transcends my physical being, but that's rubbish. When my brain activity goes below a certain threshold, I have no consciousness of any kind. This happens at various times while I am asleep, and whenever I have a medical procedure involving anesthesia. If I get hit hard in the head, my consciousness is temporarily reduced or even gone. There is a very clear connection between the physical brain and consciousness and no evidence whatsoever to support consciousness existing outside of that. If there is an immortal soul that is part of my being, I don't much care for it, as once my body dies I am quite certain that all consciousness will end. So why did I believe that? I was told that as a young child.

I used to believe there was a deity who was intimately concerned with the sexuality and sexual decisions and thoughts made by everyone. I was taught that this was for our own benefit. So, an all-powerful deity is concerned with people's consensual sexual behavior while all the time millions of people are literally starving to death and this deity could feed them, but doesn't. Instead it is concerned with arbitrary sexual rules--because it loves us.

I used to believe that this deity was so unhappy with our arbitrary rule-breaking that the only way to make up for it was that we all have to die and then suffer for eternity. It doesn't even make sense, as suffering is very much tied to our consciousness and our physical bodies, both of which do not last after death. But, that's what had to happen. The loving deity's answer to rule breaking was eternal torture. And then, out of a love motive, this all-powerful deity sent himself to exist as one of us, though without sexuality, apparently, and then to suffer for a very short period of time and then die and then un-die magically and that somehow was an equivalent payment for billions of people suffering an eternity of post-death torment. I was taught that as a young child.

I used to believe that an all-powerful, all-loving deity would gladly heal everyone who asked. Of course, not everyone who asks gets healed, there isn't even a statistically significant correlation between asking for healing and having improved health. In fact, the whole concept of faith healing is very silly. If there was a loving deity capable of healing me, if I asked, why were so many people suffering constantly. I bet many of them cry out to this and other deities on a daily basis and are never healed. Why on earth did I believe such lunacy? Well, I was taught that as a boy.

Slowly, one-by-one, each of these beliefs was repeatedly worn away in my life, starting with all of science making a complete mockery of creationism and ending with the realization that I'd never seen any hard evidence for any of my fantastical religious beliefs.

There was a period of time where I didn't believe any anti-scientific beliefs at all, but still held to the idea of a deity existing in some alternate reality that loved everyone but for some reason was unwilling to help us other than founding a religion that looks a lot like a lot of other religions. But that melted away, too. Now I'm left to look back over my life of ignorance and slow awakening and wonder what I can do to help people embrace rational thought instead of irrational belief in ridiculous stories and fantastical concepts.

Ubuntu: Reset MySQL root password (Natty)
Posted by postfuturist on 2011-05-15 13:11:08

I needed to reset the root mysql password on an Ubuntu 11.04 installation. The /etc/init.d script apparently used to have a reset-password option that no longer exists. Here's what I did.

Stop mysql daemon:

$ sudo service mysql stop

Start mysqld with --skip-grant-table option in one terminal:

$ sudo mysqld --skip-grant-table

Use mysql shell to reset root password:

$ mysql
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 1
Server version: 5.1.54-1ubuntu4 (Ubuntu)

Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
This software comes with ABSOLUTELY NO WARRANTY. This is free software,
and you are welcome to modify and redistribute it under the GPL v2 license

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> UPDATE mysql.user SET Password=PASSWORD('BlahBlah123') WHERE User='root';
Query OK, 3 rows affected (0.00 sec)
Rows matched: 3  Changed: 3  Warnings: 0

mysql> FLUSH PRIVILEGES;
Query OK, 0 rows affected (0.00 sec)

mysql> Bye

Find the process id of mysqld and kill it (not kill -9, that's always a last resort):

$ ps -e | grep mysqld
 4910 pts/1    00:00:00 mysqld
$ sudo kill 4910

Start up mysql normally:

$ sudo service mysql start
No, Git really is much better than Mercurial
Posted by postfuturist on 2011-03-27 17:16:51

In watching Linus Torvalds' 2007 talk on Git at Google, I had a realization: Mercurial actually fails to match the basic functionality of Git. It's not just a different flavor of distributed source code management, it's lacking. Before I get into the details, I should explain the workflows I have used as they inform my perspective and conclusions. I have used Git and Mercurial at my day jobs with small teams using a centralized workflow. The way it works is there is a central repository which each engineer clones and pushes to. I have also done less centralized development in my free time using github, mostly. There, I clone a repo, push some changes and send a pull request. Those are my workflows.

Cheap Branching: One thing that Torvalds mentions is that people using Git like to create many branches on their own repository and those branches are easy and cheap and don't bother anyone else. When I started using Git at work, this is what I did. I made local branches on my own repository clone and was able to work on various features and switch between them quickly and painlessly. When you push to a central repository with Git you must specify a branch (unless there is a default set). Only that branch you specify gets pushed. This is an important feature which Mercurial lacks. First of all, let's see this in action. I've created a simple clone of a centralized repository. There is only a single branch, master.

$ git branch # show me local branches
* master
$ git remote # show me remote repositories
origin
$ git branch -r # show me all remote branches
  origin/master

OK, so there is a single local branch called master and the same branch exists on the remote repository origin, which is the central repo. Simple enough. Let's start working on a feature...


$ cat foo.py 
print("hello")
$ git branch my_feature # create a local branch
$ git branch
* master
  my_feature
$ git checkout my_feature # switch to local branch
Switched to branch 'my_feature'
$ git branch
  master
* my_feature
$ vim foo.py
$ cat foo.py 
print("Hello, world")
$ git commit -am "improved message"
[my_feature c10518b] improved message
 1 files changed, 1 insertions(+), 1 deletions(-)

Let's say that feature is not yet complete, but someone quickly needs a new file added and pushed to the central repo. Here's how you handle that in Git:


$ git checkout master # switch back to master branch
Switched to branch 'master'
$ cat foo.py # verify that foo.py does not have changes from my_feature branch
print("hello")
$ vim bar.py # add a file
$ cat bar.py 
print("this is bar")
$ git status # show what's different
# On branch master
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#	bar.py
nothing added to commit but untracked files present (use "git add" to track)
$ git add bar.py # stage changes
$ git status # here's what it looks like staged
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#	new file:   bar.py
#
$ git commit -m "quickly add bar.py"
[master b856f5b] quickly add bar.py
 1 files changed, 1 insertions(+), 0 deletions(-)
 create mode 100644 bar.py
$ git push origin master # push master branch to central repo (origin)
Counting objects: 4, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 302 bytes, done.
Total 3 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
To /home/steve/gittest/central
   efdbc6d..b856f5b  master -> master
$ git branch
* master
  my_feature
$ git branch -r # show remote branches
  origin/master

As you can see, I was able to switch back to the master branch, make a change and push that to the central repository without pushing the my_feature branch. In practice, it is common to be working on many different feature simultaneously on different local branches. These local branches don't pollute the branch list on the main repository, because they never get pushed, unless I want to push them.

Mercurial does not have the concept of local branches that do not get pushed to the main repository. Instead, you do not specify which branch to push because all branches get pushed. Branches you've created locally get automatically created on whatever repository you push to. It is a fundamental flaw in Mercurial. There are a variety of plugins, some even that ship by default with recent versions of Mercurial, which work around this issue. Each one of these plugins works around the issue by allowing code changes to be put aside somewhere (other than a branch on your repository) where they will not be automatically pushed. So, instead of naturally and easily creating branches locally for work that you do not want to push (yet), you are forced to do something else to temporarily store your incomplete changesets. Some of these options are difficult to understand as they use fundamentally different paradigms than simple branches. Some very closely emulate the natural local branching semantics of Git. The closest to being useful is Local Branches which does not currently ship with Mercurial. Unfortunately, since it is somewhat of a hacky workaround, it is not as fast and simple as making branches with Git. What it does is make another clone of your repository to hold these extra branches. What it boils down to is this: no matter how you deal with this shortcoming in Mercurial's design, it requires extra mental effort to use, mental effort that could be better put to writing your code, not figuring out how to make your DVCS work properly.

Staging Changes: In some ways, Mercurial is stuck in the past. It tends to see the world in terms of files, not changesets. For example, in Mercurial, if you edit the lines of a file that is already under version control, and also add a new file and then perform a commit, the changes to the existing file will be part of the commit, but the new file will not. Why? I don't know. Git sees both of these changes as equally important. If you make some changes to a file already under version control and add a new file, neither changes will be added if you perform a commit. Instead, you need to "stage" your changes with Git. In this instance, you can stage either the new file, the changes to the existing file, or both and then commit only those changes which you have staged. In other words, in Mercurial, you have the option to only commit the changes to the existing file ignoring the new file, but you are not given the opportunity to only commit the new file and not the changes to the existing file. Git allows you to do either.

This added flexibility of Git is similar, in a way, to the flexibility of Git's branching. Let's say you are working on file X on the master branch and someone says, "quick, make a change to file Y and push it!". In Git, you could make a separate branch and put your change to file X there, and then change file Y on the master branch and push that. But you have an even simpler option. Instead, just make the required changes to file Y, stage file Y, commit only the changes to file Y and then push that to the central repo. No branching, no nothing, just simple flexibility. This is how easy it is:

$ git status
# On branch master
nothing to commit (working directory clean)
$ ls
bar.py  foo.py
$ vim foo.py # making changes to foo.py
$ git status # see that there are unstaged changes in foo.py now
# On branch master
# Changed but not updated:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#	modified:   foo.py
#
no changes added to commit (use "git add" and/or "git commit -a")
$ vim bar.py # make quick changes to bar.py that need to be pushed
$ git status
# On branch master
# Changed but not updated:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#	modified:   bar.py
#	modified:   foo.py
#
no changes added to commit (use "git add" and/or "git commit -a")
$ git add bar.py # stage only changes to bar.py
$ git commit -m "emergency change to bar.py"
[master 92890b5] emergency change to bar.py
 1 files changed, 1 insertions(+), 0 deletions(-)
$ git push origin master
Counting objects: 5, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 324 bytes, done.
Total 3 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
To /home/steve/gittest/central
   b856f5b..92890b5  master -> master
$ git status # see that indeed our changes to foo.py are still unstaged
# On branch master
# Changed but not updated:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#	modified:   foo.py
#
no changes added to commit (use "git add" and/or "git commit -a")

Simplicity: For my workflow, Git is simpler to use than Mercurial. I developed this workflow very quickly when I started using Git. I've been using Mercurial where I work for several months now, and I haven't been able to find a workflow as simple and powerful. In the examples in this article, I've only used the following git commands:

$ git status # show the status of current branch
$ git commit -m "comment" # commit currently staged changes
$ git commit -am "comment" # commit, auto-staging changes to files under version control
$ git branch # show local branches
$ git branch -r # show remote branches
$ git branch <newbranch> # create a new local branch
$ git checkout <branchname> # switch to different local branch
$ git push <remote> <branchname> # push a particular branch to a particular remote repo
$ git add <file> # stage the changes to a particular file
HP Photosmart All-In-One Wireless Printer in Ubuntu Linux
Posted by postfuturist on 2011-02-27 22:53:19

Hardware companies that support Linux well are hard to find. HP's printer division has gone out of its way to support Linux with its software. Every HP printer I've tried has always worked with the HPLIP drivers. Today I picked up a HP Photosmart D110A Wireless e-All-in-One Printer (CN731A#B1H) printer from Best Buy to replace an HP printer that is several years old and finally stopped working.

Now, I've had issues with Best Buy in the past, but I've got the Reward Zone credit card and having racked up $40 in store credit from using the credit card, so I figured I'd put that toward a printer. Just something simple. The printer I got was selling for about $70 bucks, which means I took it home for $30 after cashing in the credit.

The set-up was surprisingly easy. I turned it on, it printed a test-page which it then scanned to align the print heads. Then it asked to pick the wireless router and had me enter the WPA password. It then connected to the internet and, with permission, downloaded and installed a firmware update. It then printed out a page telling me how to register the printer online such that I can now email it documents to print. Strange, but true. Once connected to the network, configuring the printer in Ubuntu was a breeze in 10.10. My wife's laptop was a little tricky since she has a slightly older version of Ubuntu and I had to install the drivers from HP's page. That took a few minutes, but worked fine.

Basically, it prints and works great with no wires needed. That's handy. Any time a hardware company has top-notch support for Linux, they become the default choice for Linux users everywhere. I only wish more companies realized this.

Ubuntu 10.10 x86-64 Sun Spider Benchmarks
Posted by postfuturist on 2011-01-05 20:56:04

I ran some Sun Spider benchmarks on my laptop running Ubuntu 10.10. Here are the results.

  • Chrome 10.0.612.1 dev: 385.8 ms
  • Chromium 10.0.627.0 (70377): 383.0 ms
  • Opera 11.00 build 1156: 369.8 ms
  • Firefox 3.6.13: 2301 ms
  • Firefox (Minefield nightly build) 4.0b9pre (2011-01-05): 318.9 ms

It's good to see Mozilla jumping into the pot with blazing fast benchmarks for JavaScript. There is of course a lot more to having a snappy web experience than the speed of your JavaScript interpreter, but it certainly does help. I actually used Opera 10.60 for awhile but even though it was beating Chrome in the benchmarks, it didn't feel as smooth. I'll have to try out Firefox 4.0 beta and see if it compares favorably to Chrome, or if the JavaScript speed-up is just some lipstick on the same pig.

Sony's Changes to GNUstep GUI Library: Adding Touch
Posted by postfuturist on 2010-11-27 01:21:27

When Sony announced and then promptly put the SNAP (Sony's Networked Application Platform) on hold, they left the SDK's available for download. I'm a big fan of open development platforms and I was keen to give this one a whirl, especially seeing that it is built on open technologies like the Linux kernel and GNUstep platform. I managed to download the latest version of SNAP (1.2.2) before they removed the download links. I'll be disappointed if the SNAP Developer Program gets killed for unknown internal reasons at Sony, but it won't be a complete loss, as Sony did distribute (albeit for a short time) modifications and additions to the GNUstep GUI library which is under LGPL.

Sony built on top of version 0.17.1 of the GNUstep GUI library. I found the original sources of the library and created a github repo with the original 0.17.1 version of the library as the first commit, with Sony's changes applied as the second commit, so you can easily see what they changed. That is available here: https://github.com/deliciousrobots/gnustep-gui-sony .

What did Sony add? Mostly they added touch interface related code. The files they added are prefixed with "SN" so they are easy to find:


$ find -name "SN*"
./Headers/AppKit/SNCheckmarkGestureRecognizer.h
./Headers/AppKit/SNRotationGestureRecognizer.h
./Headers/AppKit/SNLongPressGestureRecognizer.h
./Headers/AppKit/SNGestureRecognizer.h
./Headers/AppKit/SNSwipeGestureRecognizer.h
./Headers/AppKit/SNTouch.h
./Headers/AppKit/SNTapGestureRecognizer.h
./Headers/AppKit/SNPanGestureRecognizer.h
./Headers/AppKit/SNPinchGestureRecognizer.h
./Headers/AppKit/SNLassoGestureRecognizer.h
./Source/SNLassoGestureRecognizer.m
./Source/SNPinchGestureRecognizer.m
./Source/SNCheckmarkGestureRecognizer.m
./Source/SNPanGestureRecognizer.m
./Source/SNLongPressGestureRecognizer.m
./Source/SNGestureRecognizer.m
./Source/SNTapGestureRecognizer.m
./Source/SNRotationGestureRecognizer.m
./Source/SNTouch.m
./Source/SNSwipeGestureRecognizer.m

As you can see, mostly they added touch and multi-touch gesture recognition. I looked at the code, it is a bit rough in places, definitely a work-in-progress, but interesting and now an LGPL'ed library of gesture recognition code in Objective-C. I was impressed with how nice some of the comments on the code were. Take a look at this bit from the SNLassoGestureRecognizer class:

/* 
 * Returns whether or not a given point is inside the lasso.
 * This is a "Point in Polygon" problem, and we solve it by the
 * "winding number algorithm".
 *
 * We first translate all vertices of lasso by -p to make it the
 * new origin. Then, we traverse all verticies to see how many times
 * the polygon winds around the point, using the "Axis Crossing 
 * Method". If the winding number is 0, then the point is not inside
 * the polygon. Otherwise, the point is.
 *
 * Note: This algorithm works for convex/concave/complex polygons.
 * Detailed algorithm can be found here:
 * http://www.engr.colostate.edu/~dga/dga/papers/point_in_polygon.pdf
 *
 *            _____        __
 *      |    /   __\      |  \
 *      |   /   /   point |  | 
 *      |  |    \  *     /   | 
 *      |  |     \______/  __|
 *      |   \             /
 *      |    \__         /
 *      |       \______/
 *   ----------------------- 
 *      |
 *
 *
 *   ---> translate polygon by -p
 * 
 *
 *                 |
 *        II       |       I
 *            _____|       __
 *           /   __|      |  \
 *          /   /  |point |  | 
 *    -----|----\--*-----/---|--- 
 *         |     \_|____/  __|
 *          \      |      /
 *      III  \__   |     /  IV
 *              \__|___/
 *                 |
 *                 |
 * 
 *
 */
- (BOOL) pointInLasso: (NSPoint) point inView: (NSView *) view;
{
   NSPoint pointInWindow;

   // convert to window-based coordinate first, if necessary
   if (view)
      pointInWindow = [view convertPoint: point toView: nil];
   else
      pointInWindow = point;

   if (pointInWindow.x > maxX || pointInWindow.x < minX ||
       pointInWindow.y > maxY || pointInWindow.y < minY)
   {
      // point is not even in enclosing rectangle
      return NO;
   }

   /***
   NSEnumerator *enumerator = [vertices objectEnumerator];
   NSValue *value;
   NSPoint vertex;
   NSPoint translatedVertex;
   BOOL quadrant_I = NO, quadrant_II = NO, quadrant_III = NO, quadrant_IV = NO;
   
   while ((value = [enumerator nextObject]))
   {
      vertex = [value pointValue];
      printf("vertex = (%f, %f)\n", vertex.x, vertex.y);
      translatedVertex = NSMakePoint(vertex.x - pointInWindow.x, vertex.y - pointInWindow.y);

      if (translatedVertex.x > 0 && translatedVertex.y > 0)
         quadrant_I = YES;
      else if (translatedVertex.x < 0 && translatedVertex.y > 0)
         quadrant_II = YES;
      else if (translatedVertex.x < 0 && translatedVertex.y < 0)
         quadrant_III = YES;
      else if (translatedVertex.x > 0 && translatedVertex.y < 0)
         quadrant_IV = YES;

      if (quadrant_I && quadrant_II && quadrant_III && quadrant_IV)
         return YES;
   }
   ***/

   NSEnumerator *enumerator = [vertices objectEnumerator];
   NSValue *value1;
   NSValue *value2;
   NSPoint vertex1, vertex2;
   NSPoint initialVertex, translatedVertex1, translatedVertex2;

   value1 = [enumerator nextObject];
   value2 = [enumerator nextObject];
   vertex1 = [value1 pointValue];
   translatedVertex1 = NSMakePoint(vertex1.x - pointInWindow.x, vertex1.y - pointInWindow.y);
   initialVertex = translatedVertex1;

   winding = 0;
   do
   {
      vertex2 = [value2 pointValue];
      translatedVertex2 = NSMakePoint(vertex2.x - pointInWindow.x, vertex2.y - pointInWindow.y);

      winding += [self calculateWindingNumberFrom: translatedVertex1
                                               to: translatedVertex2];

      translatedVertex1 = translatedVertex2;
   }
   while ((value2 = [enumerator nextObject]));

   // last vertex to first vertex
   winding += [self calculateWindingNumberFrom: translatedVertex2
                                            to: initialVertex];

   if (winding == 0) 
      return NO;
   else 
      return YES;
}

- (float) calculateWindingNumberFrom: (NSPoint) v1 to: (NSPoint) v2
{
   float intersect;
   float x1, y1, x2, y2;
   x1 = v1.x;
   y1 = v1.y;
   x2 = v2.x;
   y2 = v2.y;

   if (y1 == 0 && y2 == 0)
   {
      // both v1 v2 are on x-axis, winding number unchanged
      return 0;
   }
   else if (y1 * y2 < 0) // line v1 -> v2 crosses x-axis
   {
      intersect = x1 + (y1*(x2-x1))/(y1-y2); // x-coordinate of intersection of line v1 -> v2 and x-axis
      if (intersect > 0) // line v1 -> v2 crosses positive x-axis
      {
         if (y1 < 0)  //counter-clockwise
            return 1;
         else         //clockwise
            return -1;
      }
   }
   else if (y1 == 0 && x1 > 0) // v1 on positive x-axis
   {
      if (y2 > 0) 
         return 0.5;
      else 
         return -0.5;
   }
   else if (y2 == 0 && x2 > 0) // v2 on positive x-axis
   {
      if (y1 < 0) 
         return 0.5;
      else 
         return -0.5;
   }

   return 0;
}

You've got to love the ASCII-art diagrams. None of this code is revolutionary and most of it is pretty boring except for a couple interesting algorithms like the one above. Sony added a good deal of code and changed some things around in the existing GUI module files, too, to incorporate all the multi-touch code into the rest of the system. I hope this code is of some use to the GNUstep team. Anyone implementing a multi-touch interface could also look at this code for a little inspiration.

For me, it's mostly just interesting. I was intrigued by the idea of another open platform to build interesting apps for. Here's hoping Sony moves forward with it.