Sunday, December 29, 2013

Top 10 Albums of 2013

Just in time for the New Year, here's another list for you and another digression from my usual topics.



1. Flaming Lips / The Terror — the last two Flaming Lips albums have been excellent. They're dark, ragged affairs, not at all the polished weird pop of Yoshimi.

2. Jon Hopkins / Immunity — one of the best electronica albums in years. Crunchy, huge, pounding. Not exactly beat- or melody-driven, just amazing sounds.

3. Altar of Plagues / Teethed Glory and Injury — a black metal band that's coming full circle back around to riffs. There isn't as much tremolo picking here as tense atmosphere & well-timed brutality.

4. Kanye West / Yeezus — I liked My Beautiful Dark Twisted Fantasy a lot, but Yeezus does everything that album did—staggering egotism—better, with a more cohesive sound. A friend, not having heard the album, described it as "industrial rap," a weird label for Kanye since he's always been a pop artist at heart. But the beats are as much NIN as Just Blaze. The fact that there's only 10 songs & that there are fewer grandiose digressions (e.g. "All of the Lights") makes it more focused. MBDTF was interesting for its sprawling, diverse nature, but Kanye would do well to limit the sheer number of ideas & contributors he packs into his albums. He has plenty of creativity on his own & Yeezus shines due to that.

5. James Blake / Overgrown — Blake has a tremendous voice which quivers with insecurity, love, & despair. It's a powerful instrument sorely lacking in the dubstep scene which makes his work stand out. Blake's last album was good but inconsistent: I listened to the superlative first three tracks ("Unluck", "The Wilhelm Scream", & "I Never Learnt to Share") over & over, skipping the rest of the album. Overgrown lacks obvious standouts & is better for it. It's a rich experience where songs fluidly intermingle, no abrupt drops in quality.

6. Earl Sweatshirt / Doris — Odd Future's output has been erratic. Even the good albums (mostly Tyler, the Creator's, but Frank Ocean's Channel Orange too) tend to have half-baked songs that shouldn't have made the cut. Doris is the first great album by the crew's most talented member. Sweatshirt's flows are dense & rhyme-laden. He isn't a fast rapper or witty, he's obsessed with the sound of language & it shows. That the songs tend to be moody productions with plaintive lyrics (e.g. "Chum" & it's "get up off the pavement, brush the dirt up off my psyche" refrain) is a bonus.

7. Windhand / Soma — very low, overwhelmingly distorted metal. A nice job with the "voice lost in the machine" dynamic which has always been a favorite of mine.

8. Daniel Avery / Drone Logic —  this album hit a sweet spot for me. I've missed acid techno so much (Aphex Twin, where are you? Come back to us.) & Drone Logic does it straightforward, no frills, well.

9. Sadgiqacea / False Prism — brutal metal with a healthy dose of dissonance. Combines slow droning with rapid black metal, often in the same song. Sadgiqacea change things up just enough to make the music interesting without reducing its molten impact. The shortest song, "False Prism", demonstrates these strengths well, starting with a few echoing, quiet notes from a clean guitar before diving into frantic picking, and then towards the end becoming a slow, chugging affair.

10. The Range / Nonfiction — is this what trip-hop is nowadays? I like it. The songs with looped vocal samples toward the beginning are the best, like "Metal Swing".

Honorable Mentions


Burial / Rival Dealer — I've cheated in the past by putting Burial EPs on what's supposed to be a list of albums (what's an album, anyways?) so I'll attempt to make up it for it by leaving Rival Dealer off. It's great, though. Burial's recent swing into anthemic, ≈10 minute songs on his last three EPs—Rival Dealer, Truant / Rough Sleeper, Kindred—is wonderful. He's always been a master of atmosphere & the two-minute interludes of rustling wind work better as slow-downs in otherwise intensely emotional music as opposed to separate tracks.

The Haxan Cloak / Excavation — creepy, dark, & consistent in its execution. It's a good album but a bit too slow-moving, the atmosphere too thin in places.

James Holden / The Inheritors — pretty weird electronic music, the sort that sounds like circuits being twisted & soldered together rather than keys on a synth being pushed. Organic electronic.

Moderat / II — solid mix of R&B & electronica, catchy without being too predictable.

Wednesday, December 4, 2013

My All-Time NBA Starting Five

Possibly-surprising fact: I'm really into the NBA. This post is a serious detour from my usual subjects.


PG - Magic Johnson

Considered: John Stockton, Oscar Robertson

Magic Johnson is one of the most anomalous players the NBA has ever seen. More so than anyone else, he could (and did) play every position. He could rebound like a center and pass like a point guard. Magic's 52% career shooting average is the highest amongst point guards and his rebounding percentage (11.1%) probably ranks up there as well. His ability to play multiple positions defensively while leading fast breaks is a devastating weapon. To put it in modern terms, Jason Kidd has been an elite (for many years, arguably the best) point guard in the NBA; Magic Johnson does everything Kidd does but significantly better (Kidd's three-point shooting ability towards the end of his career aside).

It's difficult to leave Stockton off this list. He was never an elite scorer, but excelled everywhere a point guard should: good three-point shooter, terrific defender, better at generating steals than Magic, assisted on over half of the possessions where he touched the ball (an unbelievable statistic). In some ways, when building a team, it's better to have a prototypical point guard rather than someone unique like Magic, whose strength comes from his size and rebounding, not traditional point guard qualities. Stockton's ability to space the floor and set up others would doubtless serve a team of superstars well, given that there would be no lack of scoring talent on the floor. In the end though, the tremendous mismatches that Johnson causes (there isn't a point guard in the world who can guard him on the low post) as well as his additional rebounding win out.

The Big O is also difficult to leave off, but not necessarily because he's a comparable talent. Unfortunately, without a three-point line and many statistical categories (steals, turnovers), it's tough to tell just how good Oscar Robertson was relative to Magic. He had the same all-around type of game, with tremendous rebound totals for a point guard, and shot a very good 48.5% from the field while taking 7.5 free throws per 36 minutes (see how close those figures are to Jordan's below). But for what info we do have, he falls behind Magic in many vital categories: rebound, assist, and effective field goal percentages, per-minute assist and rebound totals, win shares. Robertson's star was built off of a few extraordinary early seasons and playing 40+ minutes per night; his career was great but not best-in-class.
 

SG - Michael Jordan

Considered: No one else comes close.

Shooting guard is the only easy choice on this entire list. Jordan is head and shoulders above any other shooting guard to have played the game. Jordan was known for being a dynamic scorer, someone who could create his own shot with ease and take defenders off the dribble. But he had a stunningly complete game: he rebounded better than your average guard, was every bit as amazing a defender as a scorer, generated steals, passed fairly well, and turned the ball over surprisingly rarely given the amount of time he spent handling it (9.3 career turnover percentage). Jordan shot a high percentage for a shooting guard at 49.7 and generated 7.7 free throws per 36 minutes with his aggressive drives. His only weakness is his poor three-point shooting: towards the end of his Chicago days he had a couple good years, but he was a lifetime 32.7% shooter from beyond the arc putting him well below most modern shooting guards.

Some would argue that Kobe Bryant is, if not better than Jordan, at least in the same league. There is no statistical validity to this argument. Bryant is worse is every category I mention above—rebounding, steals, turnovers, shooting efficiency, defensive win shares, free throws attempted per minute. It's barely true that Bryant is a better three-point shooter, but he's still below what you want from a SG at 33.6%. Objectively, he does not belong in the conversation. Julius Erving would be an interesting pick as he is actually better at some things—he rebounded and blocked shots at a SF level—but in the end Jordan is just clearly superior in too many categories to consider anyone else.

SF - LeBron James

Considered: Larry Bird

Short forward has surprisingly few candidates for the All-Time Team. Up until about the past decade, when the NBA started to showcase wingmen with tremendous athletic gifts like LeBron James and Kevin Durant, the position was not a premier one. Shooting guards and power forwards accounted for the majority of scoring; SFs were often role players who spaced the floor with shooting and provided defensive versatility, as typified by Bruce Bowen. The few historical exceptions were either high-volume, low-efficiency scorers (Baylor) or rebounding beasts lacking an elite all-around game (Cunningham, DeBusschere, Rodman).

Bird was a significantly better rebounder and distance shooter than James. James is a better passer who also turns the ball over a smaller percentage of the time. James, who seems to continuously improve in terms of scoring efficiency, has pushed both his true shooting and effective shooting percentages higher. He also makes up for his lesser (though dramatically improved) three-point shooting with an uncanny ability finish drives to the rim, which results in him attempting three more free throws per 36 minutes. In my book, free throws are the single most valuable source of points: they come with fouls which get the opponent into trouble and allow your defense to set itself on the next possession. In terms of defense, LeBron is clearly superior. Bird was a crafty and underrated defender but lacked lateral quickness; James not only blocks more shots than Bird (their steals percentages are close) but has more versatility with his quickness. In the end, that's what puts LeBron ahead for me: James has only a slight offensive edge but is a unique defensive talent. His win shares are significantly higher than Bird's which validates my conclusion.
 

PF - Tim Duncan

Considered: Karl Malone

This was a tough choice between two excellent players. Looking at their careers, a dichotomy becomes clear: Malone was a superior offensive player while Duncan is a better defender. Malone would benefit this team with his uncanny ability to run the floor for a man of his size and his scoring efficiency (57.7% career true shooting as opposed to Duncan's 55.1%). Malone actually wasn't a traditional big man offensively, however: he scored off of pick-and-rolls (playing with Stockton helped a lot here), dribble drives, fast breaks, and the occasional jump shot. He used his quickness as much as his strength to overcome defenders.

Duncan, on the other hand, is more of a traditional back-to-the-basket big man. He scores mostly via post-ups but also pick-and-rolls. While Malone was also an all-NBA defender in his time, Duncan's defense stands head and shoulders above: Duncan's block percentage (4.6 career as opposed to 1.5), defensive rebounding (26.5% career to 23.5%), and defensive rating (an incredible 95 to Malone's respectable 101) are all ample evidence of this. He manages to defend excellently while committing half a foul less than Malone per 36 minutes as well.

In the end, Duncan's defense outweighs the offensive benefit that Malone would bring. Also, while it'd be nice to have Malone's ability to run the floor coupled with the other fast-break superstars on this roster, it's actually even more appealing to have a post-up player in the mix. Duncan's presence down low could give the perimeter players a bit more space to take jump shots and drive into the paint. Since my center pick below isn't going to provide that low-post scoring, it's good to get it out of the power forward. Duncan's not just a good defender for a PF either, he's arguably one of the best defenders the game's ever seen at any position, but he still can't compete with my pick for center.

C - Bill Russell

Considered: Wilt Chamberlain, Shaquille O'Neal, Hakeem Olajuwon, Kareem Abdul-Jabbar

Center is, by far, the most difficult position to make a decision. The NBA has showcased many great centers, all of whom affected the game at both ends of the floor tremendously. Their high shooting percentages, team-leading rebounding, and massive defensive impact has historically made them basketball's premier position.

Bill Russell is also probably the most noticeably flawed player on this list: he's not a good shooter by any means. Russell tended to shoot mid-range jump shots, the NBA's worst shot, resulting in a miserable effective field goal percentage of 44 and a true shooting percentage of 47.1. However, he was an excellent rebounder, above-average passer, and superlative defender. While we don't have block and steal statistics for his era, he lead the league in Defensive Win Shares an unmatched ten years in a row and in eleven of his thirteen years in the NBA. In fact, his Defensive Win Shares are probably the most aberrational statistic in the entire NBA; they're almost 40% greater than the next best player (Duncan). He is the best defensive player ever. He won more championships than anyone else.

While I listed many other centers who rightfully belong in the consideration, the only one who gives me serious doubts is Wilt Chamberlain. Chamberlain and Russell were contemporaries and there is ample evidence that Chamberlain was a superior player. Chamberlain had more win shares, shot an incredible percentage, and was a good (if not at Russell's level) defender. Yes, Russell won many championships, but on a set of deep Celtics teams that featured other superstars. Russell won 5 MVPs to Chamberlain's 4. In the end though, I have to pick Russell. In filling out a roster, you want someone who makes sense given your other players. Russell is a defensive anchor who doesn't need to put up shots offensively. He fits in any lineup. If I could have one player to build a franchise around, it would be Russell and I wouldn't regret it for an instant.

Caveats

Traditional NBA and modern (1980  and on) NBA stats are not comparable because of differences in pace and the three-point line. Older games had more shots, more misses, and stratospheric rebounding totals. New games benefit from the three point shot and new statistics, such as blocks, steals, and plus/minus figures. The three pointer is an ongoing problem; the line keeps getting moved back further. Contemporary players are shooting more difficult threes than Larry Bird did in the 1980s. I tried to correct for the NBA's changing rules but the effort is ultimately futile; we cannot know if the classic greats of the game could compete with even mediocre modern players. My unsupported guess is that athletes have evolved; LeBron James would crush Oscar Robertson if the two competed in their prime.

I do want to take a moment to point out that David Stern and the NBA office clearly have an agenda behind their recent rule changes. They keep pushing the three point line back, a couple of inches every couple years now it feels like. They create new rules (no hand checks, the "no charge" semi-circle, the unspoken ban on travelling) which benefit drives. They are trying to generate dunks with rule changes. The contemporary NBA discourages long jump shots, zone defense, and perimeter play in favor of drives, isolation matchups, and flashy dribbling. Whether that is right in any sense is clearly irrelevant; it's a marketing choice and dunks are exciting. But I do wish somebody (for all the innumerable talking heads, I have yet to hear anyone mention what I consider to be an evident trend) would talk about it.

Saturday, November 9, 2013

thanks to #libtechwomen

Yesterday, Travis Good of Make Magazine gave what I thought was a pretty good keynote. He talked about technology, progress, makers, community—it hit all the right spots.
But one thing that crossed my radar, thanks to the wonder that is librarians on Twitter, is that much of his language was gendered:


I consider myself a pretty sensitive person with respect to these issues. In language as well as action, I try to let things be neutral and fair, evicting unnecessary and damaging assumptions. But I didn't notice the gendered language at all until I saw the tweets calling it out. And more than anything I want to say: I appreciate this. I need the reminder. We all do. It can't stand and it's not going to change unless people are persistent, unless they call put even the most seemingly-innocuous assumptions. Because they're not innocuous. Because we need to say what we mean, not something that's close but shrouded in the biases of our past.
So, thank you, #libtechwomen, and everyone else who fights this fight. We appreciate it and learn from you.

Tuesday, July 23, 2013

Adding LibGuides to Drupal's Search Results

This will be another super specific post about how to do something useful for libraries in Drupal. The tl;dr is that you can use LibGuides XML Export, the Feeds module, and the Feeds XPath Parser module to make LibGuides show up in your Drupal site search results. So when users search for "english composition" and you don't have any study guides on your Drupal site, something relevant from LibGuides might show up.

I was inspired to do this by the Drupal in Libraries book, though I haven't read it (I saw it mentioned in American Libraries). I didn't see specific details in the book's preview, and Michigan is putting the XML into their Solr search index which is too sophisticated for my small college, so I thought a brief write-up might benefit other libraries who have LibGuides but don't use Solr. Libraries using other CMSs might still benefit from the general outline, though the specific details won't be useful. I'd be shocked if Wordpress libraries couldn't do the same, using WP All Import or other plugins.

These directions are specific to Drupal 7; I bet the same can be achieved in 6 but I can't vouch for any of the settings or code being the same.

Set-up: LibGuides & Modules

In order to do this, you have to do a couple steps first to prepare both LibGuides and Drupal.

  • Purchase the Images and Backups Module from Springshare. In my experience, the pricing is very reasonable, and the "images" part of it means you can upload images to LibGuides which makes adding them to guides much, much easier for authors.
  • Install the Feeds module, a popular and well-maintained module for mass importing nodes from structured data (RSS/Atom feeds, CSV files, OPML files) into Drupal
  • Install the Feeds XPath Query module which adds an extra parser to your Feeds installation, allowing you to import nodes from arbitrary XML documents

Once you've done these three steps, download the XML export from LibGuides (Springshare will email you when it's ready) and enable both modules in Drupal.

Process the XML

I don't work with XML much (shame, librarian, shame!) but this is a step where you could edit the LibGuides export to make it more useful as an imported node. In my pre-processing, I only wanted to accomplish one thing: when I import the nodes, I don't want any unpublished or private guides to be published in Drupal. We have a few under-construction or private guides that shouldn't show up in search results.

To do so, there's just one Drupal quirk you have to know: later on, in configuring the way your data maps to Drupal nodes, you'll be able to map the contents of an XML element to a Drupal node's "publication status" field. 1 means published and 0 means unpublished.

Luckily, the LibGuides XML has a <STATUS> element underneath each <GUIDE> which you can easily map to either 0 or 1. To process the XML, I performed a simple pair of search-and-replace operations in Sublime Text:

  • Search for "<STATUS>Published</STATUS>" and replace with "<PUBLISH>1</PUBLISH>"
  • Search for "<STATUS>.*</STATUS>" and replace with "<PUBLISH>0</PUBLISH>"

That second search and replace uses a teeny bit of regex: the period stands for "any character except a line-break" and the asterisk means "any non-zero number of the preceding character". So I'm searching for any non-empty string of text inside of a <STATUS> element and turning it into <PUBLISH>0</PUBLISH>, which works because all of my published guides no longer have a <STATUS> element after the first search-and-replace.

Configure the Feeds Importer

Back inside Drupal, we need to create a new content type and set up the Feeds module to receive our XML file.

  • Under the "Structure" menu of the admin toolbar, select Content Types
  • Add content type and then give it a name and description, e.g. "Imported LibGuides"
  • Add fields to your new content type, which at the very least should contain two new fields: an "ugly URL" field for LibGuides that don't have a friendly URL, and a "friendly URL" field. You can make these Text field types with the standard settings.
  • Under the "Structure" menu of the admin toolbar, select Feeds importers (or visit {{drupal root}}/admin/structure/feeds)
  • Add importer and then give it a name and description, e.g. "LibGuides Importer"

There are a lot of settings here, which can seem intimidating, but is actually great. The Feeds module gives you control over how data is imported into Drupal and everything is straight-forward if you take the time to read through it. I'll walk through my basic settings, but just know that you could do whatever seems reasonable here and be OK; the only piece of this post you might need to reference are the XPath queries later on.

  • Basic Settings
    • Attach to content type: select your LibGuides content type here
    • Periodic import: off, periodic import is only for grabbing nodes from web feeds, e.g. RSS
    • Import on submission: check
  • Fetcher: File upload
    • Allowed file extensions: you can leave as is, but I put XML since I'll only be uploading XML files
    • Upload directory: leave as is
  • Parser: XPath XML parser (this option only appears if you installed Feeds XPath Query)
    • Settings: see the section below on the XPath queries, but trust me this won't be that painful
  • Processor: Node processor
    • Bundle: select your LibGuides content type again
    • Update existing nodes: this is a bit of a judgment call, but you'll be fine with either "Replace existing nodes" or "Update existing nodes."
    • Skip hash check: I leave this unchecked but you'd be fine either way
    • Text format: your call, I leave as "Plain text" which is fine for search results
    • Author: anonymous, or your user if you want to brag about how many nodes you made
    • Authorize: probably should leave checked
    • Expire nodes: Never
    • Mapping for Node processor: make the Title, Body, Published status, Friendly URL, and Ugly URL fields all map to an "XPath Expression" source. The two URLs fields are ones we created with our Imported LibGuides content type, so if you chose a different name for them back then they will appear differently in the Target drop-down options here.

Whew, we're done! I know that looks like a lot, but Feeds has a pretty nice UI for such a sophisticated and powerful module.

Parsing XML with XPath

Now for the fun part: we need to map XML elements in LibGuides to Drupal fields using XPath expressions. We also get to say things like that which only .01% of humans understand.

XPath is a query language for XML, if you know SQL or CSS it's kind of similar. It gives you a way of traversing the structure of an XML document to retrieve the contents of various elements. The LibGuides XML is structured in a pretty logical, simplistic manner so writing our queries won't be tough. Back in the Feeds importer settings that we were just editing, select the Settings link under the Parser section. This gives us a menu where we can write our XPath queries. Here's the setup that I use with some English translations:

Context: //GUIDE

We want our queries to run in the context of each <GUIDE> element. We could do without this, but it means we'd be prepending /LIBGUIDES/GUIDES/GUIDE/ to each query below, which is silly.

title: NAME

body: DESCRIPTION

Set the name of the LibGuide to the node's title and the body of the node to its description. The description is the brief sentence which shows up underneath the name of a LibGuide.

field_friendly_url: FRIENDLY_URL

field_ugly_url: URL

Each <GUIDE> element has two URLs, so we map both of those to the two custom fields we set up on our Imported LibGuides content type. Once again, if you named your fields something different, their machine-readable names (which is what you see in this menu, they're just lowercase with underscores instead of spaces) will be different.

status: PUBLISH

Remember when we edited the LibGuides XML to set up a <PUBLISH> element that's either 0 or 1? That's where this mapping comes into play, taking that Boolean value and using it as Drupal's publication status field.

You can leave all the "Select the queries you would like to return raw XML or HTML" options unchecked. Note that this could provide some interesting options if you were doing more sophisticated things with LibGuides, since the XML export contains all the raw HTML of the various boxes in each guide. Debug Options can also be left unchecked, although if you're testing this process I recommend checking them off. The debug options show you what Drupal found with each XPath query, which can help you configure the importer properly.

I leave "Allow source configuration override" unchecked as well. Since we just set up our XPath queries the way we wanted, there's no need to override them later. However, you could do something interesting where you set up a generic LibGuides importer in these settings, then have multiple different ways of mapping the XML into nodes.

Redirecting Imported Nodes to LibGuides

Before we actually import our LibGuides, we want to make sure they're handled appropriately. That is, we don't want people clicking on their search results simply to see some lame text and URLs on the screen, we want them to be redirected straight to the LibGuide.

There are probably other ways to do this, for instance the Field Redirection module, but I use node templates, which are PHP templates that apply to only specific node types. Under the Templates folder of your theme (which will be somewhere in sites/all/themes likely) create a file named "node--imported-libguides.tpl.php" where "imported-libguides" is whatever you named your LibGuides content type but with hyphens replacing spaces. Inside that template, paste the following PHP:

<?php
// redirect user to LibGuide rather than node if user is not signed in
// uid 0 means anonymous user
if ( $user->uid == 0 ) {
  // prefer friendly URL if available
  if ( $node->field_friendly_url ) {
    drupal_goto( $node->field_friendly_url[ 'und' ][ 0 ][ 'value' ] );
  } else if ( $node->field_ugly_url ) {
    // ugly_url should always exist but just in case, use a conditional
    drupal_goto( $node->field_ugly_url[ 'und' ][ 0 ][ 'value' ] );
  }
} else {
  print render($content);
}
?>

I've written comments in the code, but essentially here's the path this code steps through:

  • Is the user anonymous? If yes, redirect them. If not, we assume the user is some kind of editor, so we print out the lame text fields. This makes it easier for librarians to edit nodes after they've been imported, but assumes that your users don't have Drupal accounts. If they do, you'll need to consider the first if condition thoroughly to make sure only the right types of users are seeing the plain text.
  • Does the node have a friendly URL? If so, redirect anonymous users to it.
  • If not, the node must have an ugly URL, redirect anonymous users to that.

I noted it above, but because it's so important: if you allow users to create Drupal accounts, this template won't work well. It won't expose confidential data or anything, but it's definitely meant for Drupal sites where all non-editor traffic is anonymous.

Your theme may also have a particular way of printing out nodes that you want to stick to; in that case, you'd be better off copying node.tpl.php or another node type template rather than using my code verbatim. You could put the logic piece of this code at the top of your node template, dropping the else clause at the end. That would work fine as long as it's named appropriately, e.g. "node--imported-libguides.tpl.php".

We're Almost There

Now that our template is set and our importer configured, we need to create an importer node, give it a file, and let it run wild. Go to {{drupal root}}/import to see a list of available importers, including the default ones that come with the Feeds module and your LibGuides Importer. Select LibGuides Importer and you're greeted with the usual node editing form, except this time there's a place to upload a file towards the top. Use that to browse to the processed LibGuides XML, then upload it. You can leave the body and other fields blank.

Once you've created this node, it will have an Import tab with an identically named button. Simply click that and your nodes should be created in Drupal, with whatever debug messages you chose in the importer displaying as well.

Totally screwed up the XPath queries, causing a bunch of broken and useless nodes to be imported? No worries, the importer node that you just created has a Delete items tab which can delete any of the nodes which it imported. This makes trying out a Feeds importer rather risk free; just keep trying until you get it right.

Final Steps

Drupal's internal search index will still need to index the new nodes before they show up in its results. You can run cron a few times depending on how many nodes you just added and they should show up. Try a search for the title of a LibGuide that wouldn't return any of your other pages, and make sure clicking on a LibGuide result from an anonymous session causes you to be redirected to the guide.

As LibGuides are added and removed, you'll have to sync them to their Drupal nodes again. However, once you've done the process once, it only takes a few minutes to grab a new XML export, upload it, and click the import button.

Monday, July 1, 2013

Foreign For-In, or Python as a First Language

...being a brief recap my experience at the Python Preconference at ALA Annual. In general, the session was a smashing success and I was elated to see a diverse group of people picking up Python so quickly. Without going into details elsewhere, which I think other attendees or organizers will cover, here's one struggle and one pleasant surprise from the preconference.

Explain a For-In Loop

Describing how a for-in loop works was difficult and I repeatedly ran into attendees who just couldn't quite grok it. A Python for-in loop looks like:

for word in wordlist:
    print word

That would loop through the wordlist data structure, which we'll say is a list (similar to an array in other languages), printing each term to the screen. Simple, right? But it's actually pretty weird, because in the above example what exactly is word? It's a local variable that gets a new value each time through the loop. If for-in loops for lists didn't exist in Python, you might implement them like so:

i = 0
while i < len( wordlist ):
    # being super explicit here
    word = wordlist[ i ]
    print word
    i = i + 1

len( wordlist ) here returns the length of the wordlist list, for non-Python people. Otherwise, I assume the syntax is straightforward for anyone who knows a little code. The biggest disadvantage to this implementation is you end up with two variables in the scope—i and word—neither of which is useful after the loop has run.

I'm not sure my explicit for-in loop is more clear to a new programmer, but it's my conceptual model. Students struggled with understanding the for variable's name; where does word come from? In the lecture, Becky Yoose used this example:

for fruit in pies:
    print fruit

The reaction from attendees seemed to be "since pies is a list of different fruits, the variable name has to be 'fruit' here." As if Python was somehow doing natural language processing to figure out a good descriptive term of an individual item in a thematic list. It's a weird thing to grasp conceptually, perhaps the crux being you're getting a variable without any assignment statement. That's a nice convenience for programmers coming from other languages but it obscures what's going on for learners.

Nested Loops & First Languages

On the other hand, I found that a lot of our exercises and final projects involved nested loops, sometimes three to four layers deep. Everyone seemed to absorb this without conceptual difficulty. Maybe it's my own experience speaking here, but I get more and more anxious the deeper my indents go. A lot of this anxiety is based in JavaScript, where blocks wrapped in curly braces tend to take up more space and are harder to parse than in whitespace-happy Python. The uglist code in the world is an instantly-invoked function expression which ends in a bunch of closed code blocks:

            }
        }
    }
}( 'this happens way too often in JavaScript' ) );

Python's conveniences, like range() and how the for-in loop works seamlessly across different data types (lists, dictionaries, even strings. Strings, people!) are a serious boon to beginners. I still think JavaScript makes a great first language for a few reasons: 1) everyone already has it installed via their web browser, so there's zero setup barrier, 2) the web is where data and applications live these days and JavaScript is the language of the web, and 3) a trivial amount of jQuery can make cool things happen. Other languages require more investment before the cool things go down.

But the setup process wasn't an issue for the preconference. We held a help session the night before and only two people came; one of them already had Python installed and on the Windows path, they just needed confirmation that they'd done it right. A number of factors contributed to the ease of setup: many attendees had Macs which typically come with a 2.6.x or 2.7.x version of Python, the Boston Python Workshop docs are great and cross-platform, and a fair portion of attendees were advanced computer users. So with an easy setup, Python (or Ruby) is a sensible choice for a first language.