« | » Main « | »

A popular pastime among programmers is to make fun of programming languages, or at least the one you choose not to use. For example, Gary Bernhardt's 5-minute talk WAT is all about unexpected behavior, mostly in Javascript.

Today brought another example of surprising Javascript behavior:

> ['10', '10', '10', '10', '10'].map(parseInt)
[ 10, NaN, 2, 3, 4 ]

I looked at this and thought, like most others, "WAT??" I wanted to understand how Javascript produced this result, so I read up on Javascript's map() function. Once I read the docs, it was clear what was going on.

In most programming languages, the map function accepts a function of one argument, invokes the function for all of the values in an array, and produces the array of results. Javascript doesn't work quite that way.

In Javascript, the map function accepts a function of three arguments. For each element in the array, the function is passed the element, the index of the element, and the entire array. So this map function makes these function calls:

parseInt('10', 0, ['10', '10', '10', '10', '10'])
parseInt('10', 1, ['10', '10', '10', '10', '10'])
parseInt('10', 2, ['10', '10', '10', '10', '10'])
parseInt('10', 3, ['10', '10', '10', '10', '10'])
parseInt('10', 4, ['10', '10', '10', '10', '10'])

The second argument to parseInt is the base to use when converting the string to an integer. A value of 0 means, "do the right thing," so the first result is 10. A base of 1 makes no sense, so the second result is NaN. And 2, 3, and 4 produce 2, 3, and 4. Javascript silently ignores extra arguments, so the array passed as the third argument has no effect.

So is Javascript's map wrong? It behaves differently than the map found in lots of other languages like Python, Ruby, Lisp, Perl, Haskell, and so on. But it isn't wrong.

Working in more than one language, it's frustrating dealing with their differences. New Python learners chafe at the fact that Python names work differently than C variables. They want to know if function arguments are call by value or call by reference (neither). I saw a person on IRC once who was upset that Python lists were called lists instead of arrays.

Languages are different, that's why we have more than one of them. Language designers have to strike a balance between familiarity and innovation. We'd be pretty suprised by a language that used something other than "+" for adding numbers together, for example. Eventually though, the new language will diverge from the old, or what was the point?

Javascript's map function feels a little clunky: it's focused on integer-based iteration rather than on pure functional construction. But it can do things the other maps can't easily, like create an array of differences between elements, or a running total, and so on. Other languages have other solutions to those problems.

This isn't to say that all languages are equal, there are better and worse, of course. But too often I hear people ranting about a language being stupid for some decision, without bothering to find out why it was done that way, and what benefit they might get from it.

To which, I say: WAT!?

I started a new side project this month, called Byterun. It's a pure-Python implementation of Python bytecode execution. That is, it runs Python bytecode.

I did it because Coverage.py has some bugs in its branch coverage. It analyzes your program for potential branches by reading the bytecode rather than the source, and I think some of the bugs are due to subtle misunderstandings on my part about how Python bytecode works.

So I figured if I could have a working implementation of bytecode in Python, it would be a good way to be sure I understood how they work. I found a ten-year-old implementation called pyvm2.py by Paul Swartz, and started refurbishing it, fixing bugs, adding missing opcodes, and bringing it up to 2.7 and 3.3.

It isn't finished yet, and supporting Python 2 and Python 3 might be a bit difficult, but already it has helped me understand a few things better, such as how generators are suspended and resumed: when the generator function yields, instead of discarding the stack frame as a normal function's return does, the frame object is held by the generator object, so the next time a value is needed, the frame can be resumed just where it was.

Closure cells make more sense now too, and I know how to create them by hand. I was creating Python function objects with types.FunctionType. But one of its arguments is "closure", which must be a tuple of cell objects. The types module has ways of making functions, generators, classes, and so on, but has no way to make a cell.

Turns out you can do it by creating a closure and grabbing its cells:

def make_cell(value):
    # Construct an actual cell object by creating a closure right here,
    # and grabbing the cell object out of the function we create.

    return (lambda x: lambda: x)(value).func_closure[0]

A fine bit of Python haiku there...

tagged: » 5 reactions

Coverage.py version 3.6 is ready. It has a few new features since 3.5.3:

  • A new --fail-under switch on the reporting commands makes the command exit with a failure status code if the total coverage is below a given threshhold. This is great for build servers to raise an alarm if coverage falls too low.
  • The reporting functions now return a float indicating the total coverage.
  • The title of the HTML report can be set with a switch or a configuration option.
  • Configuration files now expand environment variables for more power and flexibility when configuring coverage.py

There have also been more than 20 bugs fixed, take a look at the change history for details.

I heartily recommend that everyone upgrade to coverage.py 3.6 right away. Happy New Year!

tagged: , » react

In the decade I've been running this blog, I've accumulated 30 or so draft posts. I'm sick of looking at them, so I'm getting rid of them. Here are a few links in those drafts that still seemed interesting:

  • Radical Cartography, a collection of map experiments.
  • Rebound Designs, handmade device cases made from old books.
  • St Atmos, a voluptuously fat font. Not so legible, but very sensuous.
  • JONGL, a crazy juggling simulator. Where else will you see a giraffe bounce-juggling flower pots?

Wrapping up vacation time today, getting ready for the year ahead. The food project over the last few days, mostly captained by Max and his girlfriend Marie, was a gingerbread "house". It's actually Castle Black from Game of Thrones, a major interest in the family these days.

Castle Black is a rough castle built at the foot of a massive wall meant to keep the primitives to the north from coming south. It's situated in a harsh icy environment:

Castle Black gingerbread

This is a pretty involved construction, we quintupled the gingerbread recipe. And there are plenty of details, like the chocolate elevator running on graham cracker rails, and a pretzel stick portcullis. The castle is manned by gummy bears with chocolate chip hats to keep them warm.

Castle Black gingerbread

There was a phase after it was built where it had only minimal icing and no candy, for a more realistic bleak look, but everyone knows the eating phase is better with icing and candy, so it was soon festooned as you see here.

The key transition was when Max was applying M+M's to the roof, and I asked what they were meant to represent, and he smirked and said, "it's candy on gingerbread!"

tagged: » react

« | » Main « | »