Open Calais’ Semantic Web Failure

For those with an eye toward next-generation web technology – in this case Semantic Web technology – the post Is OpenCalais Dead? by OpenCalais leader Tom Tague might interest you… even if only as a cautionary tale.

OpenCalais is a natural language processing web service owned by Thomson Reuters. To get an idea of what the tool offers, try out its document viewer.

The post in question details how after some initial success the company sunk a lot of effort and money into “the incorporation of a whole Linked Data ecosystem underneath OpenCalais for companies, geographies, products and a few other things”. The developer excitement and resulting innovation they were expecting simply never materialized.

As a result the company is slowing down, taking stock, and letting their product evolve in response to customer need (as opposed to the desire to be trailblazers, perhaps). Hence the need to pen the post in the first place.

Reading this, I couldn’t help but wonder: When, if ever, is the semantic web going to catch on?

What’s truly remarkable in this case is that the OpenCalais failure isn’t a matter of semantic web technology failing to take root in the world of, say, jQuery engineers. No, this is a case of semantic web technologies failing to take hold with users of natural language processing tools. Anyone sophisticated enough to need/use an NLP web service is almost certainly up to the task of handling RDF and Linked Data.

Why such problems with semantic web adoption?

Share

Leave a Comment

Filed under semanticweb

Using Prolog to Fetch and Process JSON from the World Bank API

Very often you’ll find that any potential interest you’d have in Prolog is destroyed by the fact that all tutorials on the language involve solving some completely pointless and pedantic problem such as the map coloring problem or perhaps the eight queens problem.

Fortunately, Prolog is a 100% badass language that can be used to accomplish useful tasks. In this post I’ll show you how to yank some JSON data from the World Bank’s RESTful open data API and process the results. All in < 40 lines of Prolog. Not rocket science, but that's the whole point.

Let's go.

The Code

Here’s the whole program in all of its glory. I’ll break it down below. Feel free to chime in with a comment if something looks off.

Also, you can grab the code on github here.

:- use_module(library(http/http_open)).
:- use_module(library(http/json)).

fetch_and_process_data :-
    fetch_data(FetchedData),
    process_data(FetchedData).

url_to_process('http://api.worldbank.org/countries/USA/indicators
/AG.AGR.TRAC.NO?per_page=10&date=2005:2011&format=json').

fetch_data(FetchedData) :-
    url_to_process(URL),
    http_open(URL, DataStream, []),
    json_read(DataStream, FetchedData, []).

process_data([ Header, Contents | [] ]) :-
    process_data_header(Header),
    process_data_contents(Contents).

process_data_header(_) :- !.

process_data_contents([]).
process_data_contents([JSONObject|Rest]) :-
    process_json_object(JSONObject),
    process_data_contents(Rest).

process_json_object(JSONObject) :-
    json_object_has_value(JSONObject, date, DateValue),
    json_object_has_value(JSONObject, value, IndicatorValue),
    print('Date:  '), print(DateValue), nl,
    print('Value: '), print(IndicatorValue), nl, nl. 

json_object_has_value(JSONObject, Name, Value) :-
    json(NameValueList) = JSONObject,
    member(NameValuePair, NameValueList),
    NameValuePair = (Name =  Value).

:- fetch_and_process_data.

Use SWI Prolog

We’re going to be using SWI Prolog. It’s free, well-documented and has some useful web modules. In particular we’re going to be using SWI’s http_open and json libraries.

At the top of our program we’ll include the two modules as follows:

:- use_module(library(http/http_open)).
:- use_module(library(http/json)).

Prolog programs consist of facts and rules. The syntax of a rule is rule_head :- rule_body, which is read ‘rule_head if rule_body’. You can think of the rule_head as a goal and the rule body as a sequence of subgoals that must be satisfied in order for the whole rule to be satisfied. Those ‘use_module’ statements are headless rules, so Prolog just executes them.

Create a ‘main method’ and call it.

This is one of those things that you could not know how to do after reading an entire overview of the language. What you want to do is create a main rule and then at the bottom of your code ‘call’ your main rule with the headless rule construct you just saw above.

Here’s a toy example:

main_rule :-
    print('Hello, world!').

:- main_rule.

And here’s what’s actually in our code:


fetch_and_process_data :-
    fetch_data(FetchedData),
    process_data(FetchedData).

...

:- fetch_and_process_data.

Once you’ve included a call to your main predicate in a source file, all you have to do is ‘consult’ the source file into your Prolog session, and the code runs. For example, here’s the session for the program we’re talking about:

?- consult('http_test.pro').
Date:  2010
Value: @null

Date:  2009
Value: @null

Date:  2008
Value: @null

Date:  2007
Value: 4389812

Date:  2006
Value: 4430359

Date:  2005
Value: 4470905

% http_test.pro compiled 0.00 sec, 264 bytes
true.

Prolog predicates are referred to with the naming convention name/arity, so our main rule is called fetch_and_process_data/1 because it takes a single argument. The rule has two subgoals which, surprise, fetch and process the data.

Notice that those two subgoals share the FetchedData variable. When we call fetch_data/1 the variable is uninstantiated. fetch_data/1 will instantiate the variable to our JSON object and then call process_data/1 with the instantiated variable. Let’s see what fetch_data/1 actually does.

Retrieve the JSON

fetch_data(FetchedData) :-
    url_to_process(URL),
    http_open(URL, DataStream, []),
    json_read(DataStream, FetchedData, []).

The three subgoals of fetch_data/1 correspond to the following actions:

  1. Get the URL to the World Bank API
  2. Open a stream to the resource specified by the URL.
  3. Read the JSON from the stream into a Prolog structure.

We simply store the URL with the following fact in our Prolog program. The actual URI comes from the awesome World Bank API Query Builder. The query below is asking for quantity of tractors in the US for years 2005-2011. Of course, the program could easily be modified to operate on a list of URLs or to read the URL from standard input or whatever.

url_to_process('http://api.worldbank.org/countries/USA/indicators
/AG.AGR.TRAC.NO?per_page=10&date=2005:2011&format=json').

The second subgoal opens a stream for us to read the data from using the http_open/3 predicate which comes from the first of the modules we imported above. After this subgoal is satisified, the initially uninstantiated DataStream will be bound to a stream that we can read from. That empy list in the third slot of the predicate can hold any number of options. You can read up on http_open/3 in the SWI manual.

The third subgoal reads the JSON from the stream into a Prolog structure using the json_read/3 predicate which comes from the second of the modules we imported above. After this subgoal is satisfied FetchedData will be bound to a Prolog structure containing our JSON response data. Note that when json_read/3 binds the FetchedData variable it also binds the occurrence of that variable in the head of our rule! Again, you can read up on json_read/3 in the manual.

Process the JSON

If you’ve ever read JSON into a language like Python, Ruby or JavaScript you know that one of the format’s main virtues is that it maps quite neatly into data those language’s native data structures. Unfortunately, this is not the case with Prolog. In particular, Prolog doesn’t really have a map/hash/dict type (though of course you can create such a type in the language). The module creator chose to represent JSON objects lists key=val statements that are wrapped in a json() structure. So you get the following correspondence:

{
    "first": "John",
    "last":  "Coltrane",
    "plays": "Tenor Sax"
}
json([
    first = "John",
    last  = "Coltrane",
    plays = "Tenor Sax"
]).

The actual JSON that we’re getting from the World Bank looks like this. We’ll be interested the date and value fields of the outermost object in the result list below:

[
    {
        "page": 1,
        "pages": 3,
        "per_page": "2",
        "total": 6
    },
    [
        {
            "indicator": {
                "id": "AG.AGR.TRAC.NO",
                "value": "Agricultural machinery, tractors"
            },
            "country": {
                "id": "US",
                "value": "United States"
            },
            "value": "4430359",
            "decimal": "0",
            "date": "2006"
        },
        {
            "indicator": {
                "id": "AG.AGR.TRAC.NO",
                "value": "Agricultural machinery, tractors"
            },
            "country": {
                "id": "US",
                "value": "United States"
            },
            "value": "4470905",
            "decimal": "0",
            "date": "2005"
        }
    ]
]

You can read up more on the correspondence between JSON/Prolog types in that link to the json_read/3 predicate above. Let’s take a look at the implementation of process_data/1.

process_data([ Header, Contents | [] ]) :-
    process_data_header(Header),
    process_data_contents(Contents).

process_data_header(_).

process_data_contents([]).
process_data_contents([JSONObject|Rest]) :-
    process_json_object(JSONObject),
    process_data_contents(Rest).

process_data/1 takes a single list argument and has two subgoals: process_data_header/1 and process_data_contents/1. If you’re not used to Prolog (or FP languages such as Haskell/OCaml) that rule header probably looks pretty gnarly. Basically, the ‘formal parameter’ of the predicate is a pattern that argument will get matched against. The matching will have the effect of dissecting the data structure, assigning its innards to the variables in the pattern. In the present case we know in advance that we’ll be receiving a two-element list so we simply pluck off the first and second list elements, bind them to the DataHeader and DataContents variables and call the subgoals.

Uh, Still Processing the Data

The header of the WorldBank JSON is useful but boring so I’m not doing anything at all in the process_data_header/1 subgoal. It’s just there for completeness’ sake.

process_data_contents/1 recursively walks through a list of JSON objects and calls process_json_object on each such object. In order to do just about anything useful in Prolog you have to do this type of recursive list processing. Our base case is the empty list, in which case we do nothing. Our recursive case splits the incoming list into its head and tail (here JSONObject and Rest). We then call process_json_object/1 on the JSONObject.

Finally, process_json_object/1 grabs the fields of interest from the given JSON object and writes out the values. It does so with the aid of json_object_has_value/3 which takes a JSON object and a field name as its first two arguments and binds its third argument to the object’s value for the given field. Remember that JSON objects in Prolog are lists of equals statements. That’s why we match the variable NameValuePair with Name = Value below.

process_json_object(JSONObject) :-
    json_object_has_value(JSONObject, date, DateValue),
    json_object_has_value(JSONObject, value, IndicatorValue),
    print('Date:  '), print(DateValue), nl,
    print('Value: '), print(IndicatorValue), nl, nl. 

json_object_has_value(JSONObject, Name, Value) :-
    json(NameValueList) = JSONObject,
    member(NameValuePair, NameValueList),
    NameValuePair = (Name = Value).

Phew, that’s alot of explaining but go back to the top of the page and look at the program in its entirety again. It’s relatively succinct and straightforward once you get over the initial brainsmash of operating in the declarative paradigm.

Some Random Remarks

  1. I had no idea what I was in for when I started using SWI’s HTTP/JSON modules. Ultimately, I found them to be dead simple to use and relatively well-documented.
  2. Prolog programs can be viewed either through a declarative or an imperative lens. I’ve used lots of imperative language above. It can be interesting to look at a single predicate and think about from both viewpoints: from the imperative standpoint, think of it in terms of goals being satisfied; from the declarative standpoint, think in terms of the predicate simply being the specification of a truth condition.
  3. Writing Prolog is so damn fun that I’m bewildered as to why more programmers (esp. ones that have FP hardons) aren’t knee deep in it.
Share

6 Comments

Filed under programming

A Philosopher Every Programmer Should Know: Susanne K. Langer

Susanne K. Langer

So, I recently started reading Susanne K. Langer’s classic Philosophy in a New Key (PNK). (Before I became a developer I got a couple degrees in philosophy, which is absolutely my first love.) Langer is a very wide-ranging thinker with publications in everything from formal logic to aesthetics.

PNK covers a lot of ground and though it’s all really interesting the second chapter offers up some thought that is so relevant to programmers that I had to try to get it out there. Also, it’s not relevant in the way that, say, Principia Mathematica might be relevant. Rather, it’s relevant in that it makes sense of your life. It explains why it is that you love to code.

Langer puts forth a bunch of obstacles for those who claim that thought is best explained instrumentally, in terms of its tendency to help us survive. She asks questions such as: Why do we engage in so much magic/ritual?, Why do we take our art so seriously?, Why do we dream?. Langer’s answer is really striking.

The basic need, which certainly is obvious only in man, is the need of symbolization. The symbol-making function is one of man’s primary activities, like eating, looking, or moving about. It is the fundamental process of his mind, and goes on all the time.

Regarding why we dream, Langer tells us

The brain works as naturally as the kidneys and the blood-vessels. It is not dormant just beause there is no conscious purpose to be served at the moment…

…it goes right on manufacturing ideas – streams and deluges of ideas, that the sleeper is not using to think with about anything. But the brain is following its own law; it is actively translating experiences into symbols, in fulfilment of a basic need to do so. It carries on a constant process of ideation.

One more juicy quote where the application to activities such as programming doesn’t require much imagination:

Only a part – howbeit a very important part – of our behavior is practical… The remainder serve simply to express ideas that the organism yearns to express, i.e. to act upon without practical purpose, without any view to satisfying other needs than the need of completing in overt action the brain’s symbolic process. (emphasis mine)

The act of programming is arguably one of the purest and most direct ways in which one can engage in an act of symbolization. We are constantly translating our ideas into formal languages. Within those languages we create abstractions upon abstractions. If Langer is correct, we love our work because we were meant to do it.

More on Langer here.
Link to the book here.

Update: This post shot to the front page of Hacker News. Join the HN discussion here.

Share

27 Comments

Filed under philosophy, programming

Father’s Day Picture

Father's Day Picture

Thank You For Putting Me to Sleep Each Night

Among the many amazing things that I got for Father’s Day this year is this wonderful picture of Asa. Sleep has kind of been my department for most of his little life and it hasn’t always been easy. Finally, some recognition!

Share

Leave a Comment

Filed under Uncategorized

Bottom-Up Learning of Abstract Ideas

The NYT has a really cool article on bottom-up approaches to learning ideas that are almost exclusively taught in a top-down fashion. If you’re unfamiliar with the distinction, bottom-up approaches start with concrete examples and proceed to general laws while top-down approaches first teach you the general law which you’re then expected to apply to specific cases. (In philosophical literature there’s a very similar distinction between analysis (top-down) and synthesis (bottom-up).

The article goes over some cool online tools that kids are using to learn math in a bottom-up fashion. One of these tools simply involves fast-paced matching of graphs to equations. Time isn’t taken to ‘understand’ the equations per se. The goal is basically to teach intuition… to get the learner to a state where they have a feel for what the equations look like.

As a programmer I can’t help but feel that I’ve largely learned to code in an almost completely bottom-up fashion. But not because I had access to state of the art CS curricula… but rather because I simply learn better in such a fashion and the wide variety of tools out there allows me to self select based on learning disposition. For example, considering the O’Reilly bestiary you might say that Nutshell books are top-down while Cookbooks are bottom-up. You might also be able to say that compiled languages are top-down while interpreted languages are bottom-up (because lack of compilation leads to faster development/more tinkering). You might also say that languages with a REPL are more bottom-up than languages without one (again because they encourage tinkering).

The more you tinker, the more concrete instances you’re exposed to. The more concrete instances you’re exposed to, the more likely you are to develop a feel for why your tinkerings met with success or failure. The analogy with machine learning algorithms is unavoidable: you are a statistical model whose training data are your tinkerings!

As long as I’ve been coding my biggest enemy has been the Unix man page. It occurs to me now that man pages are often laughably top-down. You effectively get a cryptic grammar for the usage of the command. Just gimme some damn examples!

Share

1 Comment

Filed under Uncategorized

Childhood Amnesia

After getting blocked from the nytimes due to their new paywall, I headed over to the latimes to see what was going on there. Luckily, I ran into this interesting article about childhood/infantile amnesia.

The condition basically boils down to the fact that people tend to have virtually no memories before approximately age three. Apparently this strange fact is universal, suggesting that it’s caused by some bit of biology that all humans share.

Given that I’m currently a dad to an amazing 16 month old I couldn’t help but think, geez, all of the amazing experiences that Asa has, all of the great things that his mom and I make sure he encounters will all be lost! And not just that. All of the mundane but no less wonderful experiences, the hugs, the soothings, the naptime bottles… all of these will be lost as well.

But of course they’re not really lost. All of these experiences are absolutely fundamental in shaping who he’ll become. As are any negative ones. For example, while on a walk this evening Asa pet a big beautiful dog who seemed to relish the attention. Now if that dog had barked at or bitten him I’m sure that this would have influenced how he perceives dogs… despite the fact that he’d completely forget the very event that had said influence.

Anyway, the subject is just damn interesting. The wikipedia article on childhood amnesia has a nice little list of theories which attempt to explain just why this weird phenomenon occurs.

Share

Leave a Comment

Filed under infants

Go The F*ck To Sleep

I must get this book. According to the product description at amazon it “is a bedtime book for parents who live in the real world, where a few snoozing kitties and cutesy rhymes don’t always send a toddler sailing off to dreamland. ” Amen.

I gotta say that putting Asa to sleep has been the most consistently difficult part of parenting so far. At 15 months he’s been sleeping through the night and going down without a fight for several months now (knock on wood!). But before that there was over a year’s worth of nighttime shrieking.

Now that there’s some distance I have so much nostalgia for the early colicky days when the only way to get him to sleep involved putting him in his swing with white noise blaring while I simultaneously pat him on his shoulder, sing to him and hold his dear binky in his mouth. Oh yeah, and he’s swaddled so tightly he looks like a lollipop.

Good god don’t let the binky fall out!

Share

Leave a Comment

Filed under books

Social Security Baby Name Data: Part 2

i. Intro.
As I mentioned in the last post, the Social Security Administration offers a great resource for choosing baby names. The web interface is here. For those interested in playing with the source data files, they can be downloaded from here.

In this post I’ll cover one way you might go about converting this data into a format that could plotted on a timeline. Let’s go.

ii. Data Overview.
Once you unzip the downloaded zip file (names.zip), you have a directory filled with 130 files named yob1880.txt… yob2009.txt. The files are very simple in structure, each containing lines of comma-separated name,sex,frequency triplets. For example, here are the first five lines from the file yob1880.txt:

Mary,F,7065
Anna,F,2604
Emma,F,2003
Elizabeth,F,1939
Minnie,F,1746

iii. Processing the Data: Preliminaries
The goal here is to convert the data into some format that can feed a visualization. Different visualization toolkits require different types of input. I’m a huge fan of Protovis and would target that toolkit, but I have a demo coming up at work that will most likely be using Highcharts.js, so I figure I’ll get a leg up and target that. (See an example of a Highcharts timeline here)

In order to generate a Highcharts timeline which shows how the frequency of a name varies over the timespan of the corpus we will process the data in such a way that it ends up being a list of files named David.txt, Shannon.txt, etc. Each of these files will consist of a JSON object whose keys will be years and whose values will be frequencies for the corresponding years.

So we want a file called David.txt, whose contents look like this:

{
    1880: 869,
    1881: 750,
    ....
}

Note: Since the years are predictable, we could just as easily store the frequencies in a JSON list and later figure out that the frequency at list[N] corresponds to year 1880+N.

Note: Some names can be used for boys and for girls. To differentiate the files we’ll complicate the naming convention so that we end up with files such as Pat_M.txt and Pat_F.txt.

iv. Processing the Data: Code
Alright, let’s shut up and write some code. Now this is the sort of task you could do in just about any scripting language. I’ll use Python for no particular reason here.

The program will take two command-line arguments: an input directory (which will contain the yobXXXX.txt files) and an output directory (where the JSON files will be written). So the top level of the program might contain code such as this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import sys
import os
 
if len(sys.argv) != 3:
    sys.exit('''
usage: python ssnames_time_series.py <inputdir> <outputdir>
<inputdir>:   directory containing the yobXXXX.txt files
<outputdir>:  directory where time series data will be written
''')
 
idir = sys.argv[1]
odir = sys.argv[2]
 
ifilenames = [f for f in os.listdir(idir) if f.endswith('.txt')]

If you’re not familiar with Python, in line 14 we gather all input filenames using a list comprehension.

Next we need to build up a data structure that maps names to frequencies in each year. We’ll do this by using a Python dictionary (which corresponds to a Perl/Ruby hash) whose keys are names and whose values are themselves dictionaries. These latter dictionaries will mirror the JSON above… their keys will be years and their values will be frequencies at that year. Here’s the code:

1
2
3
4
5
6
7
8
9
10
11
12
name_frequencies = {}
years = []
for ifilename in ifilenames:
    year = ifilename[3:-4]
    years.append(year)
    for line in open(os.path.join(idir, ifilename)):
        line = line.rstrip()
        (name, sex, frequency) = line.split(',')
        name_w_sex = name + '_' + sex 
        if name_w_sex not in name_frequencies:
            name_frequencies[name_w_sex] = {}
        name_frequencies[name_w_sex][year] = frequency

The outermost loop (Line 3) iterates over the filenames retrieved in the previous code sample. We yank the year substrings out of the filenames and stash them in a list that will be used later on. The inner loop (Line 6) iterates over those name,sex,frequency lines of the file. We simply split the line on the comma character (Line 8 ) and put the data in appropriately named variables. To distinguish boys and girls with the same name we tack the sex onto the name before using it as a key in the dictionary. Finally, we populate the dictionary at lines 10-12.

Still there? Great! At this point we have a neat little data structure (actually, it’s huge) that contains all we need to generate the output data. All we have to do at this point is traverse the dictionary and create a file for each name_sex key. The contents of the file will contain a JSON version of the dictionary that the name is mapped to. The code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
for name in name_frequencies:
    ofile = open(os.path.join(odir, name+'.json'), 'w')
    ofile.write('{\n')
    for i in range(len(years)):
        year = years[i]
        if year in name_frequencies[name]:
            frequency = name_frequencies[name][year]
        else:
            frequency = 0
        ofile.write(year + ': ' + str(frequency))
        if i != len(years)-1:
            ofile.write(',\n')
    ofile.write('\n}')
    ofile.close()

We loop over the name_sex keys and open an output file whose name is name_sex.json. At Line 3 we print the opening curly brace for our JSON object.

Starting at Line 4 we make use of that mysterious array of years that we populated earlier. The reason we need this is that if no babies named David were born in 1910, then the file yob1910.txt simply won’t have any line(s) for David… that is, the name will be absent rather than being present with a frequency of zero. Since we don’t want our JSON objects to be ‘gappy’ in this way, we loop over the years and check to see if the name has a frequency for the year. If it does, we use that as our frequency; if it doesn’t we use zero. At Line 10 we print the JSON mapping from year to frequency. Lines 11-12 simply insert commas between the mappings. Finally we close the JSON object and we’re done. That’s it!

Now, as with any programming task, there are 5000 different ways of accomplishing the same thing. One thing in particular that’s worth toying with is the form of the output data. I’ve chosen to create a different file for every name_sex. That’s tens of thousands of little files! You can also just generate one gigantic file. You could also generate 26 alphabetized files that contain all names starting with the corresponding letter of the alphabet. The best choice will depend on how you’re using the data.

In the next post on this topic I’ll show how you can have Highcharts.js consume the data we just generated with an Ajax call in order to generate a beautiful timeline!

Share

Leave a Comment

Filed under experiments, programming

Social Security Baby Name Data

The single best source for baby names on the internet is the social security administration’s popular baby names page. I can’t tell you how often my partner and I went to this site when choosing our son’s name (which ended up being Asa!).

The best part about the site is that it allows you to check out the most popular baby names in every year from the present all the way back to 1879. And not just the most popular 10… you can select to show as many as the top 1000 names from a given year.

While it’s really interesting to see the most popular names in some ancient time, it’s often much more interesting to see the ugly-duckling names. For example, here’s the least popular 10 names (out of 1000) in 1925:

991. Godfrey
992. Hughie
993. Kirby
994. Layton
995. Luverne
996. Odie
997. Okey
998. Tillman
999. Toshio
1000. Alfonzo

I’ll let those names speak for themselves.

Now the cool thing that I just realized is that in addition to offering a handy website to view the data, the SSA has been kind enough to actually make the source data files available for download. For anyone out there who likes to do fun data manipulation with scripting languages such as Perl/Python/Ruby, the data is in a super simple format. Here’s a snippet:

Sivert,M,5
Sonnie,M,5
Stancil,M,5
Standard,M,5
Stanleigh,M,5

As you can see, the data is in the form of comma-separated values, with each line containing three such values: first, the name; second, the sex; third, the frequency. The year is given by the name of the file. For instance, this snippet came from the file yob1925.txt. Also worth mentioning is that these files contain way more data than what you can get through the web interface. For example, yob1925.txt contains 10,648 entries (4870 male names and 5778 female names), including Edker, Obbie, Shafter and Jackjohn. (Jackjohn!)

Not only is this data easily parsable, but it’s the perfect type of data that could serve as input for cool visualizations. On my agenda for this weekend is converting the data files into JSON time-series data of the sort that can be pumped into a visualization library such as Protovis or Highcharts. If I come up with something worth showing, I’ll be sure to post it right here.

Share

Leave a Comment

Filed under experiments, programming

Fantastic Dover Computability Book Cover

Computability and Unsolvability

Somehow, and I really just don’t know how, my twin loves of Dover book cover art and theoretical computer science never led me to this book. The sheer size of that machine is just stupendous. Not to mention the fact that it’s a lovely robin’s egg blue. And note that the desk and the machine seem to be a single, continuous block of circuitry.

I can’t get over the proportions. Look at the keys on that keyboard! Each one is the size of that poor bastard’s hand.

Most amazing, though, is the screen. The two columns of data seem to be crushing the programmer’s head. Just amazing.

I learned my TOC from Boolos+Jeffrey and Hopcroft+Ullman, both of which I still run to on occasion. I’m gonna have to get a copy of this just for display purposes, though. I can’t think of anything I’d rather have a coworker see sitting on my desk.

Share

Leave a Comment

Filed under books