Saturday, August 6, 2016

A Close Reading of Heinlein's The Menace from Earth (part 9)

this essay begins in part 1

Who loves ya, baby?

Holly wakes in a hospital room with casts on both arms. There is pleasant banter with a nurse and then a doctor. She asks after Ariel.

"She's right here," Ariel agreed from the door. "May I come in?"

Ariel is on crutches. Actually, he doesn't say crutches, he only says she "hopped" into the room. But it turns out she has cracked ribs. If you have cracked ribs, you don't "hop", or if you do, you scream.

"You hurt your foot."

She shrugged. "Nothing. A sprain and a torn ligament. Two cracked ribs. But I would have been dead. You know why I'm not?"

I didn't answer. She touched one of my casts. "That's why. You broke my fall and I landed on top of you. You saved my life and I broke your arms."

… I didn't have an answer so I said, "Where's Jeff? Is he all right?"

"He'll be along soon. Jeff's not hurt . . . though I'm surprised he didn't break both ankles. He stalled in beside us so hard he should have. But Holly . . . Holly my very dear . . . I slipped in so that you and I could talk about him before he got here."

Holly diverts the conversation; it seems Ariel is going back to earth soon. Ariel returns to the topic on her mind.

" … But Holly . . . listen please and don't get angry. Its about Jeff. He hasn't treated you very well the last few days … But don't be angry with him. I'm leaving and everything will be the same."

Holly gets on her high horse and explains how she's a career woman and doesn't need romance. In quite a lengthy give-and-take (longer than necessary, I think) Ariel tries to straighten out her prideful confusion, finally ending with

… "How old am I?"

I managed not to boggle. "Huh? Older than Jeff thinks you are. Twenty-one at least. Maybe twenty-two."

She sighed. "Holly, I'm old enough to be your mother."

Holly doesn't believe it. (Quickly doing arithmetic: Holly's fifteen, Ariel could be in her thirties? Yeah, a 33-year-old actress in good shape; it checks out.)

"But that's why, though Jeff is a dear, there never was a chance that I could fall in love with him. But … the important thing is that he loves you."

"What? That's the silliest thing you've said yet! Oh, he likes me -- or did. But that's all." …

"Wait, Holly. I saw something you didn't because you were knocked cold. When you and I bumped, do you know what happened?"

"Uh, no."

"Jeff arrived like an avenging angel a split second behind us. He was ripping his wings off as he hit, getting his arms free. He didn't even look at me. He just stepped across me and picked you up and cradled you in his arms, all the while bawling his eyes out."

"He did?"

Here there is more self-conscious mulling by Holly, but she still refuses to break out of her "just partners" mindset. Then Jeff comes in.

He stopped in the door and looked at us, frowning.

"Hi, Ariel."

"Hi, Jeff."

"Hi, Fraction." He looked me over. "My, but you're a mess."

"You aren't very pretty yourself. I hear you have flat feet."

"Permanently. How do you brush your teeth with those things on your arms?"

"I don't."

Ariel leaves and Jeff says "Hold still" and kisses her.

… I was startled speechless because Jeff never kisses me, except birthday kisses, which don't count. But I tried to kiss back to show that I appreciated it.

… "Runt," he said mournfully, "you sure give me a lot of grief."

"You're no bargain yourself, flathead," I answered with dignity.

"I suppose not." He looked me over sadly. "What are you crying for?"

I didn't know that I had been. Then I remembered why. "Oh, Jeff -- I busted my pretty wings!"

"We'll get you some more. Uh, brace yourself. I'm going to do it again."

"All right." He did.

I supposed Hardesty & Hardesty has more rhythm than Jones & Hardesty.

It really sounds better.

OK, I'm just an old softy and I still get sniffly from "He just stepped across me and picked you up and cradled you in his arms", right on to the end. It's so sweet!

That said, there is a lot that could be better in this final scene. It's too long and talky. More seriously, Ariel and Jeff seem to have come out of the disaster with some new, deeper self-knowledge and changed attitudes. Holly has not. Right to the end she seems stuck in the obsession with Jeff as a partner in their design firm, unable to recognize a new, closer relation. You could say the last two lines show that change of attitude, but I'd like something more explicit -- something to show clearly she's stepped out of being a nerdy kid.

And in conclusion,

It was only on this careful re-reading that I realized that the argument where Ariel wants to try real wings and Jeff butts into the discussion is the pivot of the story. The cross-current of mistakes and different kinds of hurt pride (Jeff's, Holly's and also Ariel's) creates the final situation. All of Jeff's infatuation and Holly's hurt and jealousy feed into it, along with Ariel's basic selfishness. Out the other side comes a sequence of bad decisions that almost ends in tragedy but instead ends by crystallizing everyone's feelings. Despite my many nit-pickings, it's a finely-structured story.

They don't make science fiction feature movies without explosions in them, so this will never be a movie (or if it is, it will be unrecognizable). But if the SyFy channel wanted to make a nice special, this would do beautifully. Look at at the practicality: it has a small cast; the sets are all interiors; there's not a lot of special effects. The flying could all be done on wires in front of green-screens. And it's really a good story.

A Close Reading of Heinlein's The Menace from Earth (part 8)

this essay begins in part 1

Older Woman Wisdom?

They fuss about looking over second-hand wing sets and find one that fits.

While I was helping her into the tail surfaces I said, "Ariel? This is still a bad idea."

"I know. But we can't let men think they own us."

"I suppose not."

"They do own us, of course. But we shouldn't let them know it."

I really don't want to bash RAH for sexism even more. But putting "They do own us, of course" in the mouth of his "wise older woman" character... really? It's a kind of pop-folk pseudo-wisdom that you might find in an Ozzie and Harriet script. It was somewhat true in Victorian England, but by the 1950's women had the vote, could own property, had created "Rosie the Riveter" as a national meme. And it certainly did not have to be the assumptive truth of a future Lunar society. But Heinlein, apparently, assumed it was basic to human nature.

Disaster

Anyway, Holly completes fitting Ariel out with her second-hand wings.

"All right. Wups! I goofed. They aren't orange."

"Does it matter?"

"It sure does."

With the wings painted beginner-orange, Ariel tries them out on the beginner slope. After some time she begins to eye the central updraft, the Baby's ladder. Holly's not sure, and cautions her at length. They start circling up.

"Not tired?"

"Heavens, no! Girl, I'm living!" She giggled. "And mama said I'd never be an angel!"

I didn't answer because red-and-silver wings came charging at me, braked suddenly and settled into the circle between me and Ariel. Jeff's face was almost as red as his wings. "What the devil do you think you are doing?"

"Orange wings!" I yelled. "Keep clear!"

"Get down out of here! Both of you!"

… "Jeff Hardesty," I said savagely, "I give you three seconds to get out from between us -- then I'm going to report you for violation of Rule One. For the third time -- Orange Wings!"

Jeff moves off although stays near; the women continue the slow circling climb, Holly fretting that Ariel might be getting tired.

… "Ariel? Tired now?"

"No."

"Well, I am. Could we go down, please?"

She didn't argue, she just said "All right. What am I to do?"

"Lean right and get out of the circle." I intended to have her move out five or six hundred feet, get into the return down draft, and circle the cave down instead of up. I glanced up, looking for Jeff. … I glanced back at Ariel.

I couldn't find her.

Then I saw her, a hundred feet below -- flailing her wings and falling out of control.

… I was simply filled with horror. I seemed to hang there frozen for an hour while I watched her.

But the fact appears to be that I screamed "Jeff!" and broke into a stoop.

But I didn't seem to fall, coudn't overtake her. I spilled my wings completely -- but couldn't manage to fall; she was as far away as ever. … I could feel rushing air -- but I still didn't seem to close on her. … This nightmare dragged on for hours.

Actually we didn't have room to fall for more than twenty seconds; that's all it takes to stoop a thousand feet. But twenty seconds can be horribly long . . . long enough to regret every foolish thing I had ever done or said, long enough to say a prayer for both of us . . . and to say good-by to Jeff in my heart.

… and I was overtaking her . . . I was passing her -- I was under her!

Then I was braking with everything I had, almost pulling my wings off. I grabbed air, held it, and started to beat without ever going to level flight. I beat once, twice, three times . . . and hit her from below, jarring us both.

Then the floor hit us.


Wow.

continue in part 9

Friday, August 5, 2016

A Close Reading of Heinlein's The Menace from Earth (part 7)

this essay begins in part 1

Teaching Ariel to fly

So I taught Ariel Brentwood to "fly." Look, those so-called wings they let tourists wear have fifty square feet of lift surface, no controls except a warp in the primaries, a built-in dihedral to make them stable as a table … The tail is rigid, and canted so that if you stall (almost impossible) you land on your feet. …

I put myself to the humiliation of strapping on a set of the silly things and had Ariel watch while I swung into the Baby's Ladder and let it carry me up a hundred feet to show her that you really and truly could "fly" with them. Then I thankfully got rid of them, strapped her into a larger set, and put on my beautiful Storer-Gulls. I had chased Jeff away … but when he saw her wing up, he swooped down and landed by us.

I looked up. "You again."

"Hello, Ariel. Hi, Blip. Say, you've got her shoulder straps too tight."

"Tut, tut," I said. "One coach at a time, remember? … get above two hundred feet and stay there, we don't need any dining-lounge pilots."

Jeff pouted like a brat but Ariel backed me up. "Do what teacher says, Jeff, that's a good boy."

With Jeff circling above, Holly gets on with the teaching.

I admit Ariel was a good pupil … I found myself almost liking her as long as I kept my mind firmly on teaching. She tried hard and learned fast … she admitted diffidently that she had had ballet training.

Overdramatic metaphor

About mid-afternoon she said, "Could I possibly try real wings?"

Holly tries to talk her out of it.

"… You might get hurt, even killed."

"Would you be held responsible?"

"No, you signed a release when you came in."

"Then I'd like to try it."

… to let her do something too dangerous while she was my pupil . . . well, it smacked of David and Uriah.

It seems Holly knows her Bible; but my goodness that is a dramatic reference. The story is in 2 Samuel 11: David, king of Israel, has a thing going with Bathsheba, the wife of one of his officers, Uriah. When Uriah's presence becomes inconvenient, David sends him to war, carrying a dispatch to to the general saying, "Set Uriah in the forefront of the hottest battle, and retreat from him, that he may be struck down and die."

On reflection, I think it is not over-dramatic. It underscores Holly's serious concern that she could be putting Ariel in real danger, and that doing so might be mis-perceived as malice. And it adds weight to what is about to happen.

"Ariel, I can't stop you . . . but I should put my wings away and not have anything to do with it."

"If you feel that way, I can't ask you to coach me. … Perhaps Jeff will help me."

"He probably will," I blurted out, "if he is as big a fool as I think he is!"

The critical moment

What follows is the emotional crux of the story, where a wrong path is taken owing to a complex mix of misunderstandings, anger, and pride. Everything in the story leads up to this; and the conclusion inevitably follows from it.

Her company face slipped but she didn't say anything because just then Jeff stalled beside us. "What's the discussion?"

We both tried to tell him and confused him for he got the idea I had suggested it, and started bawling me out. Was I crazy? Was I trying to get Ariel hurt? Didn't I have any sense?

"Shut up!" I yelled, then added quietly but firmly, "Jefferson Hardesty, you wanted me to teach your girl friend, so I agreed. But don't butt in and don't think you can get away with talking to me like that. Now beat it! Take wing. Grab air!"

He swelled up and said slowly, "I absolutely forbid it."

Silence for five long counts. Then Ariel said quietly, "Come, Holly. Let's get me some wings."

"Right, Ariel."

continue in part 8

Thursday, August 4, 2016

A Close Reading of Heinlein's The Menace from Earth (part 6)

this essay begins in part 1

We finally go flying

We finally get to the signature image of this story, the one that has made it stick in so many people's memories for years: the vision of being able to fly like a bird. This activity is so different from normal experience that you might think it would require a lot of exposition to set up and justify. Nope. There was one prior indirect reference to Lunar gravity. Now he only adds,

Most of the stuff written about the Bats' Cave gives a wrong impression. It's the air storage tank for the city, just like all the colonies have … We just happen to be lucky enough to have one big enough to fly in. But it never was built, or anything like that; it's just a big volcanic bubble, two miles across, and if it had broken through, way back when, it would have been a crater.

There was once some theorizing that Lunar craters were the result of vulcanism. Now they are all assumed to be from impacts, but you know? For the sake of this story, we can believe there are volcanic "bubbles" under the surface. We don't know there aren't.

Now Holly nerds out over her wings. This is a lovely demonstration of a science fiction technique: if you let a character wax passionate about some feature you need to explain, you build the character and simultaneously get painless, even entertaining, exposition.

I left my shoes and skirt in the locker room and slipped my tail surfaces on my feet, then zipped into my wings and got someone to tighten the shoulder straps. My wings aren't ready-made condors; they are Storer-Gulls, custom-made for my weight distribution and dimensions. I've cost Daddy a pretty penny in wings, outgrowing them so often, but these latest I bought myself with guide fees.

They're lovely! -- titanalloy struts as light and strong as bird bones, tension-compensated wrist-pinion and shoulder joints, natural action in the alula slots, and automatic flap action in stalling. The wing skeleton is dressed in styrene feather-foils with individual quilling of scapulars and primaries. They almost fly themselves.

After that outburst you are ready to believe this is credible. You can check the words, as Heinlein no doubt did:, "alula: the group of three to six small, rather stiff feathers growing on the first digit, or thumb of a bird's wing." Scapulars are short feathers on the shoulder, and primaries are the big feathers along the edge of the wing.

Holly cycles through an air lock into the cave proper.

… I perked up and felt sorry for all groundhogs, tied down by six times proper weight, who never, never, never could fly.

Not even I could, on Earth. My wing loading is less than a pound per square foot, as wings and all I weigh less than twenty pounds. … I spread my wings, ran a few steps, warped for lift and grabbed air -- lifted my feet and was airborne.

Now follows six longish paragraphs of Holly describing flying and various techniques of flight: soaring, gliding, and "stooping like a hawk". She checks the sightseer's gallery for "Jeff and his groundhogess"; then meets a friend, Mary, in mid-air and they agree to perch to talk.

Mary has gossip: Jeff is teaching "that Earthside siren" to fly right now. She goads Holly maliciously, insisting that everybody knows she is "simply simmering with jealousy".

I watched her out of sight, then sneaked my left hand out of the hand slit and got at my hanky -- awkward when you are wearing wings but the floodlights had made my eyes water. … Then I reminded myself that I had been planning to be a spaceship designer like Daddy long before Jeff and I teamed up. I wasn't dependent on anyone; I could stand alone, like Joan of Arc, or Lise Meitner.

It is really interesting that Heinlein chose the name of Lise Meitner as hero for Holly. Meitner, co-discoverer with Otto Hahn of nuclear fission, fled Nazi Germany and spent the war years in Sweden. She lectured in the U.S. in the 1950s and received many official honors around that time, but is little-known today.

An awkward conversation

Jeff flies up.

He landed by me but didn't sidle up. "Hi, Decimal Point."

"Hi, Zero. Uh, stolen much lately?"

"Just the City Bank but they made me put it back." He frowned and added, "Holly, are you mad at me?"

… "Of course not. Why should I be?

… "Uh, that's fine. Look, Test Sample, do me a favor. Help me out with a friend -- a client that is -- well, she's a friend, too. She wants to learn to use glide wings."

So, Jeff actually wants them to get to know each other. Holly would rather eat glass but,

… what I did say was, "OK, Jeff," then gathered the fox to my bosom and dropped off into a glide.

It took a while to chase down the reference to the fox in her bosom. Holly has apparently read Plutarch's Lives of the noble Grecians and Romans. Describing the upbringing of Spartan children, he says "So seriously did the Lacedaemonian children go about their stealing, that a youth, having stolen a young fox and hid it under his coat, suffered it to tear out his very bowels with its teeth and claws, and died upon the place, rather than let it be seen."

So the expression "fox in my bosom" means heroically letting a guilty secret eat your guts rather than admitting to it.

continue in part 7

Wednesday, August 3, 2016

A Close Reading of Heinlein's The Menace from Earth (part 5)

This essay begins in part 1

Jeff is smitten

Now things start to go south for Holly.

Jeff's eyes widened and I felt uneasy … I knew she was exceptionally decorative, but it was unthinkable that Jeff could be captivated by any groundhog, no matter how well designed. They don't speak our language!

I am not romantic about Jeff; we are simply partners. But anything that affects Jones & Hardesty affects me.

When we joined him at West Lock he almost stepped on his tongue in a disgusting display of adolescent rut. I was ashamed of him and, for the first time, apprehensive. Why are males so childish?

This all seems right to me. As long as the story is focused on people, it works. It's only the auxiliary hardware that causes problems.

Holly helps Miss Brentwood get into a pressure suit.

Those rental suits take careful adjusting or they will pinch you in tender places once out in vacuum . . . besides there are things about them that one girl ought to explain to another.

Jeff and "the platinum menace" go out without asking Holly to come along.

The days that followed were the longest in my life. I saw Jeff only once . . . on the slidebelt in Diana Boulevard, going the other way. She was with him.

She knows (by some unspecified means) that Jeff is cutting classes and taking Miss Brentwood to night clubs.

Starship Prometheus fading...

At last we get a good look at Holly's obsession, the starship that she and Jeff are designing in their spare time.

Jones & Hardesty had a tremendous backlog because we were designing Starship Prometheus. This project we had been slaving over for a year, flying not more than twice a week to devote time to it -- and that's a sacrifice.

Again with the "flying". Wonder what that means?

Of course you can't build a starship today, because of the power plant. But Daddy thinks that there will soon be a technological break-through and mass-conversion power plants will be built -- which means starships. …
Jones & Hardesty plans to be ready with a finished proposal while other designers are still floundering … We had been working every possible chance … checking each other's computations, fighting bitterly over details, and having a wonderful time. But the very day I introduced him to Ariel Brentwood, he failed to appear.

Days go by; no Jeff. Holly tries to come to terms with it.

I looked at the name plate of the sheet I was revising. "Jones & Hardesty" it read, like all the rest. I said to myself, "Holly Jones, quit bluffing; this may be The End. You knew someday Jeff would fall for somebody." … I erased "Jones & Hardesty and lettered "Jones & Company" and stared at it. Then I started to erase that, too -- but it smeared; I had dripped a tear on it. Which was ridiculous!

Awwww! This is such a great moment; I love it.

Too bad I have to nit-pick it. Dear millenial reader: once upon a time, engineers drew their designs by hand on big sheets of paper. Every official design sheet had a signature block, a box, usually in the lower left corner, with the name of the company, the date, revision number and so forth. This, like all the text on the sheet, was hand-lettered. So picture Holly bent over a table piled with many big sheets of paper, each a section of the design of the Prometheus, and each sheet with its neat name plate hand-lettered in the lower corner. That's what we're talking about here.

If we rewrote the story for this century, she'd be working on a table-top display screen maybe. But the falling tear wouldn't be as effective.

Holly's parents are concerned; she's been "moping" and not eating much, her father says. With their encouragement she decides to go flying. She mopes her way to the Bat Cave, fighting her own emotions.

… Jeff had been my partner and pal, and under my guidance he could have become a great spaceship designer, but our relationship was straightforward . . . a mutual respect for each other's abilities, with never any of that lovey-dovey stuff. …

No, I couldn't be jealous; I was simply worried sick because my partner had become involved with a groundhog.

continue in part 6

A Close Reading of Heinlein's The Menace from Earth (part 4)

This essay begins in part 1

Why no maps?

Holly sets off guiding Miss Brentwood.

… I … picked up her bags. Guides shouldn't carry bags and most tourists are delighted to experience the fact that their thirty-pound allowance weighs only five pounds. But I wanted to get her moving.

(Remember that remark about bag weights.) They've barely started when Miss Brentwood wants a city map. Holly curtly tells her there are none.

"Then why not print maps?"

"Because Luna City isn't flat ... like Earthside cities," I went on. "All you saw from space was the meteor shield. Underneath it spreads out and goes down for miles in a dozen pressure zones."

"Yes, I know, but why not a map for each level?"

… "I can show you the one city map. It's a stereo tank twenty feet high and even so all you can see clearly are big things like the Hall of the Mountain King and hydroponics farms and the Bat's Cave."

OK, there is a lot to say here. A "stereo tank" was a standard Heinlein prop used in several books. I suppose he guessed that to project a 3-D image you would need an enclosed volume. (The "Golden Era" of 3D movies was current when he wrote TMFE but he was thinking of something more like a virtual museum diorama, not a film.) And true, a 3-D model of a city "miles" wide would be too small to be useful at 20 feet diameter. But—the modern reader can't help but think—you could zoom it and rotate it and… oh.

Right. You couldn't in this future because—here it comes—this world has no computers.

That's the big problem with this story, the main thing that makes it hard for a modern reader to accept. There are no computers in it at all. We are so accustomed to computers that can show us models of things, and let us rotate them and pan and zoom them, and computers that can show data in so many graphical modes, at our desks or in the palms of our hands, that we just can't understand Luna City's problem. Forget the city map; why would they not have an internal GPS system, and an app for your phone that gives you turn by turn directions?

Because nothing remotely like that existed in 1957. Computers were based on vacuum tubes and occupied rows of cabinets in air-conditioned vaults (and had less computing power than your digital wrist watch). Surely Heinlein knew about them; the whole nation watched with Walter Cronkite as UNIVAC predicted the Eisenhower victory of 1952. But nobody had put a graphical display on one (the first video display terminal was a decade away). The idea of a computer small enough to fit your desk, let alone in your pocket, was just inconceivable.

Of course if we built Luna City now, there'd be no need for guides or maps; every tourist would just install the Luna City GPS app in their finger-phone (or directly into their skull implant) and never be lost.

So, is the story ruined? Well, there are some wonders left; let's not forget the "flying" thing.

Checking in

Holly leads Miss Brentwood to her hotel, the Zurich, "in Pressure One on the west side so it can have a view of Earth." They have very different opinions of the same view:

I helped Miss Brentwood register with the roboclerk and found her room; it had its own port. She went straight to it, began staring at Earth and going ooh! and aah!

I glanced past her and saw that it was a few minutes past thirteen; sunset sliced straight down the tip of India--early enough to snag another client.

Pressure One, presumably the first and oldest pressure zone, seems to be at least partly above the surface with outside views. It's a nice touch that Miss Brentwood sees Earth as beautiful, and Holly sees it as a handy clock. Remember that the first pictures of the full earth were a decade off (the iconic Earthrise shot wasn't taken until 1968). Heinlein nor anyone else really knew what Earth would look like from the Moon. Gagarin would orbit the Earth in 1961; from then on we would know that it looks blue, white and brown, not white and green. So Heinlein was smart not to describe the view. If he'd wanted, he could have calculated the angular diameter, and realized that Holly would need very good eyes (and of course clear skies) to distinguish India.

Did you notice the "roboclerk"? There are no other automatons in the story, so this is just a throwaway bit of scenery which really doesn't support serious thought. Maybe he was picturing, not some kind of android clerk, but a special-purpose juke-box kind of device built into the desk. In any case, the 21st-century reader would expect a touch-screen terminal of some kind and wouldn't think it special enough to justify a "robo-" prefix.

Hand-off to Jeff

Miss Brentwood impulsively decides she wants to go out on the surface right away.

"…Holly,can you get us space suits? I've got to go outside."
…I simply said, "We girls aren't licensed outside. But I can phone a friend."

Oh, Robert, WHY?!? We girls aren't licensed outside? The point of this exchange is to get Holly to hand Miss Brentwood off to Jeff. To achieve that it would be perfectly acceptable for Holly to lack an outside license because of her age (Jeff is three years older, we find out in the next paragraph) or because she hasn't had time to take the exam, or any of a dozen other reasons. But to just glibly say girls aren't licensed outside? It's really hard to rise above all one's prejudices.

Oh, well.

Next we have a paragraph of exposition; I'll quote it because it establishes the Holly-Jeff relationship.

Jeff Hardesty is my partner in spaceship designing, so I throw business his way. Jeff is eighteen and already in Goddard Institute, but I'm pushing hard to catch up so that we can set up offices for our firm: "Jones and Hardesty, Spaceship Engineers." I'm very bright in mathematics, which is everything in spaceship engineering, so I'll get my degree pretty fast. Meanwhile we design ships anyhow.

I didn't tell Miss Brentwood this, as tourists think that a girl my age can't possibly be a spaceship designer.

Right, now we get the feminist attitudes. Oh, well.

Jeff … waits at the West City Lock and studies between clients. I reached him at the lockmaster's phone. Jeff grinned and said, "Hi, Scale Model."

"Hi, Penalty Weight. Free to take a client?"

"Well, I was supposed to guide a family party, but they're late."

"Cancel them. Miss Brentwood . . . step into the pickup please. This is Mr. hardesty."

I love the touch of Holly and Jeff having these pet names. But I have a hard time thinking how incomprehensible that sequence would be to a present-day juvenile reader. "Daddy, why didn't she just call Jeff's phone?" I'm an old fart; I can remember when you could call the fixed land-line phone in some workplace or office and ask for someone by name—"Hi, is Jeff there?" "Yeah, hang on... Jeff! Yeah, here he comes..."—But fewer people understand that scenario every day.

part 5

Tuesday, August 2, 2016

A Close Reading of Heinlein's The Menace from Earth (part 3)

This essay begins with part 1

Para 3, launch the action

In the third paragraph we finish up exposition, introduce a second main character, and kick-start the plot.

Mornings I attend Tech High and afternoons I study or go flying with Jeff Hardesty--he's my partner--or whenever a tourist ship is in I guide groundhogs. This day the Gripsholm grounded at noon so I went straight from school to American Express.

This wastes no time, does it? My name's Holly, I live in Luna City, there's some guy Jeff, and bam I am off to guide tourists! He feeds you just enough exposition to get you oriented and a bit intrigued ("flying"? on the Moon?), and then he starts the action.

But on a close reading, some things need mention. First, the use of "partner". Today, "partner" has some different connotations than it did when this was published 60 years ago. A millenial reader might suppose that she means a sexual partner, which would be a complete misreading. In 1957, the word connoted only a professional or business partner. The reader might suspect that Holly is inflating some juvenile pastime to adult status, and in the next paragraph she mentions, "Guiding is just temporary (I'm really a spaceship designer)." Clearly she's not actually a spaceship designer at age 15, so this confirms that she's inflating a hobby to adult status. The alert reader will connect that with "partner" reference.

Another reference that falls flat is "American Express". The modern reader's only association with American Express is of a credit card with more restrictions than most. In the 1950s, the American Express office was the traveler's oasis, providing all kinds of services including money transfers from home. Heinlein extrapolated this to the future, so it would be natural for tourists, fresh off the ship, to go first to the American Express office to get money, maps, and hire guides or porters. Sixty years on, if you want cash in a foreign city, you go to an ATM.

Meet The Menace.

Holly is assigned to guide "Miss Brentwood".

"'Holly,'" she repeated. "What a quaint name. Are you really a guide, dear?"

Holly spends a couple of paragraphs mentally dissing "groundhogs" for their ignorance, then

"My license says so," I said briskly and looked her over the way she was looking me over.

So far, so good. But now I think Heinlein loses Holly's voice.

Her face was sort of familiar and I thought perhaps I had seen her picture in one of those society things you see in Earthside magazines--one of the rich playgirls we get too many of. She was almost loathsomely lovely ... nylon skin, soft, wavy silver-blond hair, basic specs about 35-24-34 and enough this and that to make me feel like a matchstick drawing, a low intimate voice and everything necessary to make plainer females think about pacts with the Devil. But I did not feel apprehensive; she was a groundhog and groundhogs don't count.

Sorry, this doesn't sound like the Holly I've already come to know and parts just don't make sense.

First, I literally don't know what Heinlein meant by "society things ... in ... magazines". Articles? Why is Holly reading magazines? She's a busy, active student; the doings of Earthside socialites is about as far from Holly's interests as you can get. Indeed, would anybody be reading "Earthside magazines" in Luna City? Surely it couldn't be economical to ship bales of Vogue or Elle to the Moon? It would be much more credible if Holly remembered this face from a TV show, not from print. This is the first (but not the last) place where the absence of electronic media harms a modern reading. TV was hugely popular in 1957, why didn't Heinlein extrapolate that to Luna?

Next, the remark about "rich playgirls" doesn't fit. It just isn't credible that there would be enough of those for Luna City as a whole, let alone Holly, to have experienced "too many" of them. Plus, "rich playgirls" don't travel alone, they have entourages of agents and gofers, and usually handsome boy-toys. I think this remark is some personal irritation of Heinlein's that he let leak into Holly's voice.

I also don't think Holly would say "loathsomely lovely". Holly is more distanced than that, especially regarding a groundhog. She would use some word to suggest she saw Miss Brentwood as a specimen; or, being a confident youngster, she might be frankly admiring: "she was quite lovely, in a grown-up way".

The phrase "basic specs about 35-24-34" was probably meant to show Holly's engineering bent, but to me it is the kind of joke a male engineer would make. Plus, describing women by their bust-waist-hip measurements has a 1950's ring. I remember it as a commonplace sexism of my youth (in Playboy maybe?), but I don't think people use it any more.

To continue the boring nit-pickery, "everything necessary" is vague. She's already listed pretty much "everything" except—and here is a key exception—Miss Brentwood's clothes! If Holly has any girly-girl genes she would notice clothes. A teenager, realizing she is in the Luna City equivalent of flip-flops and a hoodie next to someone in expensive fashion wear, would feel instant mortification. Holly should have noticed the disparity of clothing even ahead of that in body shapes.

And also, "make plainer females think about pacts with the Devil"—is this something Holly would think? I don't think Holly lumps herself with "females", nor has the deep need to be admired for her looks that would motivate selling one's soul to get them. Her father might have this wry thought (or Heinlein might); but not our 15-year-old nerd.

continue in part 4

A Close Reading of Heinlein's The Menace from Earth (part 2)

This essay begins with part 1

For quoting the story text I use a paragraph style that looks like a typescript. In that text, where Heinlein used an ellipsis, I spell it out as three spaced dots, so: . . . ; then where I've omitted text, I use an ellipsis character,

Opening Paragraph: Meet Holly Jones

Right, then. Let's read the opening paragraph.

My name is Holly Jones and I'm fifteen. I'm very intelligent but it doesn't show, because I look like an underdone angel. Insipid.

First let's talk about what that brief paragraph accomplishes. The words give us some facts, and the tone tells us about the character: she is self-confident and plain-spoken. The phrase "underdone angel" perfectly forms the image of young, pale, blonde person, probably of slight build (certainly not voluptuous, like the swimsuit model on the cover of the book); but the phrase also has a self-deprecatory tone. She doesn't look like an angel, but like an underdone, insipid angel: pale, unfinished, childish. We suspect she has spent time looking in a mirror, feeling sarcastic about nice things her mother said. "An angel, right..."

In short, that's a model of an opening paragraph: It establishes the protagonist's looks and personality. Also, it establishes what Jeff Smith calls the bond of trust between writer and reader: we believe the author knows what he's about, and it will be worth our time to keep reading.

Let's talk about first-person narrative. Beginning writers are advised to avoid it, for a number of good reasons. Here is Heinlein, an experienced writer, using it in the most direct way imaginable. We don't know why Holly has chosen to tell this story. Is she talking to someone? Writing a diary? That's a flaw, according to many editors. "You need to be clear whom the narrator is addressing," says Julie Ann Dawson. Think of Andy Weir's The Martian: he makes it clear just a few lines in that Mark Watney is recording his notes because he expects to die and wants to leave a record.

On the other hand, Heinlein is in pretty good company when he jumps into a first-person narrative with no apology or explanation:

  • Daniel Defoe, Robinson Crusoe: "I was born in the year 1632, in the CIty of York, of a good family, though not of that country, my father being a foreigner of Bremen, who settled first at Hull."
  • Marcel Proust, Swann's Way: "For a long time I used to go to bed early."
  • Gene Wolfe, The Shadow of the Torturer: "It is possible I already had some presentiment of my future."

We never know why those people chose to tell their stories, and we are never told why Holly is telling hers.

Paragraph two: high-density exposition

The second paragraph establishes Holly's milieu.

I was born right here in Luna City, which seems to surprise Earthside types. Actually, I'm third generation; my grandparents pioneered in Site One, where the Memorial is. I live with my parents in Artemis Apartments, the new co-op in Pressure Five, eight hundred feet down near City Hall. But I'm not there much; I'm too busy.

Heinlein's Luna is familiar territory to anyone who's read The Rolling Stones or The Moon Is a Harsh Mistress, but let's read this as if we were new to it. Look at all the information that is conveyed, or clearly implied, by these four sentences.

  • We're on the Moon.
  • The Moon is populated and developed enough to have a "City".
  • The people there have enough of a local culture to hold themselves distinct from "Earthside" types.
  • People have been on the Moon for some decades, based on "third generation"—and "the Memorial" implies something with age to it.
  • The city is expanding: Holly lives in a "new co-op", and "Pressure Five" suggests there are also older Pressures One to Four.
  • The city is organized vertically: City Hall is 800 feet underground.
  • Holly is free to move around (she's not home much) which says a lot about her relationship to her parents.

That's a lot of exposition disposed of very quickly, tossed off in a breathless rush in Holly's voice.

It doesn't really matter, but how old is Luna City? Holly's "third generation" is ambiguous. If it means she's in the third generation of people living on the moon, then we have:

  • Her grandparents come from Earth aged ~20.
  • Her parents are born in Luna and have Holly about age 20.
  • She's 15; thus 35 years since her grandparents began "pioneering".

But it could mean the third generation of people born in Luna. Then her grandparents grew up on the Moon and when they reached adulthood, moved out (from some unspecified first colony) to pioneer Site One. That would add another 20 years, making the total 55. Thirty-five to fifty-five years doesn't seem like a lot of time to build a large, complex underground city. Even if there are natural caverns (which Heinlein mentions later in this story and in other stories in the series), it isn't as simple to grow as a surface city. San Francisco could mushroom in a few years of the Gold Rush, or Oak Ridge could mushroom in a few years of WWII atomic work, because people could create streets just by laying out rows of flags, and throw up frame houses on any bit of flat ground. Creating a multi-level city even in an existing cave means extensive work in pressure suits. Vertical organization needs more sturdy architecture than a framed cottage. Even at Lunar gravity, an 800-foot stack of dwelling layers needs solid support!

Oh well; again, it doesn't affect the at-speed reading experience. The "third generation" reference is enough to establish a feeling of history and Holly's pride in it. On to the next paragraph, where we learn a bit more about our girl protagonist.

continue in part 3

A Close Reading of Heinlein's The Menace from Earth (part 1)

I was looking over a shelf of old paperbacks and pulled out the chunky bulk of Robert Heinlein's The Past Through Tomorrow (subtitled "Future History Stories Complete in One Volume"), an 830-page brick put out by Berkeley Medallion in 1967 (cover price $1.95).

If this tome were in good condition it might be worth a bit on the used market, but it began to fall apart as soon as I opened it. I read most of the stories in it—stories I hadn't looked at or thought about in years—and soon a third of the brittle, browning pages were loose and falling out. So it will have to go in the recycle tub as soon as I have finished this essay.

All of the stories in the collection had the usual Heinlein virtues: tight, clean prose; characters with clear motives; fast-moving linear plots that come to definite conclusions. Some of the stories have dated badly. It should not surprise, really, when a 50-year-old vision of the future can't sustain our credibility today. The Roads Must Roll described a technology that wasn't credible even when fresh. Blowups Happen tried to predict what a failure at atomic power plant would be like before any such plants existed, and Heinlein's guesses were nothing at all like the actuality of Chernobyl or Fukushima. The plot of one of his most famous stories, Requiem, depends on an unbelievable background of rocket pilots flying from one county fair to the next, like the barnstorming pilots of the 1920s. We understand all too well that rockets that can reach Earth orbit will never be small enough or cheap enough for individual ownership, or safe enough to land or take off casually from a suburb, and I couldn't make myself suspend disbelief long enough to enter these stories.

On the other hand, The Black Pits of Luna paints a quite credible picture of a search for a lost child on the Moon's surface. Heinlein worked out what it would be like to walk on the Moon in a space-suit, what the lighting conditions would be, the constraints of limited time and air—in 1947. Nothing we saw broadcast from the Moon by an Apollo mission contradicts this story. Then there's Delilah and the Space Rigger, about the struggle of a competent woman to be accepted by a male work crew, and all the dialog and the attitudes seem depressingly modern.

For me, one story stood out: The Menace from Earth (hereafter, TMFE). It is a classic because it first gave us the unforgettable image of people in a low-gravity world flying like birds. Besides that, it has charming characters, clever dialogue, and gives us a virtual clinic on how to handle the SF-writer's bane, exposition.

When I read TMFE this time, it might as well have been the first time. I could remember having read it before and knew where the plot was going, but all the details were fresh. It was a delightful reading experience; I even got a little sniffly at the ending. "What a fun, warm-hearted, story," I said to myself, and the intention to write an appreciation of it was already half-formed.

I thought to find an online etext of the story so I could link to it and copy from it. However, Heinlein's estate guards its copyrights carefully, and I have not found a free copy. The story is still available in print on the used market, bundled in a collection of the same title. Of course that had to have a cover image of a voluptuous 20-year-old, even though the protagonist says she's 15 in the first sentence.

At the Amazon page for that collection, the "most helpful" review says,

...the title story features annoying teenagers in an annoying romantic plot. The teenagers annoy with their brilliance, and the plot annoys with its story of the girl narrator discovering, after the introduction of a beautiful Earth woman, that her boy friend is really her boyfriend.

What a curmudgeon! Well, it is a rom-com, and this reviewer clearly doesn't like that genre. If you don't like the rom-com genre, you probably won't enjoy deconstructing TMFE either.

The Heinlein Archives

While searching for an etext that I could link to, I made a surprising discovery. If you visit The Heinlein Archives, you can buy, for a very reasonable fee, PDF copies of Heinlein's original manuscripts!

Yup, for $3 I bought Opus 126, described correctly as 'numerous drafts of “Menace From Earth” all with extensive hand-edits and cuts'. Who'd have thunk it! For three bucks, I could browse Robert Heinlein's MSS pages, complete with hand-edits, which were indeed very extensive. There's a place in, I think, Grumbles from the Grave where Heinlein claimed that once he got the idea for a story, his only problem was "typing fast enough to keep up with what the characters are doing", or words to that effect. Well, maybe that's how he wrote a first draft, but look at this:

Look fast and casual to you? No, me neither. I probably edit my work that much, too, but on-screen editing doesn't leave a trace. This is what a professional's work looked like in the days of typewriter MSS. And every word clearly was considered multiple times.

Reading The Menace from Earth

My first, fast read of TMFE was a pleasure. However, now I have begun to reread the story critically, I have noticed a number of things that are not perfect: dated usages that a modern reader (i.e. one half my age or less) would not catch, or might even misunderstand; as well as some dated attitudes. So the rest of this essay does not consist of unalloyed RAH! RAH! fanboyism. (See what I did there?) (All right, never mind.)

Shortly I will begin to read this story carefully. I mean to quote quite a lot of it—looking over my shoulder for the Heinlein copyright wardens, but I think I've stayed in fair-use bounds—just the same, if you can find a copy and read it straight through first, that would be a good idea.

continue in part 2

Friday, July 1, 2016

OK Maybe I Was A Little Bit Premature

"And now for something completely different..." I said, and then...

Coming.

Real Soon Now.

Tuesday, June 14, 2016

And Now For Something Completely Different

For a couple of years, this blog has been totally focused on my adventures with Python, Qt, and PyQt as I developed a major-ish app, PPQT2.

Well, with the latest round of fixes (see two prior posts) I believe that PPQT2 is about as done as it can be. I will leave the distribution files up indefinitely. But I don't intend to add any function, and I don't believe it contains any bugs serious enough to prevent its productive use. A couple of people are using PPQT2, and I wish them well. I am not among them, because I no longer spend any of my own time editing books for Distributed Proofreaders. The first PPQT was done to meet my own requirements for a book editor. The second one was done, really, to do the job right from a software engineering standpoint, and to meet the evolving needs of the Distributed Proofreading community. It succeeded in the first goal, but, based on limited uptake, failed to meet the second. So it goes.

Back to Authorship

A decade ago I wrote a book. Well, I've written several books over the years, mostly software. But that latest book was not about software. It was in a way, an extended answer to a question my mother asked me a few months before she died. I wrote the book and self-published it.

You can read about the book at its home website. In fact, you can read the whole book online there if you wish. Or you can follow links from there to buy your very own hard-copy for a very reasonable fee.

Don't do that!

Well, you can read it if you really want to. But don't order a copy (not that you were likely to do so, but still).

Why? Because I've embarked on a major revision, is why.

Adam Osborne is famous for killing his company by pre-announcing a better model before it was ready to ship. Sales of the existing Osborne computer dried up, choking off the cash flow that he needed to finish development. I'm not afraid of killing sales for my existing book because mostly, it has none to kill.

But why am I doing a revision? What improvements do I mean to make? How am I going about it?

Such questions will be the focus of forthcoming posts in this blog. In the meantime, if you want to get an early look, go to the new publishing site, slide the "You Pay" slider all the way to the left, to a cost of $0.00, and "buy" a copy of the book as it stands. If you give leanpub your email in that process, you'll get a notification as new versions are published, which is about monthly as I add chapters.

Friday, June 10, 2016

Fixing the Edit Menu Fix

A year and a half ago, I wrote an incomplete description of my solution for separate edit menus. While using PPQT2 in Windows 7 to test the logging/dictionary bug, I noticed a problem with this system and fixed it.

To review, under Qt there is a single menu bar that is owned by the main window. (The main window is a parentless widget based on the QMainWindow class.) In the menu bar there can be just one Edit menu.

In the Qt system, any menu is populated by QAction objects. In the Edit menu, if there is, say, a "Cut" item, it is installed in the menu by adding a QAction('Cut'). Any QAction has four properties:

  • A name string, like 'Cut'
  • An optional icon
  • An optional accelerator keystroke, like control-x
  • A "slot", which is an executable

The slot is the important item. It is a function that receives the "triggered" signal when the user selects that menu item or keys that accelerator.

This is a fine system, provided that there is no ambiguity about which slot to call. In an app with a simple UI, the one main window perhaps, the slot for File:Save or Edit:Cut is a method of the QMainWindow object, or of some sub-widget that it owns, and that never changes.

In PPQT2, there are multiple different widgets that can implement Edit actions. In the simplest case, the user has a single book open. But there is an Edit panel with the book text, a Notes panel with a separate simple document to edit, and a Words panel which supports copying selected words from a table. If the focus is in the Edit panel, we want the Edit:Cut action to be implemented by the cut() method of the QPlainTextEdit object that is part of that panel. But if the focus is in the Notes panel, we want Edit:Cut to be implemented by the cut() method that is part of the QPlainTextEdit object that is part of that panel. And if the focus is in the Words panel, we don't want to implement Edit:Cut at all, but we do want to support Edit:Copy via a method of the QTableView that implements the vocabulary table.

Worse: if the user has two or three books open, each book has its own Edit, Notes and Words panels. When the user hits control-x, it is important that the cut be implemented by the Edit or Notes panel for the book that is in front -- not by some panel containing the text of a different book that isn't visible.

The only solution is to continually change out the actions in the one-and-only Edit menu whenever the focus changes. When the Notes panel gets focus, it loads the Edit menu with the set of actions it supports, including slots that are methods of that unique Notes widget (distinguished by the "self" parameter). The code to do this, before this week, was as follows:

def set_up_edit_menu( action_list ) :
    global _EDIT_MENU, _LAST_MENU

    _EDIT_MENU.setEnabled(True)
    if id( action_list ) != _LAST_MENU :
        _LAST_MENU = id( action_list )
        _EDIT_MENU.clear()
        for (title, slot, key) in action_list :
            if title is None :
                _EDIT_MENU.addSeparator()
            else :
                _EDIT_MENU.addAction(title, slot, key)
def hide_edit_menu():
    global _EDIT_MENU
    _EDIT_MENU.setEnabled(False)

Any widget that wanted to support the edit menu had to do three things. It had to set up a list of Edit menu actions it supported, including the title, the slot, and a key code for each. It had to catch the focus-in event and call set_up_edit_menu() with that list. It had to catch the focus-out event and call hide_edit_menu(). As a result the Edit menu would be disabled (grayed-out) whenever focus was in a widget that didn't support it. When focus entered a widget that did support the Edit menu, it would be enabled and populated with that widget's actions, including references to that widget's slots.

I observed that it was quite frequent for a widget to get a focus-out followed by a focus-in, with no intervening visit to another widget. That's why the work of changing the menu is only done if the id() of the list is different. The Python id() of an object is basically its memory address. In my first description of this code, I was requiring the caller to pass a unique key, but I soon figured out that was not needed; id() did the job.

This all worked fine under Mac OS. Unfortunately I'd never really checked that it worked under Windows or Linux. This week I noticed that it did not. (imagine a blushing embarrassed emoji here)

In Windows and under Ubuntu Unity at least, the Edit menu was disabled, grayed-out, all the time! Why?!? The contents of the menu were changing properly, for example when focus was in the Edit panel, the menu contained the to-Upper and to-Lower actions that only the Edit panel supports. They went away when the focus moved to the Notes panel. So set_up_edit_menu() was being called. Which meant _Edit_MENU.setEnabled(True) was being executed.

It took a good half-hour of swearing and inserting print() statements before I realized that hide_edit_menu() was being called when I clicked on the word Edit in the menu bar. (imagine an open-mouth amazed emoji)

It seems that in Windows (and in Ubuntu, I later found out), a click anywhere in the menu bar causes a change of focus! You click in the Edit panel; the focus-in event calls set_up_edit_menu(). You click on the word Edit in the menu bar and the Edit panel gets a focus-out event and immediately calls hide_edit_menu(). It's a beautiful catch-22: the menu is enabled all the time until you try to use the menu, then it is disabled.

It took some pondering to figure out how to fix this. My solution was to ask, in hide_edit_menu(), if the current mouse location was within the global menu bar. If so, do not disable the menu. Here's the new code:

def hide_edit_menu():
    global _EDIT_MENU, _MENU_BAR
    
    if not C.PLATFORM_IS_MAC :
        relative_cursor_pos = _MENU_BAR.mapFromGlobal( QCursor.pos() )
        if _MENU_BAR.geometry().contains( relative_cursor_pos ) :
            return
    # either this is Mac OS, or the cursor is not over the menu bar
    _EDIT_MENU.setEnabled(False)

That made the Edit menu behave the way I wanted in all three systems.

A Logging problem in Windows -- and a pivot!

This is the first of a few posts about PPQT2, the hobby software project that's been the focus of the blog for two years. Then there will be a post announcing a turn to something completely different.

This week I had to deal with a reported bug in PPQT -- note, the first one in six months or more, which is more a reflection of its very low usage than on its code quality -- and found a couple other things that needed work.

Dictionary bug

The bug was present on all platforms, but was only visible on Windows. Here's the problem. The user opens a file that contains some non-Latin-1 characters, maybe some words in Greek. The default dictionary is set to en_US and the Greek words are not tagged for an alternate dict. So when the user refreshes the Words panel, every Greek word gets presented to a Hunspell object initialized with the en_US dict.

The encoding of any dict is specified in its name.aff file. The en_US dict is encoded ISO8859-1, and Hunspell expects any word checked against it to be encoded the same. Obviously a Greek word will have non-Latin characters, and Hunspell will correctly suffer an encoding error.

Not a problem! I was ready for this:

            try :
                return dict_to_use.spell(word)
            except UnicodeError as UE :
                dictionaries_logger.error("error encoding spelling word {}".format(word))
                return False

However, in Windows the call to the logger .error() itself raised an encoding error! Because the logged string contained the offending Greek word, and the log file had been opened using the default encoding which in Windows, was some wonky code page that couldn't encode Greek.

So there was an exception raised in the Except block, and the return False to indicate misspelling was never executed. But that did not cause a problem! Because the caller used the return value in an if statement, if check(word):. After Python displayed the traceback in the console window, it resumed execution in the calling function with the conventional default return value of None. None is just as False as False is, so everything worked, except for a ton of error messages in the console window.

The fix was to go back to the top-level module where I set up the logging handler for the whole program, and add encoding='UTF-8' to that call. A one-line fix and the error messages disappeared.

Thursday, February 4, 2016

byteplay ok on linux

Oh my but this was a strenuous afternoon. I believe I shall now drone on about my adventures, just because the story ends well.

First, I installed Ubuntu 14.04LTS on my 64-bit dev virtual machine. I kind of hated to do this because the install ISO does not offer what to a Mac user is the sensible choice, of reinstalling the OS without changing the user files. Nope. Either it will install "beside" the existing system, meaning in a different disk partition, or it installs "over" the existing system which wipes all your user files and settings. So I did the latter, knowing full well I would have to reinstall all my dev tools and dependencies.

In a month or two I will need to install Py/Qt5.6 and some Python modules, so I can upgrade PPQT and Cobro. But for the time being, I only needed to get a working Wing IDE and Python 3.5. The Wing IDE is a simple install, then activate with my license key.

Python.org does not offer an installable package for Linux. There are Ubuntu/Debian packages for Python 2.7 and for Python 3.4, but I needed 3.5. The only way to get that is by downloading the source package and making it. Well, I've done that often enough. Download, unzip, make, make test, sudo make install. All seems to go well until I try to start Wing, and it can't start Python. "Missing module _struct".

DuckDuck that phrase and you will find that hundreds of people have encountered it. What does it mean? It can be properly translated as, "you suck at installing software". Something didn't go well in the make-install step. More missing things show up when I find out that neither pip nor easy-install were installed. When I try to download and run get-pip.py, it fails with an error because the SSL lib wasn't installed, and also the zlib was not installed.

Scrolling back in the make output I find a list of modules that it couldn't locate. Did it put the list at the end where a person would notice it? Of course not! It buried it between a couple of hundred lines of output before and after. Did it tell you what to do? Of course not! Well, it did; it helpfully advised that you "look for the name of the missing module in setup.py".

Which I did, and saw where it built a list of libraries to search for things. Then I got a terminal window and used the find command to find the things it was not finding. Oh, there they are, in a special 64-bit library. Add the path to that library to the code of setup.py and rerun the make install. OK then! Now I've got pip, and Wing is working, whee.

So now I can attempt what I set out to do a couple hours earlier, pip install byteplay3. Which of course fails with an obscure message, "Could not find a version that satisfies the requirement". What requirement? Back to DuckDuckGo. Oh yeah, plenty of people were having this problem a couple years ago, when pip began to require version numbers that complied with PEP400. I read PEP400. No, my version number fits the pattern. But in one of these postings I see a remark that, oh, also, pip does not support modules that are not hosted directly at PyPi. A module hosted at github, for example. Oh. Which I am doing.

So I find out about setup.py's build and sdist and upload commands. And find out the hard way, that you can't do

python setup.py sdist
python setup.py upload

oh no no no! You have to do

python setup.py sdist upload

all one line. Because...? Who knows.

Anyway, did that, and then in my Linux system and my Mac OS system both I could do pip install byteplay3 and it did it and I could start Python and run the example from the readme, and it worked.

Tomorrow, Windows. I am so looking forward to that.

Wednesday, February 3, 2016

On the treadmill again

So part of supporting byteplay3, which I have optimistically uploaded to PyPi, is to check that it installs on linux and on Windows. So I fired up Parallels and opened my 64-bit Ubuntu dev machine.

Now, I do this not very often, so it seems inevitable that every time I do it, I have in the interim updated Parallels itself, and every time Parallels updates, it wants to update the Parallels Tools component it inserts into every virtual machine.

So of course as soon as I open the Ubuntu dev VM, it starts installing a new Parallels Tools. Which for some reason takes 5 minutes to crawl its progress bar across, and then wants a reboot.

Meanwhile I thought, hey, let's make sure Ubuntu is up to snuff also. I bring up the Synaptic package manager. Current Ubuntu has a much simpler "Software Updater" program; and I know that the hard-core Linux user doesn't want any of that GUI shit, nothing but sudo apt-get on the command line will do. But me, I remember the SGI package manager for IRIX (but can't remember its name, though) and Synaptic is a dead ringer for it. So I use Synaptic.

I tell it, reload your info and it says it is reloading but then it says it couldn't find a bunch of repositories. It marks a bunch of upgrades but when I tell it to apply them, it grinds a while and says it couldn't download the packages. 404's everywhere.

imagine an hour or so of increasingly irritable investigation here...

Long story short, at some time in the near past, I unwisely and unwittingly approved an upgrade from Ubuntu 14.04 LTS, to Ubuntu 14.10. Probably seemed like a reasonable thing to do at the time, you know? Minor point-release upgrade? But a bad, bad move on my part!

What was making Synaptic (and the Software Updater) fail flat was that their apt sources-list file specified "utopic" release in the repositories. Because, you see, I had upgraded to 14.10 Utopic Unicorn. Unfortunately, 14.10 Utopic was end-of-lifed back last July and all the "utopic" repositories were pulled from Ubuntu and Canonical. So that's why the 404's—the repositories are all gone.

Wait, what? I thought that Ubuntu 14 was a long-term support version!

No no no, you foolish person. Ubuntu 14.04 Titillated Tarsier, that was the LTS release.

When I stupidly approved an upgrade to 14.10, I moved away from the LTS system and once more stepped onto the every-six-months upgrade treadmill. I cannot apply maintenance to 14.10, I must upgrade to 15.04 Violent Vixen, and very soon again to 15.10 Willful Wildebeast, as per this diagram.

Alternatively I can find a DVD-ROM image of 14.04 and install it over my system to force a downgrade, which I may just do.

Meanwhile, fortunately, the 64-bit test system (without Python or Qt) is still on 14.04, as are, I hope the two 32-bit systems.

Meanwhile, less fortunately, the Windows 7 dev system seems to have forgotten how to run Pip-Win. It looks as if it is starting Python 3 but with the Python 2.7 lib. So some stupid error in the PATH or other environment variable. So I have that to look forward to, tomorrow.

Meanwhile again, I applied the 15.14 update and guess what? The dev system no longer remembers that it is a 1440-px wide window. Lost all the window sizes except 800. Another config file to remember the name of and edit.

Tuesday, February 2, 2016

Function signatures; bytecode lacks "computed go-to"

Well, sure, there was a bug. A good thing I blogged about it, or who knows it would have gone untested and unfound?

The Code class represents a code object, but in a form that can be manipulated. It has two crucial methods. from_code() accepts a Python code object and captures all its contents, returning a new instance of Code class. I had properly updated that method to notice the Python 3 features of varkwargs and the kwonlyargcount.

I had not completely dealt with these in the other method, to_code(). Calling to_code() of a Code object returns a code object that is supposedly equivalent. But I had not included the kwonlyargcount in the calculation of the code.co_argcount. So it was off by 1, causing a TypeError exception, wrong number of arguments, when you called the code.

Testing for that also revealed a bug in my unit-test scaffold code. But it's all good now.

Tiny Basic and the computed go-to

In the first post in this series, I mentioned that one use of byteplay would be to implement domain-specific languages using Python bytecode as a the implementation language. And I suggested I might try to implement a Tiny Basic compiler in this manner. Turns out? Not so much.

Historic background: Tiny BASIC was the term for several, minimal BASIC interpreters for early microcomputers. The first was written by Tom Pittman of Itty Bitty Computers. The more influential version—because it was for the 8080 where Pittman's was for the 1802—was by Li-Chen Wang. I was aware of Tiny BASIC although I never used it. (I programmed my Z80-based CP/M system in assembler, thank you.)

Anyway, Tiny BASIC is such a minimal language that its interpreter, including an adequate editor, can be implemented in a few kilobytes. On a walk yesterday I thought about it and at first got rather excited about the possibility. I quickly arrived at a program structure (a dict keyed by the line number with the text of the line as value) and visualized how I could use the built-in compile() function to reduce expressions to bytecode, and glue those bytecode bits together with more bytecode and thus produce a whole Python function from a BASIC program.

Then, sitting in a coffee shop, I used my phone to look up the syntax of the language...

...and is it not a fabulous age we are living in, where one can be sitting over a capuccino and have the passing notion, "I wonder what was the syntax of a programming language last used over thirty years ago?" and be reading the answer in less time than it takes to describe it? Seriously, people, why are we not all happy as kings?

So, here's the manual. Less than five minutes of skimming on the little screen of the phone revealed a major, major problem.

The problem is that Python bytecode has no real "jump" instruction as found in an assembly language. It has several jump opcodes, for example POP_JUMP_IF_TRUE and JUMP_ABSOLUTE, but the argument to all of these is a fixed integer offset in the bytecode string. The amount to jump forward or backward, or the absolute offset to jump to, is hard-coded in the instruction. There is no way in bytecode language to say, jump to the offset encoded in the top-of-stack item.

Without such a "computed go-to" it becomes much more difficult to implement Tiny BASIC. Because Tiny BASIC has both a computed GOTO and a GOSUB. Of the GOTO, Pittman's manual explicitly says, "the next statement to be executed after a GOTO has the line number derived by the evaluation of the expression in the GOTO statement. Note that this permits you to compute the line number of the next statement on the basis of program parameters during program execution. (my emphasis)". The GOSUB presents the same problem in two ways. First, it takes an expression, so the actual target is determined at execution time. Second, it stores the return location on a stack for use by the RETURN statement. RETURN is effectively a GOTO where the destination is fetched from the call stack.

None of these things can be implemented using a bytecode jump. So that pretty well ends any thought of compiling a Tiny BASIC source file into a single Python function.

I did think of a complicated mechanism, basically each line of the BASIC program would compile into a separate function that could operate on globals (the BASIC variables) and must return the number of the next line to be executed, with None meaning, "next sequential". But it would be kind of ugly and clumsy and not a good demo of byteplay3.

Monday, February 1, 2016

Byteplay3 uploaded to pypi

The PyPi registration process turned out to be remarkably easy. I had to register as a user, something I'd never done despite hundreds of uses of pypi over the years. Then it was just python setup.py register and that was it.

It took a little while to show up in a search at PyPi, but here it is now. I do not understand why the descriptive paragraphs (which the PyPi "edit package" page clearly says, can use reStructured Text markup) are not formatted right. The rST markup for the example and the headings is just being ignored. Meh. Don't care.

What I did to Noam's code

Heh heh quite a lot, really. For one thing, I commented the bejeezus out of it. That was how I went about understanding it (and it took some understanding, lemme tell ya). I read it (and read it and read it) and then I commented what it was doing. Initially I peppered it with comments like #TODO what the bleep is this for? and the like, then gradually replaced all those with meaningful comments.

Prior to and during this work, I was watching Philip Guo's lectures on Python internals. Although these are based on Python 2.7, he leads you through the modules of CPython that are involved with compiling and executing code. So part of the experience of making byteplay work in Python 3 was discovering what about CPython internals had changed from 2.7 to 3.4. The changes are mostly cosmetic, for instance the names of attributes of the function object are different. A few bytecodes have been added and deleted. But the concepts are pretty consistent.

Function Signatures

Some significant changes involve the code object and its handling of the function signature. I'm not sure if the *args and **kwargs features are Python 3 innovations? But the Python 3 code object has properties to deal with them that are not in Python 2.

If the signature of a function has some arguments, then *args, then another argument:

def foo( a, b=False, *args, z=True ):

then the argument z is a keyword-only argument. It cannot be entered as a positional value; it must be specified with z=:

res = foo( 1, True, 2,3,4, z='z' )

The code object attribute kwonlyargcount is the count of such arguments. If the function has such arguments, it is nonzero. This attribute wasn't present in Python 2, and I had to add it to the Code object that is the centerpiece of byteplay.

Byteplay2 did know about the varargs attribute of a code object, a flag bit to say if it has a *args in its signature. (So, that's from Python 2.) However, Python 3 has added a varkwargs flag. That also I had to incorporate into the Code class.

All of these affect the interpretation of other parts of the code object. One crucial thing in a code object is a tuple, varnames, composed of the names (strings) of all local variables. The sequence of names in this tuple is:

  • Names of normal arguments, ones that precede a *args or **kwargs item, in their declared order. From the example above, a and b.
  • Names of keyword-only arguments, if any. In the example, z
  • Name of a *args argument, if any (it doesn't have to be args).
  • Name of a **kwargs argument, if any.
  • Names of other local variables.

The varnames tuple is rather important because bytecodes that reference a local, such as STORE_FAST, have as their argument an index to this tuple. But the composition of the tuple is not drop-dead simple. The Code object method from_code() takes one apart and stores the pieces; and the to_code() method assembles the pieces to recreate a code object. And that had to change a bit for Python 3.

And now I've reminded myself, that my unit test suite lacks a test of function signatures with *args and **kwargs. I think I shall go and do one now...

Saturday, January 30, 2016

Byte-playing, a new project to doodle on

So. Been awhile.

Going to introduce a different project I've been messing with for a few weeks: byteplay3.

More on that in a minute. First the status of old projects.

PPQT2 continues to have one or two regular users, and a minor UI issue was posted a couple of weeks ago. No real bugs found; is that good news? It says something about the code quality; but I suspect it says more about how nobody is using it. I would be a regular user, if I were still doing PGDP Post-Processing. Unfortunately, the switch to EPUB with its unavoidable compromises on book quality has killed my interest in that. So I'm not PP'ing any more.

Anyway, I want to fix that UI issue and bring it up to the latest levels of its dependencies. Qt 5.6 is due out shortly, with PyQt5.6 to follow soon after. So when I can install those levels, I'll do the code update to the Find panel and rebuild on all platforms. That will probably be the end of PPQT2. Well, it was a most satisfying hobby project to design and build.

I remain a daily user of CoBro, and it definitely needs to be refreshed. It embeds the Qt WebEngine. Recently a couple of the comics I read have stopped loading, I think because they are insisting on a higher or different level of https encryption than this old WebEngine module supports. So I will also rebuild CoBro with Py/Qt5.6 and, one hopes, it will stop giving an obscure error when it tries to load Penny Arcade.

But that's all to do in a couple of months, whenever the Py/Qt upgrade happens.

The new project, byteplay3, needs to be at Python 3.5. I've been making do with 3.4 for a while, and there's no function in 3.5 that would benefit PPQT, but for byteplay I need to test against async coroutines and such.

Byteplay

The original byteplay, written by Noam Raph, was uploaded to PyPi in 2010. Briefly, the point of byteplay is to make it easier to diddle with the bytecodes generated by the Python compiler. You, gentle reader, are a highly competent Python user, so I will only show the meat of the example code. I'm sure you can figure out what's going on.

>>> def f(a, b):
...   print(a, b)
...
>>> f(3, 5)
    3 5
>>> from byteplay3 import *
>>> # convert code object of function f to a Code object
>>> c = Code.from_code(f.__code__)
>>> c
    <byteplay3.Code object at 0x1030da3c8>
>>> print(c.code)
2        1 LOAD_GLOBAL          print
         2 LOAD_FAST            a
         3 LOAD_FAST            b
         4 CALL_FUNCTION        2
         5 POP_TOP              
         6 LOAD_CONST           None
         7 RETURN_VALUE         
>>> c.code[4:4] = [(ROT_TWO,None)]
>>> f.__code__ = c.to_code()
>>> f(3,5)
    5 3

Short intro follows. If you understood perfectly what happened in that demo, skip ahead. Or for a longer explanation see the detailed "about" page that I've made (starting from Raph's original).

When Python processes a def statement or lambda or compile() expression, it compiles the source text into an internal form. The heart (though by no means all) of the internal form is a byte array, a string of "bytecodes" which represent machine instructions for a simple stack machine. The bytecode representation is binary and designed first for speed of execution and second for compact storage. Readability and post-compile editing were not goals of that design.

The standard module dis will display the bytecode of a function or compiled expression in much the same format as shown in the example. In fact, dis of Python 3.5 has a nice facility that lets you read a bytecode stream one instruction at a time from an iterator. (I think this preempts a couple of the usual uses of byteplay. But it still has some value.)

That takes care of displaying the bytecode of a function. But what if you want to modify it? In the preceding example, a ROT_TWO instruction is inserted in the sequence and that changes the function's behavior. More realistically, there are many opportunities for peephole optimizations, where you look for inefficient code sequences and shorten them. Ryan Kelly made the promise package based on the original byteplay. It provides decorators that optimize certain code sequences of your functions.

Or, I can imagine wanting to generate a bytecode sequence starting with some other notation. You could design some little Domain-Specific Language, and compile it down to bytecodes, and call it from a Python function. I'm considering doing a demo of byteplay3 in which I implement, say, Tiny Basic by compiling it into bytecodes. Python bytecode: the poor man's LLVM!

Kelly's promise module, and the original byteplay, are firmly dependent on Python 2. I thought it would be fun to bring byteplay, and maybe promise, into the world of Python 3. And that's what I've been doing in the odd spare hour for the past month or so.

This is long enough; I'll delve into some of what I've done next time.

Meanwhile ... if Noam Raph is out there? I'd love to talk! You aren't on Facebook or LinkedIn nor a user on github...