Saturday, January 26, 2013

When is a programming feature 'helpful'?

There was a kerfuffle in my twitter feed this morning which highlighted some issues about language design. Don Stewart retweeted this conversation about funny JavaScript behavior. Reproduced in a JavaScript  console, it looks like this:
    > ['10','10','10','10','10'].map(parseInt)
    [10, NaN, 2, 3, 4]
It looks completely mysterious to a Haskeller, and by "mysterious" I mean "punishable by life in prison." I'd bet there's many a functional head listing on a sprained neck out there in Haskell-land,and that co-functional parents are explaining to their children what adult words are. I set about looking for an explanation, because I could not imagine how map could behave this way. I got the exact same answer in my browser, which made it unlikely that it was due to uninitialized memory. So first I tried using the identity function:
    > ['10','10','10','10','10'].map(function(x) {x;})
    [undefined, undefined, undefined, undefined, undefined]
Oh, right, JavaScript requires a return:
    > ['10','10','10','10','10'].map(function(x) {return x;})
    ["10", "10", "10", "10", "10"]
Great! Sensible behavior. Next, I looked up parseInt and found that it takes two arguments: parseInt(string, radix). JavaScript code is full of helpful functions that take variable numbers of arguments and guess what you mean based on their type, so this seemed a likely candidate for trouble.
    > ['10','10','10','10','10'].map(function(x) {return parseInt(x,10);} );
    [10, 10, 10, 10, 10]
Much better. Presumably then, the broken version supplies a second argument to parseInt that it pulled out of its ass. The tail of ..2,3,4] made me suspect there was some accumulation happening, so I tried this:
    > [1,2,3,4,5].map(function(x,y) {return (x+y);} )
    [1, 3, 5, 7, 9]
Prison's too good for them, I thought. Apparently this sums adjacent elements, except...wait, there would have to be a zero prepended for that to work. I observed that addition being commutative, it was projecting out valuable information, so I tried something a little more revealing:
    > [1,2,3,4,5].map(function(x,y) {return ('('+x+','+y+')');} )
    ["(1,0)", "(2,1)", "(3,2)", "(4,3)", "(5,4)"]
Still looks like adjacent sums with a magical zero. At this point I had some coffee and realized I was confused about the types. I had two lists of small integers, one from a mysterious source, not one list being zipped with itself.
    > [1,2,3,4,5].map(function(x) {return x*10;}).map(function(x,y) {return ('('+x+','+y+')');} )
    ["(10,0)", "(20,1)", "(30,2)", "(40,3)", "(50,4)"]
Okay. The second argument is clearly not a function of the array contents and now looks like the array index of the current argument. Going back to the original problem:
    > parseInt('10',0)
    10
    > parseInt('10',1)
    NaN
    > parseInt('10',2)
    2
Great, this sort of makes sense now. A radix of zero is assumed to be 10, a very C-like idiom, and the second example fails correctly because '10' is not a unary number. So, parseInt is not the villain, nor really is map. It is perfectly reasonable to have a variant for arrays that would operate on an element and its index. For me, the villain here is the handling of optional arguments. The map function is overloaded silently and that's the kind of help I don't want.

'Helpful' means different things to different people. To a Haskeller, the definition is something like: publish your promises and keep them. Explicit type signatures, referential transparency, static type-checking are all there to make those promises come true. In JavaScript, optional arguments are considered helpful, because they let you reuse the same function name in slightly different contexts.

Steadily, monotonically, over the years, I have moved away from convenience in the specification of a problem whenever it introduces uncertainty. When I first learned C, I memorized the precedence of operators so that I could write expressions with a minimum number of parentheses. Then I learned other things and forgot the precedences and just started parenthesizing everything until it was unambiguous. A friend in graduate school used Modula-3, which had no automatic casting between numeric types. C programmers were horrified, but I felt it was a breath of fresh air.

These conveniences should be part of interactive programming environments, not programming language specifications. Fuzziness, forgetfulness, disorder are part of the human condition and should be acknowledged, but they should not make it into the code. Say what you mean to the programming environment, by all means, but have the resulting program be completely unambiguous.

Which reminds me, the JavaScript map actually accepts a function of three arguments...

Saturday, January 12, 2013

Falafel Waffles

I love falafel.  Used to eat it all the time in NY.  But it's not so common in San Diego.  I'd make it myself, but I hate deep frying. But Mark Bittman's love of freshly-cooked chickpeas (the the resulting broth, which he dubs 'gold') set me to cooking them myself.  Which made me want falafel.

So I thought about alternatives to deep-frying. The essence of deep-frying, I reasoned, is total immersion in high heat, where high means more than boiling, hot enough to brown.

Enter the waffle iron, a device that forms batter into a shape with a lot of surface area exposed to high heat.  Clearly, this is genius on two scales, I thought: it will work beautifully; it rhymes.

Trademark time!  Google search!  First entry: No, you aren't the first person to think of falafel waffles.  D'oh! Still, there aren't many falafel waffle recipes out there, and many included wheat flour, but I wanted both wheat- and gluten-free, so I set out on my own expedition.

I started with chickpea flour (Bob's Red Mill).  For seasoning I used garlic, scallion and some fresh herbs (various mixtures of dill, cilantro, mint and basil.)  The taste was great, but I had used very little water, just enough to bring the mixture to a paste, and that did not work so well in a waffle iron.  If I left it long enough to brown, my falafel turned into a jawbreaker.

So gradually I increased the liquids, water and some olive oil, until I got to a pour-able batter, just like flour waffles.  On the advice of waffle experts, I added baking powder, and where others added a little wheat flour for binding, I added arrowroot.  I haven't done controlled experiments on the effects of the arrowroot as a binding agent, so don't panic if you don't have any.


In addition to the baking powder, I borrowed the traditional wet/dry method from baking: two bowls, one for wet ingredients, one for dry, mix each thoroughly, then combine.  The wet and dry mixtures should be roughly equal in volume.  This recipe uses 1/2 Cup of each, resulting in about two waffles in a medium-sized circular waffle iron.



The result is spicy, savory waffles, light and crunchy, with a uniform texture.  They are different from traditional falafel, but close enough for my purposes.  I eat them as a standalone snack, or with chopped  cabbage (usually red) dressed with a sauce made of lemon and tahini (sesame paste).

To restore some of the feel of traditional falafel, I often add small quantities of mashed chickpeas, cooked brown rice, or both.  Both of these reduce the tensile strength of the waffle, but add a nice crumbly texture and nuttiness.


I put the wet ingredients in a measuring cup and puree with an immersion blender:

  • 2 scallions or 2 cloves of garlic, or mixture
  • a few sprigs of herbs
  • pinch of salt
  • a tablespoon or two of olive oil
  • enough water to bring the level to 1/2 Cup.
  • Optional: a tablespoon or two of chopped/mashed chickpeas and/or brown rice (cooked)
Dry:

  • 1/2 Cup chickpea flour
  • pinches of salt, pepper, cayenne, cumin
  • 1 teaspoon of arrowroot
  • 1 teaspoon of baking powder
The resulting batter should pour easily. If not, add more water and mix until it does.  Bean flour takes some time to re-hydrate.  I let the batter rest for an hour; anything less than that seems to leave a hint of raw bean taste.  This recipe makes about two waffles in a circular waffle iron.



Monday, May 21, 2012

GHC Talks to the Scarecrow

I have a strange sense of humor. It's essential that you know that before reading on.  'Cause really, this is a pretty dry little post about an obscure error in a Haskell program.

Today, I got a message from GHC that always amuses me. Why?  Because the message makes feel like GHC is poor little Dorothy, stuck at the fork in the road, confused by the Scarecrow's advice that some people go both ways.

Saturday, October 1, 2011

@ConalElliott re: What resources & practices (teaching Haskell)

Conal Elliott asks: What resources & practices would you recommend for helping a group of Java & C++ programmers learn to work in Haskell? I saw the question first on Twitter, but my compression skills were not up to the task of answering there.

I have two recommendations: teach them the simplest definitions of the fundamentals; read programs with them, out loud, like children's books, skipping nothing.

These steps are often passed hurriedly, by teachers so steeped in a subject they don't realize how different their worlds are, by students eager to build things as quickly in a new programming language as they do in the old. Most likely, the students will assume that they just need to learn new syntax to be off and running. Indeed, I am sure they are capable, self-motivated software professionals and that they would succeed in learning Haskell, just without the deeper understanding that can make it such a joy.

As a test, ask your students to write down the definition of 'type'. I'll bet that their answers will be longer than 'set' and include some mention of bytes, words and big-endian. We were all trained to be mechanics instead of drivers, because all we had were go-carts.

C and its children will have left clutter in their minds. Types will be obstacles, efficiency a drug, correctness assured by machismo. They will gloss over code without really reading it, trying to fit it into an existing model. Saying things out loud helps slow things down, forces them to make connections between symbols, words and definitions so fundamental they are rarely written down. (I backtracked through Paul Hudak's book for hours trying to find out how to say '::'; newcomers ask pretty regularly in #haskell.)

Make sure they know all the places that patterns can appear in code and how they work. Make sure they know that data constructors are functions. Have them verify that with :type in GHCi. And that type constructors are functions, verifying with :kind. They should be familiar with the syntax and semantics of higher-order types before they meet IO, so that they understand it is not magic.

Lists, recursion, functional algorithms, none of these will be challenges, but constructs they have not seen before will give them much more trouble if they cannot deconstruct them to primitives. I cannot imagine Conal omitting such fundamental semantics from his lessons, but I can easily imagine both teacher and students, excited to build new stuff, skimping on the exercises necessary to flush out the old foundations and cement the new.

Sunday, September 11, 2011

Two Degrees from 9/11

On the morning of September 11, 2001, I didn’t board a bus to New York City, and in not doing so, buffered myself from a world of hurt.

I wasn’t clever or prescient, just lucky. I was at the bus stop, I’d seen the bus and flagged it down. But just then, two women jogging by stopped and asked if I had heard what happened. I wanted to keep eye contact with the driver, but the anxiety in their voices made me turn and listen. I glanced back to the driver as he slowed, saw him looking for confirmation, but I didn’t signal him again and he drove on.

I went back home, turned on the TV and soon saw the first tower fall. Had those ladies not happened by, I might well have seen that tower fall from the bus instead.

One friend was not so lucky. He was at jury duty that morning, just ten blocks away from the towers. He saw both towers fall as he walked back to his office in SoHo, breathing the first fumes of the fires, crying uncontrollably at the insanity. Days later, he discovered that a good friend and colleague had been high up in a tower, unable to evacuate. That same victim was actually the boss of another friend of mine, who then had to step up and fill that position.

Another friend was a SCUBA instructor who had trained dozens of NY firefighters to dive. Over a dozen of his students died in the collapse.

Earlier that year, I had attended the wedding of a diver I knew, held at Windows on the World, the restaurant at the top of the north tower. He and his new bride were devastated that so many of the restaurant staff they had worked so closely with to plan the event were lost.

It seemed that everyone lost someone, except for me. My connections were always indirect, like some tragic form of the Kevin Bacon game where I was always degree two. I do not know how much that separation lessened the blow, but it was significant.

On Friday, I went to the NJ side of the Hudson River near the Lincoln Tunnel to see the damage for myself. Though still several miles away, the plume of smoke was trivial to spot, and the lack of that distinctive skyline was like a splinter in my mind.

Still, I did not find myself overwhelmed by anger and grief, like those more closely linked. I looked at New York City, not the way you look when you’ve grown up in a place, but with the eyes of a tourist. The New York metropolitan area is huge. Standing on the palisades, I could see a dense urban landscape, seemingly extending to infinity in all directions.

Suddenly, all I could think of was how little physical damage the attacks had done. Perspective has that effect. The WTC towers were so huge, their presence swamped the mind, especially as one got closer. Just standing at the base and looking up, it felt like they were arching over you instead of going straight up. Their destruction was equally overwhelming. But from where I stood on the palisades, with two degrees of separation in my pocket, I could focus on the thousands of huge buildings, housing millions of people for miles around and see that they were all fine.

I’m not saying the impact wasn’t real, even for someone of degree two. Business dried up and I took advantage of a job offer in San Diego, where I’ve been ever since. All my friends mentioned above stayed and went through some hard times. I sometimes wonder, if I had caught that bus, would I have been degree one, and what might have been different as a result.

Sunday, December 28, 2008

Fish in Monk's Clothing

"Merry Christmas! Could you come to the kitchen and help with the fish?" It wasn't exactly unexpected, but it was still a nice little challenge, like Top Chef the home edition. Unfamiliar kitchen, lots of competition for space, no specific instructions and the opportunity to make or break someone's Christmas dinner. On such occasions, it's good to have a little bit of wisdom from Julia Child in your back pocket.

My travel plans to Illinois had been delayed for two days by snow and ice, so I ended up flying on Christmas day, getting to my brother's house about an hour and a half before guests were showing up for dinner. Everything on the menu was traditional in that household except for the fish, which my brother doesn't eat, so I got volunteered to cook that.

Again, I did have some warning. Several days before, my brother had mentioned on the phone that some guests had asked for fish instead of tenderloin, and that he had bought some frozen tilapia filets. All he needed was a recipe. I said that for simple white fish, I used to like the method that Julia Child had gotten from some monks in the south of France, an episode entitled ``Fish in Monk's Clothing,'' where the fish is baked covered in lots of aromatic vegetables. "Search on the internet, I'm sure you'll find a recipe," I said.

Well, he had searched and had found four recipes for tilapia, but none were Julia's. I didn't like the look of them and although I hadn't made this dish in years, I preferred to find my own way again. Fortunately, the dish turned out well, but the down side was that I had no recipe to give to the people who asked for one, only a bunch of vague constraints.

The essential thing is that the mix of vegetables should taste good, but be reasonably mild so as not to overpower the fish, and should still have a lot of moisture in it. You need enough to cover the fish reasonably well. The vegetables drip flavor into the fish while at the same time protecting it from losing moisture in the direct heat of the oven.

(For the Haskell readers, think of the following as a sort of QuickCheck test suite for the actual method.)
  1. Chop and saute a bunch of aromatic vegetables, season with salt and pepper, thyme or other mild herbs, and reduce with some white wine. The vegetables should be soft, but still very moist and it's good to have some liquid remaining.
  2. TASTE the vegetables. If they don't taste good, fiddle with the seasoning until they do.
  3. Season the filets with salt and pepper, then place on an oiled baking pan. If the tails of the filets are much thinner than the main part of the body, overlap them so that the fish is roughly the same thickness all over.
  4. Layer the vegetables over the fish along with any remaining liquid.
  5. Bake gently until the fish is just opaque and flakes easily. I think we did 350F for about 15 minutes, but this will vary with the amount and size. You could also microwave it for 5-10 minutes, covered, if you use a glass or porcelain casserole dish.
As for which vegetables, that varies based on what I have on hand, but start by sweating an onion in olive oil and butter, then adding celery and a few chopped cloves of garlic. It is fine to brown the onion a bit, but not the garlic. This time we added some chopped mushrooms and parsley. I have added carrots in the past, but that's about as strong as I would go. Julia Child used a head of iceberg lettuce chopped up, but I've never had the nerve to try that. Spinach or swiss chard make a nice choice, but kale, mustard greens and cabbage are too strong. Fennel is a nice addition if you like anise flavor.

Any simple white fish works here, such as flounder, sole, tilapia, catfish, etc. I think I've done it successfully with bluefish, but I would avoid salmon.

How close this is to what Julia Child did, I really don't remember, because I only saw the show once long ago, but I'm pretty sure I've got the essence right. It's a simple way to marry fish with whatever vegetables are available. In any case, it was good enough for Christmas dinner.

Sunday, December 14, 2008

The Pattern of Walking

These images may look like Christmas lights, but they were created simply by holding a camera on long exposure while walking on the beach at dusk. If you just wave the camera around, the patterns are uninteresting, because they have little cohesion, but just walking straight created unexpectedly interesting results.

They remind me of high school physics, when we learned about cycloids by attaching a light to a bicycle tire. The walking images are more regular than I expected, but also far more convoluted than a cycloid. Just as the hidden motion of the tire was revealed in that experiment, these images must show something fundamental about walking. I'm just not sure what.