Showing posts with label programming. Show all posts
Showing posts with label programming. Show all posts

27 August 2012

Four hours

On Sunday I had the pleasure of introducing a C/C# programmer to Python.


I always tell people it only takes 4 hours to learn Python, and I say it because it’s true. But when I actually see someone pick up a whole new language from scratch, port some existing scrap of C code to Python, learn where the documentation is, set up virtualenv, and write a web app that sends somebody a text message, all in four hours f’real wall-clock time, well. It gets me right here.


When I was interviewing for Mozilla, Mike Schroepfer asked me two questions. (I bombed them both; to this day I have no idea why they hired me.) One was something like: why is it that dynamically-typed languages can be so much more productive than statically-typed languages? I mumbled something inane about not having to write out the types. Worst answer ever, and I knew it.


It was especially chastening because I had spent a lot of time thinking about it, apparently without drawing any insightful conclusions.


The question seems a little passé to me now, but only because about a decade of my life was a fantastically stupid flame war over that one thing. Not because we really finished with it.


I wonder if we could have a better discussion about this now that we’re all grown up. Maybe I’d have something intelligent to contribute now.

13 August 2012

A bit more about the library

Nashville hack day happened Saturday and it was awesome. Click the link—every single talk was great, and that page has slides, links, source code, etc. for all of them.

Mine was on sorting and searching at the library.

The talk starts out talking about what happens if you take a book off the library shelves and put it back in the wrong place. When I wrote it, I got curious, so sent email to the library, asking:

Hi, I have a few questions about the library system.

  1. How many books are in the library system?
  2. Do you ever take a full inventory of the library, scanning every book on the shelves?
  3. If not, is there any other way to know if a book is missing? (that is, the catalog shows it as Available but it's not actually on the shelves in the right place)
  4. If you do track missing books, how many are missing right now? How long does it usually take for them to turn up?

I'm not planning a book heist. :) I'm preparing a talk about information technology and libraries for a local event for software engineers.

Engineers are always interested in “failure modes” -- that is, what happens when something goes wrong.

I didn’t get the response in time to change the talk, but the library sent me email this morning with these answers:

  1. There are 1,629,308 items in the collection.
  2. No, we do not do a complete inventory of our entire collection.
  3. We do monthly weeding (de-selection) reports for items that haven't circulated in 1-2 years and that usually catches most missing items. We cover almost the entire collection within one year. However, we also will do a system-wide ILS report and change items automatically to missing status in the computer that haven't circulated in branches in a very long time. We also do this for items stuck in transit mode between locations for a long time.
  4. We do not track missing items at a level that will provide us with statistics like return rate. Anecdotally, however, it is rare that missing items are located again. They are usually missing because of theft.

(I’ll just note that the way they actually track missing items means they wouldn’t detect items that are only misshelved for a month or two. There might be a lot of them. I find two or three every week.)

Anyway, the talk was picked up on Reddit programming and got some wonderful comments. My favorites:

  • “My high school did a volunteer day where we took our entire class year and spent an hour in a class learning how they sort the library books, then sent us each to a section to go through, find misplaced books and put them back in order. It took 2-300 of us ~5 hours to sort all of the library.” –Kimano

  • “This posting reminds of when I visited a warehouse that had automated storage and retrieval of items from the warehouse.

    “One of the cool things that had to happen periodically was essentially the real-world equivalent of defragmenting a hard disk. If you think a hard disk is slow, imagine how slow it is to physically move pallets and cartons!” —grandzooby

  • “The reason we can insert books in a shelf is that there are some gaps distributed between books, and insertion shifts a couple books around to make space. Insertion sort is O(n log n) is a fun research paper that describes a similar way to organize data in arrays, with enough bogus elements (gaps) for insertion to be logarithmic time, but not so many that binary search is super-logarithmic.” —phkuong

And several folks linked me to ShelvAR, a super-cool augmented reality app for keeping library books in order. (Incidentally, those guys seriously need to cheat to get higher apparent performance!)

Hack day was so great that I can’t wait to do another one.

09 May 2009

Recently I learned...

  • Your browser uses the public suffix list to determine whether two web sites may share cookies. This is not very robust but better than the previous strategy.

  • If you take a long strip of paper, fold it in half as many times as you can, and unfold it, it'll make an approximation of the fractal shape called the Heighway dragon.

  • If you take two fractions, say 1/2 and 1/3, and add the numerators and denominators, you get the mediant, in this case 2/5. I don't know much about the mediant, but it is linked to Ford circles in a way I don't really understand, and I'm told mediants give a startling way of approximating the value of continued fractions.

  • glibc's qsort only does an actual quicksort as a last resort. If there's enough memory, it does a merge sort.

    This is old news, and I kind of figured it was the case, but I never looked at the source code before.

And I was reminded that complex numbers are key to quantum mechanics, something I missed when I was writing about complex numbers a year or two ago.

We've been unpacking books that have been in boxes for a year and a half. It's like meeting old friends. I read the epic of Gilgamesh, the book of Joshua, and Six Easy Pieces. I love small books.

04 April 2008

This month I learned...

The past three or four weeks are a bit of a blur, but:

  • Just before he died, Beethoven claimed to be working on a Tenth Symphony. Fragments of this were discovered among Beethoven's sketchbooks in the 1980s (!), and musicologist/composer Barry Cooper stitched together a highly speculative, but performable, first movement.

  • I knew that John Harrison invented the first clock that could keep time on a ship and that such clocks cracked the longstanding problem of determining longitude at sea, leading to the first accurate maps. (H1 was his first attempt; his masterpiece, H4, was a 5-inch watch with a diamond-studded movement.) I didn't know that Harrison faced competition from an astronomical method relying on careful on-ship measurements of lunar occlusions of certain stars, huge tables of laboriously pre-calculated data, and maybe four hours of additional calculations to be done on the ship. It was a usability disaster, as one might expect. But at the time, the idea of making a clock run reliably on a pitching, rolling ship apparently seemed even crazier.

  • Bill McCloskey's memoize is a replacement for make in a few lines of Python. The complete source code fits on my screen. This is the coolest hack I've seen all year.

  • The word goodbye comes from the saying “God be with you”. According to the American Heritage dictionary's etymology note, “A letter of 1573 written by Gabriel Harvey contains the first recorded use of goodbye: ‘To requite your gallonde [gallon] of godbwyes, I regive you a pottle of howdyes,’ recalling another contraction that is still used.”

  • According to Wikipedia, the lungfish has the largest genome of any vertebrate. But as of today, Wikipedia does not say anything about the lungfish's lungs! (I usually try to contribute in cases like this, but here I haven't a clue.)

  • On the Mac, if ls -l output has an @ symbol here:

    -rw-r--r--@   1 jason  jason     54838 Sep 27  2007 #tamarin 9-25-07.colloquyTranscript

    then the file has extended attributes. These are used, for example, to mark files as “saved from the web”, triggering a warning if a user tries to open the file.

16 September 2007

Floating

It's not until you study math a little bit that you realize just how awful floating-point arithmetic is. I mean, so it's a little inexact. So what? But the following are examples of statements that are absolutely true for integers, rationals, and reals, but not for floating-point numbers (even setting aside floating-point infinities and “not-a-number”s):

a - (a - b) = b

If b > 0, then a + b > a.

The associative law: (a + b) + c = a + (b + c)

The distributive law: a × (b + c) = (a × b) + (a × c)

Cancellation: If a × b = a × c and a ≠ 0, then b = c.

In short, if you've ever done any reasoning about floating-point numbers, you were probably wrong.

It's easy to come up with formulas that floating-point arithmetic gets seriously wrong, especially for numbers very close to zero.

The inexactness is impossible to track in a useful way, so you never know just how bad the result is. Typically you just assume it's almost right until you find out it's wrong. The only surprising thing is that it works so often.

17 July 2007

Too cool for screenshots?

Mac OS X comes with a command-line utility, pbcopy, that copies its stdin to the clipboard. pbpaste dumps the clipboard to stdout.

06 July 2007

C puzzle

I was reading source code this morning and came across this curious line in a header file:

JS_STATIC_ASSERT(sizeof(JSGCThing) >= sizeof(JSString));

The effect of this macro is to check at compile time that sizeof(JSGCThing) >= sizeof(JSString). If the condition is not met, compilation fails with an error.

The puzzle is: how does this work? A hint is in the comments.

05 June 2007

Danger!

In Scheme and Ruby, procedures that modify variables or data structures are marked with a ! by convention:

  // Java
  static <T>
  void arraySwap(T[] arr, int i0, int i1) {
      T tmp = arr[i0];
      arr[i0] = arr[i1];
      arr[i1] = tmp;
  }

  ;; equivalent Scheme
  (define (vector-swap! v i0 i1)
    (let ((tmp (vector-ref v i0)))
      (vector-set! v i0 (vector-ref v i1))
      (vector-set! v i1 tmp)))

What's the point of this convention? Well, Scheme thinks this kind of behavior is slightly dangerous and needs to be highlighted. A little explanation:

If your variables or data structures can be changed, expressions have different values in different places within a program. The order of operations matters. The order of lines within a procedure matters. Procedures can subtly change global data as a side effect, and this can lead to rather subtle (mis)behavior.

Theorists call this language feature state. The word is intended in the sense of status, as in “the state of affairs” or “state of the Union”. C, C++, Java, Perl, Python, Ruby, and so on are stateful languages.

Maybe it sounds kind of, er, sweeping, or maybe absurd and stupid, to say that state is bad. But you already know this is true, at least sometimes: Global variables = bad. Unobvious side effects = bad. Leaving an operation half-finished when you throw an exception = bad (usually). Global state + threads = migraines.*

But what's the alternative? Well, there are alternatives, but it's beyond the scope of a quick blog post. Just like the boundary between static-typed and dynamic-typed code, the boundary between stateful and pure-functional code is getting a lot of attention these days. Interesting times.

What Scheme gets wrong here is that state-related bugs don't necessarily happen at the places where data is modified. In fact, the whole idea of marking the danger spots is a total whiff. The problem with state is that it permeates your program. The danger is everywhere a stateful variable or data structure is used, whether for read or write, directly or indirectly.

*It's not just that there are race conditions and potential crashes here. You can eliminate that with a single global lock. The problem is that a single global lock often isn't fine-grained enough; it creates a bottleneck. So you go to a fine-grained locking scheme, and that's where the migraines come in.

03 June 2007

Types

I've been working through Types and Programming Languages by Benjamin C. Pierce. It's filling in a lot of blanks for me.

According to the drafts, ECMAScript 4 will have an ambitious new type system with optional type-annotation. So for example you could write:

//code without types
function sortLines(text) {
    let lines = text.split(/\r\n?|\n/g);
    lines.sort();
    return lines.join("\n") + "\n";
}

// or with
function sortLines(text : String) : String {
    let lines : [String] = text.split(/\r\n?|\n/g);
    lines.sort();
    return lines.join("\n") + "\n";
}

Exactly what effect these annotations will take some explaining. Suffice to say, life is about to get really interesting. I'm sure I'll be writing more about this in a few weeks.

10 April 2007

Things of the moment

  • My current side project is fixing bugs in Python's DOM implementation.

  • Brainteaser: What if chess had a single level of undo? Specifically, after your opponent moves, you may, instead of making your next move as usual, roll the game back to the point just before your previous move and do something else instead. (Your game clock, though, would not be turned back.) How would this affect the game?

  • I'm trying to understand Haskell's existentially quantified types, and (a shallower question) why there's no Data.Hashtable.ST, darn it.

  • GoboLinux is a Linux distribution with a sane filesystem layout. From reading that one page, I like it. You can't fight with MSI for a week without realizing that the people who design installer systems have been working waaaay too hard, and that the fate of our civilization hinges on stopping them.

  • Yesno is a toy programming language in which every method call returns something immediately ...and returns the correct result eventually. It's either hilarious or awesome, probably. The obvious next step is to make it self-host.

  • In the spirit of yesno, here are two quick reviews of books I'm not done reading yet. (I'll refine these opinions later, maybe.) The Time-Traveler's Wife is too sentimental and too linear, and does not contain enough adventure or suffering. Oryx and Crake has a lot of the same strengths and weaknesses as 1984, but much more angst.

01 November 2006

Elements, part 2

RT asked me last weekend about programming. She just learned HTML. Now she's considering learning PHP.

It was this conversation that got me interested in Euclid's Elements. RT has a classical liberal education; she actually studied the Elements. (Sidetrack: Wikipedia says the Elements were “the basic text on geometry thoughout the Western world for about 2,000 years. For centuries, when the quadrivium was included in the curriculum of all university students, knowledge of at least part of Euclid's Elements was required of all students. Not until the 20th century did it cease to be considered something all educated people had read.” Now get this: the mathematical discoveries of the past hundred years are so mind-blowing, so revolutionary, that the relegation of the Elements to obscurity is arguably justified. The mathematician's view of geometry is forever changed; the Elements can never again be what it was. I mentioned this to SC at work. He says we're living in the golden age of mathematics.)

RT liked Euclid's approach of starting from a few axioms and deriving everything else from them. She wanted to know if it is possible to learn programming in the same way, from the ground up.

Here's my answer: Structure and Interpretation of Computer Programs. (You can read the full text for free online.) Elsewhere I've called this “the best programming book ever written”. As it turns out, Section 1.1 is actually titled “The Elements of Programming”. These first few sections still bear re-reading. Powerful stuff.

Is SICP a practical book? It walks a fine line. The programming language you'll learn, if you read it, is not Java or PHP or Ruby or C#. It's Scheme. Scheme has a simple design well suited to the Elements approach. You might find one of those others better suited to the program you're interested in writing, and if so, you might prefer some other book. On the other hand, page 1 of SICP starts by quoting an essay by John Locke; and then it tells you what a program really is, what the word means; and then by section 1.2.1 it's telling you things that I went through a whole CS degree program without learning. That kind of book.

27 September 2006

Amazingly bad

It reads like a deliberate insult:

To distribute a .NET Framework application both to 32- and 64-bit platforms, build two MSI packages, one targeted at a 32-bit and the other a 64-bit computer.

I have to say, Microsoft's support for 64-bit architectures is a total nightmare. MSI can't compete with that, but it's pretty darned awful.

.NET supposedly supports XCOPY deployment. Even MSI can make a directory and put files in it, and as far as I know the MSI database contains nothing platform-specific. What's the problem here?

I know the answer. The problem is that XCOPY deployment doesn't really work. Not for everybody, certainly not for apps with legacy dependencies. What's a tech writer supposed to do? Document how things would work if the world were ideal, because they might work that way for your app? Or give everyone the ugly workaround that most real-world apps need to apply?

Blaaargh.

06 September 2006

Gradual typing

Seen on Lambda the Ultimate: Gradual Typing for Functional Languages, a paper that introduces a new flavor of lambda calculus which they call (this is an approximation) λ?→.

Hard to explain why language geeks should be excited about this. I am.

We present a formal type system that supports gradual typing for functional languages, providing the flexibility of dynamically typed languages when type annotations are omitted by the programmer and providing the benefits of static checking when function parameters are annotated.

The proofs in the paper are machine-verified!

Relationships

I have a big file full of thoughts about programming languages. Re-reading it today I found this (edited):

Component-container relationships. I suspect these are all over the place. This is always a hassle. I wish someone would make it easy.

One idea is to put the component class lexically inside the container class:

        class Container {
            class Component {
                // each Component is part of a Container
            }
        }

Hmmm... I need to think about this some more.

And I realized: I now know the solution to this problem. In fact I've implemented it. Relationships.

I mean relationships in the sense of entity-relationship diagrams. Relationships describe how entities relate to each other, like the bolded words below:

His new paper cites 37 sources. (This could perhaps be written in code like this: paper1.cites(paper2))

Tom Hanks starred in the movie Big. (actor.starredIn(movie))

The patient Joe Bloggs is treated in the cardiology ward. (patient.isTreatedIn(ward))

Of course parent-child relationships are one case of this, but there are many others.

Okay. Now consider some hypothetical Python code:

    class Component(object):
        ...

    class Container(Component):
        ...

    # Define the relationship between these two classes...
    containment = OneToManyRel(
        Container, 'container', 'contains', Component, 'contents')

Here's what the OneToManyRel does:

  • Adds a container property to class Component. Each Component's container is always either a Container object or None. It's initially None, but if you like, you can just initialize it in Component.__init__ like any other attribute.

  • Adds the reverse property, contents, to class Container. This property is an object that looks like a set. It's initially empty. The two properties are automatically kept in sync, so if you do thing.container = None, the thing is automatically removed from its container's contents set.

  • Adds a method Container.contains(Component) that can be used to query the relationship.

  • Keeps a data structure of all the relationships, so you can do containment.items(), which returns a list of (Container, Component) pairs.

Here's a taste of how powerful this can be:

    class BuildStep(object):
        ...

    class File(object):
        ...

    # Each BuildStep produces some Files called its targets.
    # Each File is produced by one BuildStep called its buildStep.
    produces = OneToManyRel(BuildStep, 'buildStep', 'produces', File, 'targets')

    # Each BuildStep depends on some Files called its sources.
    # Each File is depended upon by some BuildSteps called its dependants.
    dependsOn = ManyToManyRel(BuildStep, 'dependants', 'dependsOn', File, 'sources')

That's very brief, compared to what you'd have to type to get this the traditional way.

So I have an implementation. In addition to OneToManyRel and ManyToManyRel, it has things called SymmetricRel and EquivalenceRel. (Warts: My current implementation doesn't use weakrefs, so it keeps objects alive until explicitly removed, which is kind of silly. Also, the properties currently look like iterators, not sets.) The whole thing is 355 lines of Python code.

All this is inspired by Inform 7 relations. I wonder about Ruby. I can only assume Ruby on Rails automatically does stuff like this for databases; I don't know about regular old in-memory objects though. I imagine this would be a breeze to implement in Ruby.

Anyway. I don't know how generally useful it will be, but it's certainly encouraging to come across an old gripe about the exact problem this solves.

15 August 2006

I blog parts of speech

There's a part of speech in English that I didn't know about before about a week ago. It's called the determiner. Some examples (quoting Wikipedia):

  • Articles: a, an, the
  • Demonstratives: this, that, these, those...
  • Possessive determiners: her, his, its, my, our, their, your
  • Quantifiers: all, few, many, several, some, every...
  • Numbers: one, two, fifty...
  • Ordinals: first, second, last, next...

I always figured they were just adjectives. But in English, determiners appear at the very start of a noun phrase, before any adjectives. Here's a quote; some noun phrases are in bold. The words in red are the determiners.

What are man and woman if not members of two very different and warring tribes? Yet decade after decade, century after century, they attempt in marriage to reconcile and forge a union. Why? I don't know. Biological imperative? Divine law? Or just a desire to connect to that mysterious other? In any case, it's always struck me as a hopeful thing.

—Diane Frolov and Andrew Schneider, writing for the TV show Northern Exposure

This has implications for programming languages, but I'll skip it. Olivia and I are rewatching Northern Exposure show by show, courtesy of Netflix. Great stuff.

09 August 2006

Tables need space to roam (horizontally)

One thing in particular that doesn't fit on half my screen is tables. Most tables need quite a lot of horizontal space. Otherwise you get horizontal scrolling, a nuisance. Examples:

  • A database query screen, like the one in SQL Server Management Studio, wouldn't work in two columns with code on the left and query results on the right. The code would fit nicely, but the results are tabular data. The people who made Management Studio apparently realized this; the app has two wide panels (code in the top half, results in the bottom half) and you can't change it.

  • The Inform 7 language has this ill-conceived feature where you can put tabular data in your program by literally typing in a tab-delimited table. As in:

    Table of Conversation
    topic reply quip
    "dream/dreams/nightmare/nightmares/sleep" "'Sleep well?' you ask solicitously.
    
    'Not really,' she replies, edging away from you. So much for that angle." "'Ghastly nightmares,' she remarks. You nod politely."
    "marriage/love/wedding/boyfriend/beau/lover" "'So,' you say. 'This is a little weird since we just met, but, um. Would you like to get married?'
    
    She looks at you nervously. 'Do I have to? I mean, I'd rather not.'
    
    Well, this could get prickly fast." "'I, er,' she says. 'I hope I'm not supposed to marry you or something.' Uh oh."
    ...
    

    That makes perfect sense, right? Well, I guess having line breaks within table cells makes it a little harder to read. And tabs look too much like spaces. But if you solve those problems, you're still left with this:

    Table of Conversation
    topic reply quip

    "dream/dreams/ nightmare/nightmares/ sleep"

    "'Sleep well?' you ask solicitously.

    'Not really,' she replies, edging away from you. So much for that angle."

    "'Ghastly nightmares,' she remarks. You nod politely."

    "marriage/love/ wedding/boyfriend/ beau/lover"

    "'So,' you say. 'This is a little weird since we just met, but, um. Would you like to get married?'

    She looks at you nervously. 'Do I have to? I mean, I'd rather not.'

    Well, this could get prickly fast."

    "'I, er,' she says. 'I hope I'm not supposed to marry you or something.' Uh oh."

    A table with three columns, two of which can hold large chunks of text, is going to have trouble fitting on half the screen at any reasonable font size.

  • You can force tabular data into non-tabular form. For example, the default view for Windows Explorer isn't tabular. (Or rather, it wasn't in XP. The Mac Finder is the same way.) But geeks tend to set the view to "Details", which is tabular, and leave it that way. We often need to be able to look at the largest files in a directory, or see which ones have changed recently, or select only the .cpp files; and in Details mode we can do those things by clicking on column headers, if not just by looking at the screen. It's a trade-off: determining the size or date-last-modified for a particular file requires a bit of care, because it's not immediately obvious how the rows line up. (Clicking on a file only highlights the filename, not the whole row, so that doesn't help.)

    A compromise UI would show things in a narrow format with the details initially shown in small print. It would be like Explorer's "Tiles" view, but arranging the tiles vertically, not in a grid. Ctrl+Scroll would zoom in and out, showing more or less detail. You could right-click on the small print and say "Break out Date Modified as a separate column". As a pleasant side effect, this UI would fit on half my screen. There's probably a reason I've never seen this, but speaking from innocence, it sounds nice.

02 August 2006

On the importance of being the right width

This shambolic post is about the width of things, specifically C++ code, Python code, and GUI windows.

I ran a script on some C++ code today, looking at line width. This probably raises more questions than it answers, but here are the results:

    2-5: #############
   6-10: ###########################
  11-15: ########################################
  16-20: ##################################################
  21-25: #########################################
  26-30: #####################################
  31-35: ###############################
  36-40: ###########################
  41-45: ########################
  46-50: #####################
  51-55: ################
  56-60: ###############
  61-65: ############
  66-70: ##########
  71-75: #########
  76-80: ########
  81-85: #########
  86-90: ###
  91-95: ##
 96-100: ##
101-105: #
106-110: #
111-115: #
116-120: #
121-125: #
126-130: #
131-135: 
136-140: #

This graph shows how many lines are 2-5 characters wide, how many are 6-10 characters wide, and so on. The script ignores indentation and comment lines, and lines that are blank or just have one character (usually }).

So you can see that, for example, about 75% of nontrivial code lines are 50 characters wide or less.

The trailing end of the curve actually goes all the way out to 500 characters, but almost all of these extreme cases involve very long strings.

Looking at some C++ code I personally wrote, the curve is similar, but for me about 75% are 40 or fewer characters wide, and the curve dies out entirely at 115 characters. I habitually break up long lines. Of the longish lines (40+ characters), most are not all that complex. They're long because the things involved have really long names:

  bool ImageGenerator::isImageObjTypeReloadable(uint64_t uObjectType)

  initCustomBasket(pOldBasket->getLocalResourceNameObject());

  Boa::WebBoaSubnetStartAttributes networkAttributes = wbNetwork.getDefaultStartAttributes();

(Disclaimer: Identifiers have been changed to protect the innocent. I'm just talking about line length and complexity, and in these respects the examples are genuine.)

Okay. Now, same script, totally different code sample:

    1-5: #################
   6-10: ######################
  11-15: ###########################################
  16-20: ##################################################
  21-25: #############################################
  26-30: ########################################
  31-35: ##################################
  36-40: ############################
  41-45: ######################
  46-50: ###################
  51-55: #################
  56-60: ################
  61-65: ###############
  66-70: ########
  71-75: ###
  76-80: #
  86-90: 

This graph describes the idlelib directory of Python 2.4. This is Python code, not C++. It's a similar curve, though.

Python and C++ are very different languages, but they (and all modern languages) have this in common: they'll let you write arbitrarily complex code lines. The compiler will accept a 3000-character line without batting an eye.

The teams that wrote these two code samples are undoubtedly very different, but they have this in common: they tend to break complex thoughts into two or more lines.

Part of this is just that it's easier to understand several simple, independent lines than one complicated one. I think part of it is visual, too. But the why doesn't matter; IDEs should take advantage of programmers' tendency to work in lines of a certain size.

Microsoft Visual Studio is an excellent example of an IDE that completely fails to do this. The current MSVS is all about the myriad gadgets and capabilities it offers—in ten trillion fiddly docking panels, none of which shows any tendency to make itself the right size or shape for the information displayed. Least of all the central code window, which initially gapes some 200 characters wide, but spinelessly cedes screen area to each new gadget you open until practically nothing is left.

By contrast, the Inform 7 GUI shows source on the left half of the screen and uses the right half for documentation and play-testing.

I can't rave enough about this arrangement. It works extremely well, at least partly because each half is a comfortable width.

It's possible to mimic this layout in Visual Studio: put Solution Explorer in a narrow strip on the left, code in the center, and a wide tabbed panel for everything else on the right. I'll try it out.

One last note. I have a program called HalfMax in my Startup folder. It runs in the background, and when I hit Windows+1 or Windows+2, it moves the current window to fill the left or right half of the screen, respectively. It so happens that a lot of the programs I use are almost ideal when filling half of my screen (given my font sizes, screen resolution, and so on). Right now I have the following applications open: Microsoft Outlook, an Outlook e-mail message, Perforce, some Command Prompts, SysInternals DebugView, some Notepads, some file Explorers, Microsoft SQL Server Query Analyzer, the Windows Services control panel, TestTrack, a Python shell, Emacs, Araxis Merge, Adobe Reader, Word, Visual Studio, and Internet Explorer. (I think this is just about all the applications I ever use. :) Most of these are either simple browser-like apps, Notepad-like editors, or shells: the kind of app that is significantly better half-maxed than maximized. The rest—Visual Studio, Windows Explorer, TestTrack—use that horizontal space for stuff like docking panels or table-view UIs.

28 July 2006

Human visual navigation

I think the region of the screen I can concentrate on at once is quite small, maybe twelve words wide and six lines tall. But then why do I prefer two large high-resolution screens to one low-res screen? I can't really use all that screen space, can I?

Let's talk about the navigation features provided by human vision. Although I have this 12x6 mental "window", the window doesn't stay still. I can read a whole screenful of text without touching the mouse. Human vision has built-in scrolling. I can take in more of the screen at once by sacrificing attention to detail; or I can concentrate on one line of code, at the cost of ignoring everything else for a moment: visual zoom. If I look away, then when I look back, my eyes easily snap to where I was. This is kind of like Alt+Tab in that it lets me hop back and forth between different things; call it visual switching. If I need to look for something, I'll zoom out, skim down without reading, find it, and zoom back in, all without thinking: visual search.

With a huge screen, I can scroll, zoom, switch, and search without touching the keyboard or mouse. With a smaller screen, I have to use the mouse or keyboard to navigate. Each motion takes time and a little bit of brainpower; and each time the screen changes, it takes me a moment to find my place. Eyeball scrolling is superior to software scrolling. Eyeball switching is superior to Alt+Tab.

Having dispensed with that paradox, I want to talk about the 12x6 window and what it means for PL and IDE design. More to come.

18 July 2006

Hiding algorithms and data structures

In Java, you might write:
class Thing {
    ...
    public int getCurrentSize() {
        int size = getBaseSize();
        for (SizeEffect se : getWorld().getAllEffectsEnchanting(this)) {
            size += se.getMagnitude();
        }
    }
}

In Python 2.4 or later, you might write this instead:

class Thing:
    ...
    def getCurrentSize(self):
        return self.baseSize + sum(
          se.magnitude for se in self.world.getAllEffectsEnchanting(self))

If that's not entirely clear yet, just read on. I'll come back to it.

In Inform 7, you would write this:

To decide which number is the current size of (subject - a thing):

Decide on the base size of the subject plus the total magnitude of the size effects enchanting the subject.

I hope that clarifies what the Python version is trying to say. The Python expression sum(se.magnitude for se in ...) is just a way of saying “the total magnitude of ...”.

Ah, but in Inform, you don't use anything that resembles loop syntax to iterate through the size effects. And you don't explicitly query anybody to get the set of relevant objects. You just state your requirements. You don't build data structures explicitly, either—I wouldn't want to post an example World object here, but I can show you the complete Inform code:

A thing has a number called the base size. The base size of a thing is usually 1. The base size of a person is usually 5.

A size effect is a kind of thing. A size effect has a number called the magnitude.

Enchantment relates various size effects to various things. The verb to enchant (he enchants, they enchant, he enchanted, it is enchanted, it is enchanting) implies the enchantment relation.

That's all. The last sentence is just a convenience so that later in your program, you can write “The giant growth spell is a size effect with magnitude 3. It enchants the wombat.” or “Now the wicked ginormosity spell enchants everything.” or “Every turn, if anything is enchanting the player, say ‘You feel prickles all over your skin.’”

Of course, Inform is probably cheating a little bit: in its niche problem domain, there are only ever a few hundred objects in an entire program. So it can implement the implied searches naively. Still, with a little whole-program analysis, the technique could scale.

Why does this feel so new? We've been there with Prolog and SQL, right? I guess Prolog had unrelated problems, and SQL is still awfully cumbersome to get to. I'm awfully impatient about these features showing up in a language I can use every day.

07 July 2006

Duck-typing and Scheme

Duck-typing advocates occasionally say that querying the type of an object is unnecessary. Certain Pythonistas are fond of this idea. I just don't see it.

Maybe querying the concrete type is unnecessary. (Aside: Python has a builtin, isinstance(), that does just that—but I found out a week or two ago that an object can lie to isinstance() about its type. It's just the most absurd design ever.) But once in a while, I need to know whether a given object can swim, at runtime, preferably without having to put it in the water. Surely in a well-designed language, these questions can be answered without unduly revealing how an object is implemented.

For example, in Scheme, you can't ask if a number is a floating point number, as opposed to a native int (fixnum in Scheme jargon), big-integer, rational, or big-decimal. But you can ask if the number is exact, meaning basically that it hasn't been subjected to rounding. You can ask if a number is an integer or not: (integer? x); but you are actually asking about the number, not its type. So (integer? 1.79e123) is true, as is (real? 1).

For numbers, the difference between querying an object's characteristics and querying its concrete type might seem unimportant. Also, numbers are a special case where the concrete type matters a whole lot, for performance. (The next Scheme standard will add a bunch of procedures like (fx+) that only work on fixnums.) But when you start talking about objects, the difference is whether or not you support duck typing, which I think is a big deal. Languages should make it easy to query an object's interface and inconvenient to query its implementation.