Since I don’t have much forward-looking stuff to discuss these days, I decided to reach back and tell a few tales about side projects, career trajectories, and just some damn good luck.

In 1993 (yes, 22 years ago), I was working at one of the very first nationwide ISP’s, a small company known as Spry. Spry was trying to get off the ground with 2400b/s modem POPs (points of presence) in every major metropolitan area, selling something called Internet In A Box: An early (pre-Netscape!) browser, a gopher client (no kidding), an email tool, and a few other things. Today it seems unbearably primitive, but at the time it was a freakin’ miracle; customers could put in a CD, type in their zip code, and within minutes they were surfing the web! In 1993 there wasn’t a hell of a lot of web to surf if you weren’t a deep nerd, but hey, the cart finally had a horse to pull it.

I was working tech support. I was down in the basement, helping people understand their tech issues, explaining how to put in the CD and walk them through the set up process. It was a lot of handholding. It was labor intensive. I was one of 24 people at this particular bank of telephones, working the 6am through 3pm shift. It kinda sucked.

One day a woman appeared at the steps of the basement space. She actually bent down and leaned her head in, almost as if she feared touching the floor and becoming mired among the peons. I’d never seen her before. “Does anyone here know Perl?”

I raised my hand. I was the only one. “I know Perl.”

“They want to talk to you upstairs.”

Upstairs, Stuart, a manager for Spry’s sister company, a network provider, had a problem. Spry knew it needed content on the web for people to visit, so it had decided to leverage its Internet access and provide web hosting. Nobody there had any idea how. Worse, their very first customer wanted to be able to collect contact information from people on the web. The customer had a magazine article saying it could be done with a technology called CGI, which was written in Perl. Perl, in turn, ran well on SunOS, which was a good thing because that was the OS of choice at Spry. “Do you think you can make this work?” Stuart asked me.

Here’s where serendipity becomes important. I’d been a Linux nerd for barely a year; I’d installed it a year before and found it a much more comfortable environment than Windows after my precious Amiga 2000 had died. But I’d been on the Internet for a while already, and since I was playing with Linux I already knew how to compile things. I had a vague notion of web servers, and I knew Perl. “I think so. It can’t hurt to try.”

They gave me my own SparcStation and what was, for 1993, unlimited bandwidth. In very short order I had found the NCSA webserver, downloaded, configured (with --with-cgi, whatever that was), and running. I also installed Perl. I read the magazine, ran through the example, and had it up. I was able to change the example and fit it into the client’s pages. It took three days, but I had a working demonstration that matched the customer’s need. I even automated mailing the results every night.

They nearly tripled my salary, gave me an office on the top floor with a window. I was one of the very first paid webmasters in the United States, and that’s how Genielift became one of the first companies in the world with a web presence.  I stayed at that position for five years, and felt like I was constantly producing miracles.

Here’s the other part about serendipity, the Perl part. I had literally taught myself Perl two weeks prior. I’d never worked in a real scripting language before that; I’d written a few toy filters in AWK, and maybe edited a shell script, but prior to that I’d only ever worked in “real” languages: C, Pascal, Cobol, Fortran.

I’d taught myself Perl because Usenet’s porn repository had changed to using a new encoding technique that none of the software I used recognized. It was a minor tweak to uuencoding, but it broke everything. I wrote a Perl script to troll through the database (which was still disk-accessible; those were the days!) and decode every image it found, with a file to record work done so it wouldn’t process an image twice.

It was fun, and easy, and amazing, and I started to write a lot of Perl scripts.

But when I think about that: I had just taught myself Perl, and two weeks later that knowledge tripled my salary.

20Jan

Adless: Seedless for Twitter

Posted by Elf Sternberg as Uncategorized

Following up on Seedless, I present Adless: a Tampermonkey extension that looks for the [Dismiss] button on your Twitter feed’s promoted items and pushes it for you:

function adless() {
  var stats = document.querySelectorAll('.dismiss-promoted');
  var lines = [];
  for(var i = 0, l = stats.length; i < l; ++i) { 
    lines.push(stats.item(i)); 
  }
       
  lines.forEach(function(a) {
    var e = document.createEvent("MouseEvents");
    e.initEvent('click', true, true);
    a.dispatchEvent(e);
  });
    
  setTimeout(adless, 1000);
}

setTimeout(adless, 500);

I’m sure a lot of people need this, but not many would write it.  This is a tampermonkey script (tampermonkey is like greasemonkey, only for Chrome); Tampermonkey is an extension that lets you modify pages on the fly as they’re coming back from a server using Javascript.  Tokyo Tosho is my favorite torrent site for scanlated manga and anime, but they have a bit of a problem: they have no way to filter out anything on the list that isn’t seeded (i.e. isn’t really available).

The script below is a Tampermonkey script, raw.  What it does is fairly obvious: it finds the “Seeds: 0″ token and filters out any entries in which that appears.  TokyoTosho’s layout is a bit old-school, so it took some fiddling but it works, and it saves me a bit of time.

Use at your own risk.

function seedless() {   
  var stats = document.querySelectorAll('.stats');
  var lines = [];
  for(var i = 0, l = stats.length; i < l; ++i) { 
    lines.push(stats.item(i)); 
  }
  
  lines = lines
    .map(function(a) { 
      return [a, a.innerText.match(/S\:\s+(\d+)/)]; })
    .filter(function(a) { 
      return (parseInt(a[1][1], 10) == 0); })
    .forEach(
      function(a) { 
        var p = a[0].parentElement;
        p.previousSibling.style.display = 'none';
        p.style.display = 'none';
      });
}

setTimeout(seedless, 500);

04Jan

2014 In Review

Posted by Elf Sternberg as Uncategorized

Being a professional developer really takes some of the wind out of your non-professional sails.  Last year, I had exactly two new repositories added to Github: git-wc and mp-suggest.  The first was a simple wordcount handler for git that would calculate the difference between your current repository and your work in progress, and tell you how much you’d written that day.  I wrote is as a tool for NaNoWriMo.  The second is a fairly straightforward tool for taking apart a directory full of MP3 files and, depending upon the command line flags provided, prints out a bash script for making the contents of the directory consistent: same genre, same album, (possibly) same artist, and some heavy lifting to clean-up the titles, or to derive the titles from the filenames.  It’s not rocket science, but it was fun, it’s a tool I use regularly, and it’s written in Hy, a lisp written for the Python VM.

What did I do in 2014?  Well, mostly I worked for my employer, Splunk, on a pair of projects: first a window manager for data panels associated with the Splunk server; and more recently with the latest revision of the Splunk for Microsoft Exchange application.  I mastered the fine art of managing Splunk configuration files via its REST API, which is absolutely no fun (and the ACL API is a nightmare).

I also read a megafrackton of articles about two distinctly different technologies: Lisp and Haskell.  Lisp’s homoiconicity appeals to me much more than Haskell’s mathematical underpinnings, and I’m assured that there are uniformities between them.  The problem with these technologies is that my employer isn’t really interested in either; since I work with the applications group, what I write has to be comprehensible to third-party developers, they have to be showcases of what’s possible using Splunk with a Python back-end and a Javascript front.  So although I love Hy and ClojureScript, it’s unlikely that I’ll be working in them professionally anytime soon.  Sad, but true.

I’m still not sure what a Monad is.

This weekend I had a project to write.  I have a huge archive of MP3s left over from the mid-90s through mid-00′s, and they’re a bit of a mess and moving them onto my iPod is always a pain in the neck because they might be inconsistently labeled or have proto-Unicode issues or whatever.  So I decided to write a little command line utility that would help me sanify my files.  Since I’m old-school and organize my songs by album and genre, my tool  would pick the most frequent album and genre names from the ID3 files, while taking in alternative sources for those if they weren’t complete, such as the name of the directory (which also would give artist, if not otherwise available).  It would take the song title from the filename if it weren’t available in ID3.

And I decided to write it in Hy.  Hy is a Lisp parser that produces Python AST, and since the Python VM accepts and executes raw AST, it’s possible to write performant Python using a completely Lisp-like syntax.  Having not written Lisp in many years, this sounded like fun.

You can find the entire source code at my elfsternberg/mp_suggest repository on Github.

I like the feel of this code.  It’s a series of let statements that gets the MP3s and then creates a new list of details (genre, album, artist, title, position), then searches through the command line options and details for genre and album names.  Each let statement creates a new object; it looks mostly like immutable code, although that fantasy is blown by my frequent use of ap-reduce.  Some of the objects created are anonymous functions containing closures, so they’re new functions with pre-created knowledge of things they care about, such as the legal commands and whether or not to override the ID3 title with a modified copy of the filename.

Overall, it doesn’t feel much like Python.  It doesn’t feel like Javascript, either.  It feels most like RistrettoReginald Braithwaite’s version of Coffeescript, chock full of functional declarations rather than procedures.  The threading macros and anaphoric macros are huge time-savers, and the organizational principles of Lisp’s parenthetical syntax gave me plenty of vertical room to work with, a luxury I don’t usually have with Python’s whitespace-delimited syntax.

I’m not sure I’ll ever write in Hy again.  I’m not sure I have a need to; I know I’d never be able to talk my employer into using Hy; they don’t even want to discuss preprocessors for web apps like Haml, Less, or Coffeescript.  I’d love to have something like this that I really enjoyed using for Javascript, but I haven’t found anything beyond Coffeescript that really has lispy expressiveness and pythonic power.  Still, if I were doing more Python at home, Hy is really the language I’d be doing it in.  Hy for Python, Clojure for Java, and Coffeescript for Javascript.  Because Clojurescript is still not good enough.

25Oct

Facebook as a psychic shopping mall

Posted by Elf Sternberg as Uncategorized

I was hanging out with a friend last night and she said, “I think Facebook is designed to distract you. You go in looking for one thing, but oh, hey, there’s this other thing, and what’s Aunt Marsha up to? Eventually you find yourself spending hours on it.”

What she’s describing is the Gruen Transfer, “the moment someone experiences after entering a shopping mall when, surrounded by an intentionally confusing layout, lose track of their original intentions.” You enter saying “I came to buy a coat” and leave “Ooh, shoes! Cameras! Necklaces! Smoothies!” that much poorer.  Modern mall architects deliberately go for this effect; their objective is to keep you inside the mall as long as possible, in order to increase retailer’s opportunities to sell to you.

Facebook appears to have hit a Gruen Transfer state completely by A/B testing, which if you think about it, is exactly what A/B testing of ad-driven sites is intended to do: keep your eyeballs in front of the advertiser’s windows for as long as possible. Facebook has just had more time, more money, and more talent to throw at the issue than anyone else.  The objective remains the same: to distract you from your objectives, to encourage an increase in sales.  Your pleasures and interests don’t enter into it.

This may be why Twitter is “experimenting” with their newsfeed.  A/B testing shows them that they could keep your eyeballs on their interests for much longer; the only question remains how corrupt they want to deviate from their original mission.

Programmer Zef Hemel recently made a confession, aptly titled I Hate Puzzles. In it, he confesses to a deep loathing for the recruitment come-ons that read “Do you love puzzles?” and even more for recruitment ads that are puzzles. He says, eventually, that he and his wife agreed they don’t understand why people do puzzles. “Sure, if you don’t have anything else to do. We had plenty more useful stuff to do.”

And while I enjoy video games, and especially enjoyed the puzzles within the Portal series, in general I avoid the kind of puzzles found on the average bookshelf or in the average game store window display. Not just jigsaw puzzles, but sudoku, crosswords, the whole gamut of paper-and-pencil puzzling that seems to take up some people’s entire commutes.

Zef then says that he doesn’t care much about algorithms. Algorithms aren’t at the heart of programming: design is. Coming up with a syntax, a domain specific language, an API, that reduces the friction between what another developer wants to acheive and the computer’s capability to achieve, are at the heart of almost all that he does.

And he’s absolutely right.

If I want a word puzzle that’ll increase my vocabulary, I’ll write a fucking novel with my thesaurus and my mind open. I’ll delight in solving the puzzle (yes!) of making sure the characters are three-dimensional, the plot is consistent, the foreshadowing is appropriate, the guns on the mantlepieces have all been fired or elided or revealed to be red herrings. I will hope to make someone else’s day sexier, happier, thoughtful, and delightful.

If I want a brain-teaser, I’ll spend my days trying to understand monads, write tools that let users write tests to a database more efficiently, or (my current unreleased project) write my own goddamn programming language. I will solve not just a puzzle, but a problem. I will hope to make someone else’s day easier, more interesting, and more effective.

I won’t write experimental novels, messing with the structure to produce quasi-lyrical novel-like works, although I admire writers who try that. I don’t have to create new algorithms for my programming language; transducers, scope, homoiconicity, Hindley-Milner, pattern-based dispatch and automatic currying are solved problems; I just want to put those puzzling pieces together in my own idiosyncratic way and hope to learn something about how programming languages work as I do, to make myself a better programmer and to give myself tools for the project after that.

I am a novelist who doesn’t do crosswords. I am a programmer who doesn’t enjoy sudoku. And I don’t do those things because I have much more interesting things to do with my grey matter.

New things.

Last night, I went to a coding meet-up where a few of the others were playing a game.  There’s a website called Exercism.IO, where you can download code exercises and submit your answers to a global community of like-minded players.  The others had been at it for a few minutes; I had to take time to install the exercises and begin.  The basic problem, coded in JavaScript, was “Here are seventeen sentences, each marked as being one of four kinds of conversation: statement, question, argument, or silence.  Write a function that successfully matches the expectations of the test case.”

While the others struggled with both regular expressions and multiple if / then / else if ... trees, I went straight for a different solution:

    this.hey = function(input) {
        var tests = [[function(i) { return /^\s*$/.test(i); },                                      "Fine. Be that way!"], 
                     [function(i) { return /[A-Z][A-Z]+.*!$/.test(i) || /^[A-Z\s]+\??$/.test(i); }, "Woah, chill out!"],
                     [function(i) { return /\?$/.test(i);},                                         "Sure."]];
        for(var i = 0, l = tests.length; i < l; i++) {
            if (tests[i][0](input)) return tests[i][1]; };
        return "Whatever."; };

Seven lines. (Yes, my Javascript coding conventions look like Python and Coffee. Whitespace is one of my secret weapons.) Where everyone else had 20-plus lines of comparisons, I went with the brute-force solution. It took four iterations to shake out all the bugs, all 17 tests passed, and it was done.

There are plenty of places where this is silly code; it's basically arbitrary string reduction, and is completely dependent upon the order of the tests, but as the last result says, "Whatever." To me, however, this reduces a lot of if / then statements down to a single if expression in a hand-written map/reduce: map all possible regexes with a single string, returning [String | Nothing], then reduce down to a single memo of the String. But really, this comes down to a basic rule: wherever you have a list of things to do, see if your language has a proper List with which to contain them.

16Jan

Airplane Mode from the Ubuntu Terminal

Posted by Elf Sternberg as Uncategorized

I ride a bus to and from the office every day. While I love Ubuntu, getting a constant barrage of “WiFi Available” popups whenever we pass by a coffee shop is annoying as hell. Digging into the network manager every day is a chore. Solution: A simple command line handler with a memorable name:

#!/bin/bash
MODE='off'
if [[ -n "$1" ]]; then
    MODE='on'
fi
nmcli nm wifi $MODE

Typing airplane will turn off your wi-fi stack; typing airplane off will turn it back on. (Actually, typing airplane four score and seven years ago will turn it back on. Any argument is accepted as turning airplane mode off. Even airplane on.)

07Jan

git wordcount

Posted by Elf Sternberg as Uncategorized

This is an early draft, but I figured you might enjoy taking a look at my latest little toy:

#!/bin/bash

ARG=""

if [ "${1:0:2}" == '-h' ]; then
    HEAD="${1:1}"
    HEAD=${HEAD//h/^}
    ARG=" HEAD$HEAD..HEAD "
fi

REM=`git diff -U $ARG | grep '^-' | sed 's/^-//' | wc -w`
ADD=`git diff -U $ARG | grep '^+' | sed 's/^+//' | wc -w`
DIF=`expr $ADD - $REM`
echo "Word count: $DIF"

As I’m sure I’ve mentioned before, I write award-winning science fiction as well as hack code, and I use emacs for both activities. I’ve always written primarily in what I think of as Usenet mode, with asterisks and underscores for emphasis and bolding, respectively, a mode that has become codified as Markdown. I used to have my own homegrown toolkit for that, but these days I just use the Python version of Markdown instead. I’ve always used a VCS for that, living entirely in GIT almost since the beginning.

I wanted a simple script to tell me my daily progress. This is it. It compares what’s on the filesystem right now to the latest check-in, or, if you pass it the -h option, it will compare the last check-in to the previous check-in. Each additional -h will push the oldest comparison back one commit, so -hhh will compare the last check-in to the one three check-ins ago.

After writing this, I learned about git-sh-setup and --word-diff=porcelain, both of which will find their way into a future version; hopefully soon you’ll be able to say ‘git wc HEAD^^^..HEAD^^’ or similar, and see how productive you were any two days in the past.

Recent Comments