Listening To Reason

random musings about technologies by Andy Norris

09 May 2008

Awesome Game 3 from the Spurs

After two brutal games being ripped apart by the offensive juggernaut of the Hornets, I wasn't sure if I was going to be able to stomach Game 3. It was brutal watching the Spurs get ripped apart in Game 1, but how often is Duncan going to deliver a complete no show like he did in that game? And after hearing the report that Duncan had played with a 103 degree fever in Game 1, I figured that it was just a fluke game, and they would rebound and pull through. Instead, Game 2 was like Groundhog Day -- the same thing all over again.

So I didn't know what to think going into Game 3 -- were the Hornets simply a better team?

Instead, the Spurs completely kicked ass. The Hornets led for most of the first half, but the whole thing was close. But the Spurs completely broke things open in the second half, putting on an offensive clinic and getting crucial stops for long stretches. The Spurs' adjustments -- most notably using Bowen to completely own Peja -- caused a significant momentum shift that allowed the Spurs to control the game down the stretch instead of just trying to hold on.

Meanwhile, his stat line doesn't show it, but Duncan was the key to the offensive explosion, ripping the Hornets apart passing out of the double team. The Spurs were great at bringing it around the court quickly to find the uncontested 3. All the open 3s the Spurs were getting then forced the Hornets to stop packing the paint so tightly, and that broke things open for Parker and Ginobili to drive to the basket at will.

It will be interesting to see if the Hornets will keep bringing the double teams on Duncan in Game 4. Then again, the Spurs may not shoot 44% from behind the arc again, so they may be able to get away with it. Either way, we've got one victory out of the way, and we just have to find a way to get the second to take it back to the Crescent City tied 2-all.

Labels: , , , ,

Why Apple is REALLY more expensive than Amazon

Reg Braithwaite is one of the smartest bloggers out there, but I really don't agree with his latest post, Why Apple is more expensive than Amazon at all.

Setting aside the question of whether Apple is pro- or anti-DRM, the most problematic meme that is being spread (not just by Reg, but elsewhere, as well) is the idea that the music industry is using price fixing to undercut Apple. Quite simply, this is ridiculous.

Here's the bottom line: Apple has exercised complete control over item pricing on iTunes, both in terms of setting consumer prices and setting producer prices. They have a single flat-rate deal that they offer all music producers, and if you don't like that deal, your songs aren't on iTunes -- it's that simple.

The record companies want the ability to offer variable pricing -- higher on new hits, lower on catalog titles -- and Apple has refused:

The recording industry and Apple had been at odds over Apple's insistence to keep its flat rate with some labels wanting variable pricing, including higher prices for new releases.

“Apple has all the cards, and when you have all the cards, you can play hardball,” said Ted Schadler, an analyst at market research firm Forrester Research.
AP Wire, May 2, 2006

So while Amazon is fine with selling some songs for $0.99 and some for $0.89, Apple enforces their one-price-fits-all philosophy. Record companies actually can't offer tracks to iTunes for less, regardless of what they would prefer. And while Amazon may be operating at a lower margin on some items and a higher margin on others (as they do with books, CDs, and everything else they sell), Apple always takes the same margin on every song.

So, are the record companies obligated to sell Amazon tracks at the same price Apple has fixed? If so, why? Does Apple have the right to dictate pricing for all vendors, and not merely the price at which they will purchase? That doesn't even make sense.

Are the record companies trying to undercut Apple? Of course they are. But calling this price fixing is tantamount to saying that Apple has the right to set the royalty rate for the entire digital music industry. I hope people will stop drinking that particular Kool-Aid.

Labels: , , , , ,

29 April 2008

Spurs-Suns Game 4 Game Blog

First Quarter

It's 2 minutes in when ESPN cuts in from the Cavs win over the Wiz. Suns 5, Spurs 1. Bell hits a jumper, and it's 7-1. Pop calls time. After the break, the Suns are still rolling, and it's quickly 11-1. Suns have more intensity on both ends of the court. Finally, foul on Amare, and Oberto goes to the line and makes them both. Amare answers with some sick penetration, and gets the and-1. 14-3.

Interesting stat: 6 more games, and Horry passes Kareem for most playoff games all time. Wow.

Nash dribbles through the court at will, pulls up, and drops an easy jumper. All 5 starters for Phoenix have scored. Sloppy Spurs turnover, then a bad shot leads to an easy Barbosa basket in transition. 20-5, and the Spurs aren't even in this game.

One saving grace. Amare picked up his second, and Bell has 2. Spurs try to slowly claw their way back in, and take it up to 21-9, but then Nash hits a nice 3 to reassert their lead. The rest of the quarter kept spinning out of control, to wrap up 34-13. Ugly. It looks like the Spurs decided they deserve a game off after taking a 3-0 lead.

Second Quarter

Parker takes it to the hole for the first points of the 2nd. Good start. Ginobili follows with another easy layup, and the Spurs are getting stops. Well, better late than never. Maybe someone told the Spurs the wrong starting time for the game. Duncan slams one home, and it's 34-19 now. D'Antoni wants to regroup.

The teams trade baskets, then Bell hits a wide-open 3 to go up 39-21, but Duncan answers with another nice 2. He has the Spurs last 6, now. Oberto picks up his 3rd on the next possession. Shaq hits 1 of 2. Duncan hits yet another, on a nice bank shot, but Bell drills another 3 from the wing. Shaq called for defensive 3 seconds, and Fin hits the tech. Parker misses the teardrop, but Spurs pull down the offensive board. Another defensive 3-second call on Shaq, and another FT made by Fin. This is an epic possession, but they need to convert. But no -- Parker misses a jumper from the foul line. 43-27. Diaw makes a nice basket on a postup, but Duncan answers. Duncan has 12, now. Parker penetrates and draws the and-1 on Nash, but can't convert the FT.

Offensive foul on Shaq, and the Spurs get a chance to close -- Parker with a nice 15-footer from the wing. Bell has is a lethal 6 of 7 from the field for 15, but the Spurs have shaved the lead down to 47-35 when time is called. Still 5:15 to play in the half. If they can get it a little closer -- or at least keep the Suns from widening the gap -- the Spurs can totally win this thing in the second half.

Parker fouls Nash, who hits 2 from the line. The teams trade baskets, then Parker passes it out of bounds on the pick and roll, and Shaq gets an easy 2-footer. Ginobili steps out of bounds, Diaw takes it to the basket, and the Suns extend their lead back to 55-37, with 3:32 on the clock. Turnover by Tony, foul on Manu, his second. Bell goes to the line -- he's red hot. The Spurs started to play themselves back into this game, but now they're playing themselves back out. Maybe they forgot their brooms back in San Antonio.

Phoenix runs it up to 12 straight, and take a 24 point lead with 1:16 to play. Horry finally ends the run with a 3 from the corner, but it's still 61-40 with just over a minute. Parker does a good job drawing contact, but the Suns finish with a brutal 65-43 lead. Bell sums it up coming off the court: they were completely embarrassed after stinking the place up before, and now they're motivated to get their token game. That may not be quite what he said, but it's sure what I heard.

Third Quarter

Spurs don't come out of the gate playing any better in the third, so it looks like they've resigned themselves to waiting a game to close this series out. They can't buy a shot, and the Suns quickly go up 71-43. For reasons I can't fathom, Finley is on Amare, and he's getting beaten like a dusty rug. They're pretty content to just run up and down the court and trade baskets, and before too long, the Suns are up 83-61 with 3 minutes to play. They've cut the lead a little bit as the Suns lowered their intensity, but this doesn't look like a team that wants to make up 22 points in 15 minutes.

Suns come out shooting after the time out, and the Suns finish the quarter 93-65, with Duncan, Parker, and Ginobili on the bench. Bell has a game high 24, and Diaw has 20. I doubt we'll see the first team again -- this thing is over.

Fourth Quarter

Sure enough, Parker,s in the locker room to start Q4. In addition to his 20 points, Diaw has 9 rebounds and 7 assists. Hell of a game. He's come a long way since Game 2, when he was the worst player in the quarter. Parker comes back part way through with ice on his calf. The quarter pretty much plays out as garbage time, but it's good to get Horry, Barry, and Stoudamire some court time. Bonner's inactive, so it looks like we won't get to see him today.

Somehow D'Antoni gets himself thrown out with the double technical with 3 and a half to go. As you'd expect, he leaves to a standing O. Final score: 105-86. The sad part is that it wasn't that close. The Spurs don't seem too worried, but they need to drum some urgency back up if they're going to close this thing out on Tuesday.

Labels: , , , ,

25 April 2008

Spurs-Suns Game 3 Live Blog

First Quarter

Really active crowd. Both teams are pumped. The teams start out playing about equally, running it up to 6-6, which is far better than the Spurs have started in the first two games. Parker hits a nice midrange jumper -- if he can keep hitting those, he'll eb unstoppable. He' starts out 4-4, to make it 12-8 -- of course, Nash is "defending" him. Duncan hits a long 2 -- sweet. Slam from Timmy! Spurs are on a run! Parker hits a nice layup of the pick and roll. Only 6 moinutes in, Tony's 5 of 6, with 10 points and 4 assists. Their defense on the Suns has been pretty sharp -- they're holding the Suns to 36%. Spurs 18, Suns 8 when D'Antoni calls time.

Stoudamire and Shaq hit solid baskets coming off the time out, but they're not stopping the Spurs on defense -- whoops, spoke too soon. Nice team defense, forcing Bowen to take a shaky 3 with no time on the shot clock. The next trip down, Manu hits a 3 coming off a screen that just looks easy. Parker drills a 3 shortly thereafter. Diaw hits a midrange jumper, and Pop calls time to yell at Oberto for not bringing the help defense. Still, 27-12. Of course, I doubt the Spurs can keep up the 72% shooting pace.

Suns bust out the 2-3 zone coming off the timeout, but Amare immediately sends TP to the line. Converts 1-2 Hill gets a good look from behind the screen but can't convert it. Parker can't make the long floater land, and the Suns drive back down for an easy layup. Duncan loses his balance on the screen and picks up his second on an offensive foul. He sits. Crap alert: JV intentionally fouls Shaq up 28-16. WTF? Shaq has lane violations on both attempts. Oops. On the other end, Thomas takes an early 20-footer, misses, and we're back on the other end. More Hack-a-Shaq. Yuck. Shaq's 1-2 this time. On the other end, The Big Brick puts a hard foul on Manu in the paint, which gets a Standing O from the pissed Suns crowd. Manu makes them both. Diaw drops in an easy layup,Spurs get it back with 8 seconds or so, beautiful series of passes, JV sets up KT, who takes it to the hole and makes the and-1 with 0.3 remaining.

33-19 at the buzzer. Living large, shooting 61% to the suns 31%. Tony leads the field with a sweet 13-6 (the whole Suns team have 3 dimes), and Kurt Thomas has a surprising 7 points getting a rare start. Stoudemire leads the Suns with 6 points.

Second Quarter

Manu shorts the midrange jumper to start the quarter. Barbosa take it hard to the hole and gets the 3-point play. Ginobili makes a great hustle play to save an out-of-bounds ball, but the Spurs can't get a jumper to drop. Barbosa takes it to the hole again. Barry loses the ball out of bounds. Another shot from Barbosa, and it's 35-26. The Suns are on a run, and the zone seems to be working for them. Pop calls the TO.

Oberto with the turnaround J, and the Spurs end their drought. Barbosa is looking great with 10 points. Suns close to 37-31, but the Spurs get the next 4. Bell fouls Finley on the 3, and Findog hits them all.The Spurs seem to have put the Suns run on hold as they go back up by 13. Good defense by both teams for a few possessions. Parker called for traveling, which takes awaw the basket. It's already the Spurs 6th turnover, halfway through the 2nd. Timeout.

Spurs seem to be defending well, then Nash makes a sweet pass to Stoudamire for an easy finish at the basket. Bowen hits the 3. Stoudamire again, after about 4 great passes. Beautiful ball movement. Thomas pisses off the ref and gets T'ed up. Ginobili draws the charge and Diaw sits with his 3rd. Hill comes back in. Parker shorts the jumper from the top of the key, then can't get set in time in transition and fouls Bell to stop the 3 on 1. Bowen then stops the 2-footer to send Shaq to the line for an 0-fer. The ref must still be pissed at Thomas, because he gets called for a really iffy moving screen. Hack a Shaq strikes again. Lane violation on the missed first, but he makes the second. 46-39 with 2:56 to go. Shaq fouls Duncan near the basket -- his second. Duncan will shoot 2 after the commercials.

Stat: Nash is 7-21 lifetime in the playoffs vs. the Spurs. Ouch.

Duncan hits them both. Hill knocks one down. Ginobili takes a jumper from the top of the key, surrounded by 3 Suns, and drops it. Quick Suns miss, and it's Manu to Timmy for the finish at the basket! 20-second TO. Stoudamire drops a nice jumper, Barry responds with the 3. 57-43, inside a minute. JV comes in. More Hack-a-Shaq? Yep, but this time it's Horry. Shaq misses the both, but lane violation on Oberto on the second, and Shaq hits the makeup. Parker takes it to Nash and draws the foul. Two easy points. And they Hack Shaq again. Sigh. Shaq seems to have found his stroke again, and makes them both. Parker can't get it to drop, Amare takes it to the hole with time running out, and Barry gets called for the blocking foul. 4.2 remaining, Amare bricks the first, the second rolls in. Pop calls for the 20 to draw up a quick play. Parker splits tow defenders and hits the floater at the buzzer!

61-47 at the half. If they can stay focused for another half, they can pretty much put the series away. Parker with 19, 3 and 7 on the half, Duncan with 10 and 6, Manu with 9 and 5. Spurs are still shooting 57%, and outrebounding the Suns 23-18, while the Suns are shooting 43%. Spurs are 11-12 on free throws, Suns are 8 of 16, with Shaq 5 of 12. Amare leads the Suns with 15 and 6, Nash is held to 3 points, 4 rebounds on 1 of 4 shooting. Barbosa with 10, Shaq with 9 (5 from the line).

Third Quarter

A couple of misses to start the half, Hill lays a solid foul on Tony on a drive, and Tony hits them both. Tony misses the long 2, and Bell gets called for the offensive foul on the other end. Parker hits a sick float with Hill all over him, and converts the foul shot. 19-point lead. Amare draws the foul in the paint, but he can only convert the first. Parker gets surrounded and throws the ball away. Hill hits a nice midrange shot off the screen. Suns have a nic defensive possession, and Tony has to go with a bad look from 3. Duncan hits a great jumper. The Suns are looking sloppy on offense. Duncan hits another 18-footer, Suns call time. Spurs 70-50. It looks like the Suns have started to realize in their guts that things are spinning out of their control. Somebody has to get that team to pull together, or this is going to go in the books as one of the all time great choke jobs. You can hear a pin drop out in the stands about now.

Nash to O'Neal gets the Suns started again. Oberto somehow shorts a 3-footer. Amare finishes at the basket, and the crowd wakes back up from their coma. Parker hits a nice 15-footer off the screen. Shaq somehow misses an uncontested 4-footer while his man is on the ground, but gets it back off some funky bounces, and makes two off a foul by Oberto. Oberto answers on the other end, and Amare takes a lousy shot. Ginobili drills a three when his man plays off him, Amare answers by taking it inside for the layup. Udoka misses the wide-open 3 from the corner. 79-58, A lot of quick shots, and not much ball movement right now. Beautiful series of passes gives Duncan the ball at the basket with nobody in the same area code. Barbosa misses a breakaway layup in transition somehow, but there's more running back and forth, and Nash finishes the next drive with a layup. Sloppy play on both ends by the Spurs, and Pop calls time to remind everyone. 81-64, 3:13 to play.

Beautiful sets of passes, Ginobili in to Duncan, over to Oberto for the easy shot at the basket. Amare answers from the baseline. Bowen with the long 2. Some more quick back-and-forth baskets. Parker hits 2 more baskets, and he's at 30 now. Nash answers, and it's 89-70. Parker slows it down with the clock running down, but can't make it. Suns get the last shot, Nash penetration with the kickout to Amare in the corner, but he can't get it to drop. 91-72 with a quarter to go.

Spurs are shooting 58%, and the Suns just don't have a clue how to get stops. Tony has 11 assists to go with the 30 points, Duncan has 18 and 9, Ginobili has a quiet 14 and 5. Amare with 26 and 10, Shaq with 13, Barbosa with 12. Nash with 8 assists and 7 points.

Fourth quarter

As you can imagine, the crowd is pretty much in shock. Barbosa gets the first two points of the quarter. Parker knocks down yet another midranger from the wing. Barbosa follows with another 2 quick baskets, Parker answers, Barbosa answers again. 95-80. Duncan misses from the top of the key with the 24 running out. Bell goes to the line on a Finley foul, but misses them both. Kurt Thomas can hit a wide open midranger from the wing. Time out with 9 minutes to go, and the Spurs need to try and settle down and keep things under control.

Diaw hits 2 from the line, and it's a 13-point game. Ginobili misses from 3 early in the shot clock, and Diaw can't get a tough shot to fall when surrounded. Manu drills the 3, and goes back to the well on successive possessions. Manu just kills their rally when it's coming down to crunch time, and it's 101-85 with 7 minutes. Spurs are moving the ball well, and Thomas makes a nice baseline jumper. Shaq gets 2 to answer. Duncan misses the layup, but goes to the line. Makes them both,m and it's 18 points with less than 6 minutes. Duncan fouls Shaq on a basket, and Shaq converts. Flagrant foul, Shaq on Duncan. Duncan goes to the line for 2, then they get the ball. Duncan converts 1 of 2, and Manu converts the following play with a ridiculously long 3. 109-92 inside 5 minutes, and Suns fans are starting to leave. Duncan with a beautiful layup. On the other end, Shaq fumbles the ball, but gets fouled and converts 1. Inside 4 minutes now. Parker with the steal, Duncan misses the long 2 after the Spurs burn off the clock. Spurs aren't all that worried about scoring anymore.

Parker hits a couple more shots to finish things off, and both teams send in the second unit with a minute to go. Parker finishes with a demented 41, and the Spurs wrap things up 155-99. If you had told me that there would be garbage time at the end of this game, I would ever in a million years have guessed that it would be because the Spurs dominated. I thought this would be a tough 6 game series, but it looks like the Suns will just try to keep from being swept now.

These Spurs look like they want some more hardware to put up on the mantle. Assuming they can wrap things up here, I guess we'll get to see how they fare against a pretty tough field deeper in the playoffs.

Labels: , , , ,

23 April 2008

Spurs-Suns Game 2 Game Blog

My dad is currently in the Adriatic on a cruise, so in game 1, I gave him play by play of the last 2 minutes -- and then again for the last 2 minutes of each overtime. It was a lot of fun, but international minutes are pricey, and starting time for game was about 3:30 in the morning there, so he asked me to send him an email instead. So I decided to keep a live game blog and ship off the result.

I didn't start until the second quarter, after Tucker went to bed. Hope it's not too big a problem that this is kinda huge.

First Quarter

The Spurs can't buy a shot. Amare can't miss from range, which is getting them plenty of points and also opening up some easy shots for Shaq. Nash is making some beautiful passes. Ginobili gets the call early, but it seems to take him a few minutes to get warmed up. Suns open up a 14 point lead at one point, but the Spurs bring it back to 35-26 at the quarter mark, with some nice drives by Tony.

Second Quarter

Manu is on fire! 15 points after 12 minutes on the court. 6-9 from the field with 2 daggers from 3 and a beautiful breakaway dunk -- he had time to make a taco run before the Suns would have caught up with him.

The Suns regain the momentum after Ginobili takes a breather, and run the lead back up to 44-34 with a 6-0 run. Both Oberto and Thomas have 2 early fouls, so we could see the Red Rocket if they can't keep the fouls under control.

Halfway through the 2nd, Barry hits a 3, for the first points of the game not scored by Duncan, Parker, or Ginobili. Ouch. So much for the Red Rocket -- Big Shot Bob is on the court! Beautiful teardrop by Tony -- 11 points. Nash is unreal -- 7 assists already, and he's creating some awesome shots -- Phoenix is shooting 64%! Beautiful rejection of Shaq by Horry leads to a textbook second-chance layup by Timmy -- Suns call time with 3:11 left at 52-46. The Big Fundamental's already got 13 and 8. Amare has freakin' 20 already, going 9 of 11. Ack!

Shaq just got 11 -- their big men are killing us. 56-46 at the 2-minute mark. Time to make a run. Oh shit -- Shaq plowed right into Tony while Tony was running full-speed at the basket. Damn, that's got to hurt. He's up and shooting his FTs, but that was a brutal collision. Makes them both -- sweet!

Beautiful pair of passes plus an and-one makes it 59, then another teardrop by Tony answers it. Beautiful Manu penetration, where he clears space with a beautiful spin move and makes a jumper from just inside the free throw line. Travel on Barbosa on what would have been the last play, and the Spurs get it back with 6.8 seconds, but Ginobili can't convert the long foot-on-the-line 2 pointer. 61-54 at the half.

Could be worse, but we have to do a better job of taking away the passing lanes and making them take tougher shots in the second half. They shot 61% on the half.

Third Quarter

Finley makes 2 quick baskets, and suddenly it's 61-58 to kick off the quarter. Sweet -- a 24-second violation on Phoenix. Finley scores again, and it's 61 all, with the Spurs on an 11-0 run dating back to the 2nd. Bell does a nice job to draw a charge on Tony. KT picks up his third foul defending Shaq, but the Big Brick can't make either one. Tim draws Shaq's second on some pathetic pick and roll defense. Crap -- Timmy misses 2 from the line. Still 61 all. Tony strips Shaq, but they can't convert yet again. Long scoring drought. Beautiful Bowen pass to Finley on the cut gives the Spurs their first lead!

Suns call time and regroup. 5 minutes into the quarter, and they don't have a point yet.

Spurs smother the pick and roll and Nash has to kick it out for a desperation 3 as the shot clock runs down. No good. Ginobili for a long 2! Nash finally gets a nice pull-up jumper to break their drought. Shaq goes out for Diaw, Duncan goes out for Udoka -- small ball. Great pursuit on defense gets a slick turnover. Nice second-chance hustle layup for Udoka. Another Suns TO leads to a fast break, foul on Diaw sends Udoka to the line. Udoka hits them both to take it to 69-63. Duncan's back on the court, gets the rebound on a crappy Suns shot 5 seconds into the shot clock to start the break. Ginobili to Udoka for the finish! Suns call time. 17-2 Spurs on the quarter. Doesn't get any better than that!

The Suns came into this game still confident that they were the better team in this series and that they should have won the first one, but it looks like they're living through their worst nightmare out there. They seem to be remembering that the Spurs completely own them in the playoffs, and falling apart accordingly.

Suns manage to regroup after the timeout and Shaq gets 2 quick baskets. 71-67. Scramble for the ball on the next Suns possession, Ginobili and Barbosa go to the floor, Shaq picks up his 3rd foul. Sick play -- Ginobili goes over Shaq, it doesn't drop, Timmy follows him and finishes at the basket -- beautiful. Spurs are intentionally fouling Shaq, but I don't think it's helping. 75-70. Sick fullcourt pass, Duncan to Udoka. Shaq 5 of 6 from the line on the intentional fouls. Time to give it up. Ginobili makes 2 from the line, 79-72. Final seconds, Spurs ball, Bell commits wrong early and grabs Ginobili to send him to the line. Manu hits them both, Suns can't convert with 6.1 left and a contested 3 from Giricek.

81-72 at the end of the quarter. Looking good, knock on wood.

Ginobili with 24, Parker with 22 points, 5 assists, 3 steals, Duncan with 15, 16, 4 assists and 3 blocks. 48% from the field, 3 of 8 from behind the arc, 15 of 20 from the line.

Amare 25 and 4, Shaq 18 and 8 with 4 blocks, Nash 12 and 9 assists. 49% from the field, just 2 of 7 from the arc, 10 of 15 from the line.

Fourth Quarter

Amare gets fouled by Manu, and converts 1 of 2. Ginobili with a huge 3! 11 point lead! Phoenix calls time only 40 seconds into the 4th. Suns can't box out for crap. Oberto reels in the second chance on a poorly set Bowen 3, and Parker drives it home with the teardrop.

Another steal by Parker. Spurs are moving the ball incredibly well, and the Suns have nowhere to go with the ball right now. ESPN's David Aldridge is basically laughing at the Suns on voiceover for giving away Kurt Thomas. Shaq misses 2 from the line, and Parker takes it to the hole yet again. 90-73, and the Suns look lost like someone dropped acid in their Gatorade.

Suns try to get it together by pushing the ball harder, but it looks sloppy. Stoudemire gets to the line for 2, though. Beautiful pullup by Parker at the line. 94-77. Beautiful screen sets up an easy Nash 3.

Stoudemire misses on a good look near the free-throw line. He hasn't made a shot from the field in the second half. Spurs look a little sloppy out there now, like they're ready for it to be garbage time. But there's 6:22 left and even though it's a 13-point game, Phoenix can catch up if the Spurs let them back in.

Suns defense has really tightened up. There's a lot of fouls right now. Phoenix has only converted 4 of 10 from the line in the 4th -- can you say choke?

Yet another layup for Parker, who has 30 now. 96-85 with 4 minutes. Fastbreak points: Spurs 23, Suns 3. Wrap your mind around that one. Stoudemire defends Duncan really well on the baby hook near the basket. Bell and Nash convert from the line, and it's 96-89 with 3:16 to play. Spurs need to stop this run. Nash gets a nice pull-up (9 points in the 4th), and it's a 5 point game. Duncan goes to the line on an Amare foul that could have gone either way. Time out, 2:16 to play.

Tim misses the first, makes the second. We need a stop here. Sweet -- Diaw posts up Finley, but he travels. Spurs ball.

Hoooooly fuck. Ginobili penetrates, then flips a behind-the-back pass to Duncan for the easy 6-footer. Nash answers, then Parker hits the long 2 with 5 seconds on the shot clock. Nash misses the long 3. 101-93, Spurs run down the clock. Spurs lose the ball, but it's an 8 point lead with 22 seconds as they go to timeout. I think the fat lady is clearing her throat on this one.

Stats: Spurs +14 in the paint, Suns shoot 27% from the field in the second half.

Amare hits a nice contested 3. 5 point lead, 14.3 to play, Phoenix presses on the inbounds and Duncan calls time. Bell, hard foul on Ginobili, but they don't call the flagrant, which would have been a killer. Ginobili hits 1 of 2. Suns miss 2 3 pointers on their last possession, then Parker dives to the floor and wraps up the ball. Buzzer.

Final score: 102-96.

Now, the Suns are going to go back home and listen to 3 days of criticism from the media about how the whole point of the Shaq trade was to beat the Spurs, and yet here we go again. Spurs just have to make sure they don't lose focus and start thinking ahead to Chris Paul or Kobe.

Labels: , , , ,

02 April 2008

All too true


see more hipster robot webcomics and pixel t-shirts

Labels: , ,

05 March 2008

Dispatch from MIX08, Day One

Greetings from Vegas! The weather's pretty nice (highs in the 60s) here, though I haven't been outside much. I'm not all that impressed with Vegas, but the Venetian (where the conference) is very nice. In particular, the canals on the shopping level are very impressive.

I have a little while before the first breakout session, so I'm going to take a little while and write down some thoughts on the keynote, and try to finish it as I get a chance. For starters, there was some ridiculously cool stuff! When Microsoft wants to put on a show, they definitely can.

Before the keynote, they had a 15 year old kid from Seattle up on stage for half an hour who sounded like a dead ringer for Johnny Cash. He was all glammed up in a sparkly Vegas perfromance outfit no less. Spooky, but impressive.

Ray Ozzie led off, but he talked about some fairly nebulous stuff with no product announcements. He said there was a bunch of stuff they would be announcing, and that it would all be on the table by PDC this fall. One thing he talked about was having a central way to control all of our various devices, sync data, etc. using the web. It could be an interesting initative if it's executed well. He also committed to converge server apps so you get the same functionality whether you maintain servers for things like exchange and sharepoint or you subscribe to equivalent services over the net. Very nice, though it's still vapor at this point.

After Ray wrapped up, Scott Guthrie took over the stage to direct the rest of the show. Scott is the guy responsible for dev tools and web stuff, from IE to .net to silverlight. He first went through IE8, which released its first beta today.

IE8 looks like a giant win -- finally. It's a full implementation of css 2.1, and they even submitted over 700 test cases to the w3c for interoperability testing and to solicit feedback. They also implemented some sweet HTML5 features, including one that makes the back button work with AJAX apps, and a feature for rich internet apps where a page can keep track of whether it's connected to the server, and even save itself on your system so you can reconnect to the server after you get a connection back. Pretty slick. Also touted were a 2.5x JavaScript speedup, a sweet-looking web debugger, and a css style tracer that allows you to pinpoint the css rule that's responsible for a particular prperty of a particular region.

After IE8, everything was Silverlight, and the demos were crazy impressive.

They started with media streaming stuff, which isn't stuff we're as interested in. The demo was just unreal however: they had a guy from NBC Sports up there showing off what they're planning to do with Silverlight for the Olympics. They're going to have 2200 hours of video available for live streaming as they film it, so if you're a fan of something obscure, you're not limited to the tv feed. It will also all be available later to watch as on-demand, with keypoints that make it easy to jump forward or backward. They even demoed picture-in-picture, as well as a track and field viewer that allowed you to switch between 4 different camera angles. It's hard to describe in words how cool this was to see.

Then they moved on to talk about rich internet app development in Silverlight, which is our main area of interest. The first beta of Silverlight 2 is out today, which features multilingual, networking (including cross-server), and a client-side data store. S2B1 allows you to code in 5 languages: JavaScript, C#, VB, IronPython, and IronRuby. It's only 4.3MB, so it should be an easy download.

Silverlight appears to come with a boatload of XAML controls out of the box. The app building demo was pretty nice, because it allows you to build the structure of an app in Visual Studio and wire everything up, then import it all into Expression Blend (a tool for graphic designers) and completely reskin the look and feel. In addition to basics like colors and fonts, you can insert images,transition animations, and other elaborate graphic niceties. If this works in the real world, it looks like it could really facilitate collaboration a lot better than traditional UI tools.

After demoing the design tools, they moved on to more of the slick stuff. There was an incredible demo for Hard Rock Cafe where they had taken their massive memorabilia collection and put it online. Using an incredible Silverlight feature called Deep Zoom, it allowed you to zoom and pan over a massive *2 billion pixel* image set. You could browse a massive set of images fluidly (including resorting it as well as panning all through it and zooming into it. The level of detail of some of the images was crazy. There was an image of a guitar owned by Bo Diddley, and you could zoom in to see the coil on one of the guitar strings, and even a fingerprint on the guitar. It was so crisp, it really was like looking at the actual guitar. With a magnifying glass.

The next demo was a site for Astin Martin. You could spin a car around and view it from all different angles, as well as change the colors. It wasn't just retinting an image -- apparently it was actually renderng on the fly, including light reflection and a lot of other gorgeous details. Then they took you into the interior, using Deep Zoom again. The interior picture was something like 18GB, if I remember correctly, and you could zoom in so far you could see the texture of the upholstery fabric.

The final demo for Silverlight RIA was a vertical app. Being MIX, it naturally wasn't an app for a bank or something. It was for Cirque du Soleil. They had an online + offline app built in both Silverlight and desktop WPF. They had some really showy control designs, including a gender selector using body silhouettes. It also had plenty of traditional controls as well -- Silverlight looks like a natural for browser-based enterprise apps.

The last area they covered was Silverlight for mobile. A partnership with Nokia was announced yesterday, so Silverlight is coming to both Windows Mobile 6 and several Nokia lines, including S60, S40, and their Linux internet tablets. The demo was an app called MIXr, which was a touchscreen social networking app designed to keep friends in touch and answer the question "where's the party at?" You could select a mood like relaxed or upbeat, search for nearby locations that matched the mood, and see what friends were there. Cute demo.

I went to a folowup session on Silverlight for Mobile at 1:30, and found out some additional details. Silverlight 1 should hit CTP in a few weeks, and go live in Q4. Silverlight 2 is scheduled to release a CTP in Q4 and get a full release in Q2 2009. It's a full version of Silverlight with the same code that's in desktop except for codecs. By sticking with the codecs that are native to Windows Mobile, they got the Silverlight install down to 1MB -- plus some codecs won't run on phone hardware anyway. Because of the codecs, the differing screen sizes, and the practical limits on media size, it looks like Silverlight app builders will usually need to build special versions of theo Silverlight apps for mobile. Still, Silverlight everywhere looks like a great deal.

Labels: , ,

06 February 2008

The .Net DLR makes building a new language implementation ridiculously easy

Making language implementations easier

I've noticed that the greater population of working programmers seems to consider designing and implementing a new programming language a black art that only large-corporations or super-gurus know how to do. And really, it has traditionally been the case that an enormous amount of work is involved.

In the past, you had to know how to use parsing and scanning tools like lex and yacc (or you built your own recursive descent parser from scratch), you had to use tree transforms to build an intermediate representation, then you either had to build a virtual machine that could run on the intermediate representation, or you had to compile the intermediate representation down to machine code. And, of course, this isn't even counting things like various forms of optimization and type validation.

Even if you understand the theory involved very well, building out a language implementation like this takes a ton of work. Fortunately for potential language designers, new tools and technologies have started making things easier.

Step 1: Reusable Virtual Machines

The existence of widespread, reusuable VMs like the JVM and the .Net CLR have made life a lot easier for language implementers. Instead of having to design your own VM and implement things like garbage collection, JIT compilation, and virtual method dispatch yourself, you can just use a pre-existing implementation that has the virtues of being well-tested and highly optimized.

The advent of these VMs has led to a great variety of language implementations for them, such as Scala, Groovy, IronPython, JRuby, and F#. But even with a virtual machine, there's still a fair bit of work left for a language implementer to do. You still have to do scanning and parsing, and you still have to generate all that bytecode from your abstract syntax tree.

Step 2: New Parsing Technologies

Of course, if you've been following parsers, there are a lot of cool technologies for making parsers a lot easier. Parser combinators such as Parsec make parsing a lot easier.

In fact, there are quite a few ways to put together a parser fairly easily these days. I saw several last week at Lang.Net 2008. Roman Ivantsov presented a very cool new toolkit called Irony that makes writing a combined parser/scanner about as easy as writing a BNF grammar. Harry Pierson presented some cool work he'd been doing with Parsing Expression Grammars in F#, and also was nice enough to suggest I go check out FParsec, an F# port of the Haskell Parsec library.

I'm planning on spending some time trying out all of these over the coming months, to try and see which is the best fit for how I work. I'll try to post blogs as I work through some of these.

So that simplifies two out of the three hard parts, but what about building bytecode?

Step 3: The DLR is awesome for generating bytecode

The DLR contains a general-purpose system of expression trees (a superset of the LINQ expression trees, if you're curious) that allow you to define not only simple expressions like x + 2, but statements such as variable assignment, function definition, control flow (loops and branches), and so on.

Once you construct a DLR expression tree out of these common constructs, it handles all the work of rendering your code to IL and just-in-time compiling it. In other words, once you build an abstract syntax tree, you're done.

Martin Maly from the DLR team has started publishing a lot of useful information on his blog on how to work with the DLR, but the easiest entry point may be to download the latest IronPython 2.0 alpha (which includes the DLR) and play with the ToyScript sample. IronPython, the DLR, and ToyScript are all released under the open source Microsoft Permissive License, so feel free to reuse anything you see for your own purposes.

To give you some idea of what I'm talking about, here's the toyscript source for taking a binary operation expression off the parse tree and constructing an equivalent DLR expression tree.

protected internal override MSAst.Expression Generate(ToyGenerator tg) {
    MSAst.Expression left = _left.Generate(tg);
    MSAst.Expression right = _right.Generate(tg);

    Operators op;
    switch (_op) {
        // Binary
        case Operator.Add: op = Operators.Add; break;
        case Operator.Subtract: op = Operators.Subtract; break;
        case Operator.Multiply: op = Operators.Multiply; break;
        case Operator.Divide: op = Operators.Divide; break;

        // Comparisons
        case Operator.LessThan: op = Operators.LessThan; break;
        case Operator.LessThanOrEqual: op = Operators.LessThanOrEqual; break;
        case Operator.GreaterThan: op = Operators.GreaterThan; break;
        case Operator.GreaterThanOrEqual: op = Operators.GreaterThanOrEqual; break;
        case Operator.Equals: op = Operators.Equals; break;
        case Operator.NotEquals: op = Operators.NotEquals; break;

        default:
            throw new System.InvalidOperationException();
    }
    return Ast.Action.Operator(op, typeof(object), left, right);
}

Operator is a ToyScript enumeration of all of the supported operators, while Operators is the DLR enumeration of all supported operators. And, of course, if your language supports some exotic operators that don't exist in the DLR, you can just generate a function call.

Doesn't get much simpler than that, does it?

So if you have a cool idea for a new language and you're not allergic to the .Net platform, consider trying out the DLR. You'll spend a lot less time implementing the back end for your language, which will leave you more time to concentrate on whatever interesting new characteristics you want your language to have.

kick it on DotNetKicks.com

Labels: , , , , , , , , , ,

30 January 2008

Some potential advantages of Arc

To me the (potentially) most interesting thing about Arc is that it takes a language family that is stuck and has plateaued in popularity and offers it a reboot along lines that are similar to other successful insurgent languages.

I'm not a Lisp expert, but here are my impressions:

1. Common Lisp is bloated, antiquated, fractured between implementations, and nearly impossible to improve except on a per-implementation basis.

2. Scheme is a much better language, but it's even more balkanized than Common Lisp, and the future of the language is being handled by a committee, which is rarely a good sign.

3. Many (most?) successful insurgent (i.e. not launched by a major corporation as a product strategy) languages have a single person who occupies the BDFL position. Smalltalk, Perl, Python, Ruby, and Erlang are all examples. Of course, counterexamples include Haskell and Scheme.

Rebooting Lisp with a new system started from scratch, having a single spec, and a single BDFL running the language is potentially a very good idea.

Sure, there's not much there right now, as everyone has complained, but there's enough there for a few motivated people (who can live with ASCII for now) to play around with, which is all you need for a version 0.01. If this is all there ever is, it obviously won't amount to much. On the other hand, if the community of interested people provide feedback and code and Paul starts building and/or blessing new libraries and code chunks for doing various tasks -- things like networking, data access, and, yes, internationalized strings.

People have talked about the social problems of Lisp for a long, long time. A lot of people (yes, I'm raising my hand here too) think this is the real reason Lisp has never taken over the world even as many of the ideas that Lisp pioneered become core components of more popular languages. What could be a better hack to fix a community with social problems than using a new language variant as an excuse for starting a new community?

Labels: ,

06 May 2007

Languages Matter

Giles Bowkett is a pretty smart guy, but in his recent post, "Languages Only Do So Much," things run off the rails a bit (no pun intended, for you Ruby fans).

I'm going to pass over his trainwreck of a religious metaphor pretty quickly, except to point out that it's awfully improbable to count the argument that virtually all cultures have a belief system that obeys highly similar principles as a warrant for believing in atheism, a belief system that stands in stark contrast to all of them. Surely it would be more plausible to count it as justification for some sort of religious unificationism. But like I said, let's set that one aside.

The real problem with this post is the notion that if you develop a personal style, you should write more or less the same code in any language. Should good programmers really write more or less the same code in Haskell, Smalltalk, and C? That seems awfully silly to me. They're good at vastly different things, so if you're going to write the same code in all of these, you might as well pick whichever language supports your idiom the best, and run with it.

I'd like to offer a counterproposal: learning and choosing languages is part of developing a personal style. Now, if you're picking between Python and Ruby -- or Java and C# -- the languages may be close enough that you can develop a style that works for either one and switch freely based on the requirements of a particular project. But some languages are very intentionally different.

There's no particular reason to write a web server using the same idiom you use for shell scripting. In fact, I think it's pretty silly to stick with exactly the same idiom in both places, because you're doing very different things. Depending on what your languages of choice are, it's perfectly natural to choose different languages and idioms for these different tasks.

Some of the different things you might need to do in a programming career include systems programing, shell scripting, web servers, web clients (i.e. JavaScript), desktop clients, application servers, scripting for extensible applications, problems that decompose most cleanly using objects, problems that decompose most cleanly using functional programming, and perhaps problems that decompose most cleanly using procedural programming. Certainly not every programmer will need to do all of these things, but most people will need to do a number of them.

It's unlikely that a single language (and likewise, a single idiom) will be an ideal tool for every problem area you face. In fact, I'll go further: trying to use a single language for everything is almost always a terrible idea. Perl started life as a brilliant scripting language, but now it's limping along as a somewhat broken general-purpose programming language. Java tried to be the One True Programming Language, and developed a framework so large that no one who wants to retain their sanity will try to learn all of it, and there is now vast interest in new JVM languages -- Jython, Groovy, Scala, JRuby -- that will cleanly interoperate with all the legacy Java code but are less painful to use.

So here's what I think: by all means, develop your own personal style. All good programmers will do this. But part of your style should be choosing the best tool for the job. If you write shell scripts, figure out what language and idiom are most effective for what you like to write, and run with it. If you write web applications, use whatever suits your style there, as well. Depending on what languages you choose, these may or may not be the same language. If you like Python or Ruby you may like them for both, whereas if C# is your language of choice for web applications, you will probably pick a different language for shell scripting. Powershell, perhaps. Even if you do use, say, Ruby for both, it is likely that you will use a somewhat different style for shell scripting than you use for Rails applications.

If OCaml is your language of choice, you may well choose it for both object-oriented design and designing functional code. If you prefer Haskell, then either you will need to pick a different language for OO, or simply choose not to use the OO idiom at all. If you write Java, you may want to mix Scala into your code to alow you to use functional programming. Likewise, F# offers similar opportunities for .Net. Some of the other languages for JVM and CLR offer additional choices for occasions where dynamic typing may be more efficient than static typing.

My point overall is that for many -- perhaps most -- programmers, a suite of languages that fit together well for you may be a more effective choice than a single language. And even if you find that a single language works for you, it will pay to learn a variety of appropriate idioms for different tasks. Languages matter, and imposing any one idiom on a variety of them is rarely the ideal.

Labels: ,

04 March 2007

A Simple JavaScript Command Line Interpreter for Windows in JScript.Net

I've been playing around with JavaScript quite a bit lately, and one thing I enjoyed having was a web-based JavaScript shell for trying out JavaScript commands quickly. I was also thinking about the fact that my command-line skills have gotten a little rusty lately, and that I should get those back into shape. I remembered JScript.Net, which I've been meaning to read more about anyway as a possible solution for embedded scripting in .net applications. So I decided to write a simple interactive command-line interpreter, and see how far I got.

Working with JScript

The JScript compiler, jsc ships with the .Net SDK. If you Have Visual Studio (I've tested this casually with both 2003 and 2005), you can launch the Visual Studio command prompt and just type jsc foo.js to get it to compile jscript. The resulting executable will run against the .Net runtime, and unlike C# or VB.Net, it doesn't require you to create a class with a main function. In fact, here is "Hello World:"


print("Hello, World!");

That's all there is to it.

If you save it as hello.js and enter jsc hello.js, it will compile to hello.exe, and you can then type hello at the command line to run it. Dead simple.

That's the good news. The bad news is that the JScript.Net compiler is configured to be much more .Net-like than any other JavaScript compiler you're likely used to dealing with. For example, suppose you create the following program, and save it as bar.js:


function bar() { 
  print(5);
}

bar = function() {
  print(9);
}

Straightforward, right? It declares a function bound to bar, then changes bar to point to a new function. Well, if you compile this with jsc bar.js, it will throw an error at you:

bar.js(5,1) : error JS5040: 'bar' is read-only

In fact, there's enough annoying limitations that the obvious conclusion to draw is that JScript.Net is flatly broken.

Fortunately, there's a way around all the limitations. The reason it's so broken is that JScript is trying to produce code that will run reasonably quickly on the .Net CLR. And as many people have noticed by now, there aren't a lot of affordances in the CLR for dynamic languages. This is called "fast mode," and it's enabled by default. But if you care more about compatibility with the ECMAScript spec than speed, you can turn it off.

If you type jsc /fast- bar.js, then it will compile just fine, and pretty much all the features you expect will be there.

A Simple Interpreter

Once I figured out how to make the full JavaScript language compile, it was pretty easy to make an interactive interpreter. The core of one is ridiculously simple:


import System;

for ( ;; ) {
  Console.Write("%");  // prompt

  var input = Console.ReadLine();

  if(input == "quit" || input == "exit") { break; }

  try {
    Console.WriteLine(eval(input, "unsafe"));
  } catch(ex) {
    Console.WriteLine(ex.ToString());
  }
}

This is really all there is to a read-eval-print loop.

I refined it a little from there, with support for multiline statements (by ending with a backslash, which is a little cheesy, but I don't think I can support something like shift-enter without using a more elaborate IO methodology).

I also added a load command, so you can bring in libraries from file. The load has to be handled specially, so that the resulting code will be in the global namespace and not inside a closure, where the bindings will be lost on function exit. I think I can do better than the current implementation, however. load("baz.js") will load a library "baz.js" from the working directory. load("c:\\scripts\\baz.js") will do the obvious thing -- you should be able to use any path you choose to declare.

Finally, I added a cmd() function, that will execute its argument in the windows command shell, and return stdout and stderr to you. As with load, the argument will be evaluated like any other javascript expression, so if you do something like:

var xyzzy = "directory";
cmd(xyzzy.substr(0,3))

then xyzzy.substr(0,3) will evaluate to "dir", and the command will list the working directory. Of course, you can also do things like

%var dir = function() { return cmd("dir"); }
function() { return cmd("dir"); }
%dir()

which will then list your directory. So if you have common tasks like listing directories, listing file contents, etc., you can just map them to javascript functions for easy use. Obviously, you could also map ls(), if you prefer Unix-style commands. Of course, with the parentheses, it's more verbose than using a real shell -- or even a language like perl -- but it still makes general command line functionality fairly accessible.

There are obviously plenty of ways in which the code could be enhanced beyond this, but this seemed like enough to pass along to anyone who might be interested. If you have ideas for additional functionality, let me know. Or, for that matter, implement it yourself -- it's a simple code base. If you do anything cool, I'd love to hear about it.

The source

I don't have anywhere to post this for file download, and it's short, so I'm just going to post it here, inline. If you save this as "ijs.js", you can compile it with

jsc /fast- ijs.js

Then you can just run it at the command line with ijs.

Here's the source in full. It clocks in at under 100 lines at the moment:


// ijs.js -- an interactive javascript interpreter for Windows in JScript.Net
// Copyright 2007 Andrew Norris
// Anyone is free to use, alter, or redistribute this code for any purpose.

import System;
import System.Diagnostics;
import System.IO;

function loadFile(input) {
  // pull out what's inside the parens
  var begin = input.indexOf("(");
  if (begin == -1) {
    Console.WriteLine("syntax error: bad argument to load");
    return null;
  }
  var end = input.lastIndexOf(")");
  if (end == -1 || end < begin) {
    Console.WriteLine("syntax error: bad argument to load");
    return null;
  }
  var fileExpr = input.substring(begin+1, end);

  var fileName = "";
  try {
    fileName = eval(fileExpr, "unsafe");
  } catch(ex) {
    Console.WriteLine(
        "Exception evaluating file expression '" + fileExpr + "':\n" + ex.ToString());
    return null;
  }

  try {
    var file = new StreamReader(fileName);
    var result = file.ReadToEnd();
    file.Close();
    return result;
  } catch(ex) {
    Console.WriteLine(
        "Exception loading file '" + fileName + "':\n" + ex.ToString());
    return null;
  }
}

function cmd(cmdName) {
  var process = new Process();
  process.StartInfo.UseShellExecute = false;
  process.StartInfo.RedirectStandardOutput = true;
  process.StartInfo.RedirectStandardError = true;
  process.StartInfo.CreateNoWindow = true;
  process.StartInfo.FileName = "cmd.exe";
  process.StartInfo.Arguments = "/c " + cmdName;

  try {
    process.Start();
  } catch (ex) { 
    Console.WriteLine(ex.ToString());
  }
  Console.WriteLine(process.StandardOutput.ReadToEnd());
  Console.WriteLine(process.StandardError.ReadToEnd());
}

Console.WriteLine("Welcome to Interactive JavaScript.");

var cont = false;
for ( ;; ) {
  Console.Write("%");  // prompt

  var input = (cont) ? input + Console.ReadLine() : Console.ReadLine();

  // trim leading and trailing whitespace
  input = input.replace(/^[ \t]+/, "");
  input = input.replace(/[ \t]+$/, "");

  // end execution on quit
  if(input == "quit" || input == "exit") { break; }

  if (input.charAt(input.length-1) == '\\') {
    // lines ending in backslash continue on the next line
    input = input.substring(0,input.length-2);
    cont = true;
    continue;
  } else if(input.match(/^load\W/)) {
    // load file to eval
    input = loadFile(input);
    if (!input) { continue; }
  }
  cont=false;

  // eval the input
  try {
    Console.WriteLine(eval(input, "unsafe"));
  } catch(ex) {
    Console.WriteLine(ex.ToString());
  }
}

Labels: , , , ,

13 September 2006

A proposal to simplify calling L# from C#

Note: this has also been posted to its own thread in the LSharp Google Group. If you want to discuss the merits of this proposal, please consolidate all comments there. Thanks.

Calling L# from C#

Right now, it's much harder than it needs to be to call L# code directly from C#. Since one obvious use of L# is as a scripting language for .Net applications, it would be nice if it was really simple. Of course, one good way to make this happen would be with an L# compiler that produces a .Net assembly that can be called from any .Net language. However, as useful as this would be, it still might not be ideal for some application scripting scenarios. In some situations, it may be more appropriate to use the interpreter (which already exists), and simplify the interface to make it easier to call from C# and other languages.

Suppose you have a lisp environment already running, and you want to call a simple function you have defined. The easiest way to call it right now is probably to create an L# string and evaluate it, like so:


object result = Runtime.EvalString("(my-function)",
    new LSharp.Environment());

This isn't a very clean way to work with code, but it should work in simple cases. In more elaborate cases, building correct strings gets to be an increasingly elaborate problem. And really, even if it weren't that hard, marshalling data to and from strings for interoperation isn't an especially good practice.

The other way to call L# code from C# is to work with the actual L# objects: symbols, functions, closures, and so on. Here is some simple example code for calling the system function map and the user-defined function (technically, a Closure object) concat from C#:


LSharp.Environment env = new LSharp.Environment();
Console.WriteLine(Functions.Load(new Cons("mylib.ls"),
    env));

// build the arguments for map
ArrayList myarraylist = new ArrayList();
myarraylist.Add(env.GetValue(Symbol.FromName("double")));
Cons intCons = Cons.FromArray(new object[]
    { 1, 2, 3, 4, 5 });
myarraylist.Add(intCons);

// call map
Cons args = Cons.FromICollection(myarraylist).Reverse();
Function map = (Function)env.GetValue(
    Symbol.FromName("map"));
Console.WriteLine(map(args, env));

// call concat
object[] myarray = { "foo", "bar", 42, "baz" };
args = Cons.FromArray(myarray);
Closure concat = (Closure)env.GetValue(
    Symbol.FromName("concat"));
Console.WriteLine(concat.Invoke(args));

The library that is being loaded, mylib.ls is simply:


; mylib.ls -- simple sample library
;
; Copyright (c) 2006, Andrew Norris
; Simple license: reuse this however you like.

(= double (fn (x) (* x 2)))

(= concat (fn (&rest items)
    (let sb (new system.text.stringbuilder)
      (foreach elem items
        (call append sb elem))
      (tostring sb ))))

When the above C# code executes, it will output:


(2 4 6 8 10)
foobar42baz

Using the L# interpreter objects directly is a better idiom in general, because it doesn't require data structures to be marshalled as strings, and because it allows for some things to be checked at compile-time. However, it's obvious that this code is pretty cumbersome, if all you want to do is call a couple of functions.

A side note: the ability to call a couple of trivial functions like this generally isn't worth the trouble of embedding the L# interpreter in your C# application. However, there are some things that may be much easier to write in L#, such as list manipulation functions or code that uses macros effectively.

1. Add a string indexer to Environment

It would be nice if it were easier to access symbol values in the L# Environment. C# doesn't have symbols, so you have to create one from a string. The GetValue call adds some an additional operation as well. This seems like a good place to start in making it simpler to call L#.

The current method of dereferencing a symbol isn't that complex an operation if it's called occasionally, but working with symbols in the environment is one of the two most basic operations in L#, along with calling a function. To be usable, it needs to be concise. A string indexer makes this code much simpler:


LSharp.Environment env = new LSharp.Environment();
Console.WriteLine(Functions.Load(new Cons("mylib.ls"), env));

// build the arguments for map
ArrayList myarraylist = new ArrayList();
myarraylist.Add(env["double"]);
Cons intCons = Cons.FromArray(new object[] { 1, 2, 3, 4, 5 });
myarraylist.Add(intCons);

// call map
Cons args = Cons.FromICollection(myarraylist).Reverse();
Function map = (Function)env["map"];
Console.WriteLine(map(args, env));

// call concat
object[] myarray = { "foo", "bar", 42, "baz" };
args = Cons.FromArray(myarray);
Closure concat = (Closure)env["concat"];
Console.WriteLine(concat.Invoke(args));

As you can see, it doesn't affect the rest of the code, but it makes recovering the functions from the environment much simpler.

Adding a basic string indexer to Environment is simple. The code looks like this:


public object this[string s]
{
    get { return GetValue(Symbol.FromName(s)); }
    set { Assign(Symbol.FromName(s), value); }
}

Of course, this only gets things started. There are a lot of other things we can improve as well.

2. Simplify the interface for converting a .Net data structure to a Cons

The interface for converting a collection of items to a Cons can usefully be simplified. Cons contains some useful conversion functions, and SpecialForms.The() does a good job of wiring them up. But, really, anything that can be enumerated can be easily converted to a Cons, if we add a new method. Cons.FromIEnumerable() is simple, and virtually identical to Cons.FromICollection():


public static Cons FromIEnumerable(IEnumerable enumerable)
{
    object list = null;
    foreach (object o in enumerable)
    {
        list = new Cons(o, list);
    }
    return (Cons)list.Reverse();
}

This doesn't have a significant impact on the code right away. But it will enable us to do some more important steps later on.


LSharp.Environment env = new LSharp.Environment();
Console.WriteLine(Functions.Load(new Cons("mylib.ls"), env));

// build the arguments for map
ArrayList myarraylist = new ArrayList();
myarraylist.Add(env["double"]);
Cons intCons = Cons.FromIEnumerable(new object[] { 1, 2, 3, 4, 5 });
myarraylist.Add(intCons);

// call map
Function map = (Function)env["map"];
Cons args = Cons.FromIEnumerable(myarraylist);
Console.WriteLine(map(args, env));

// call concat
object[] myarray = { "foo", "bar", 42, "baz" };
args = Cons.FromIEnumerable(myarray);
Closure concat = (Closure)env["concat"];
Console.WriteLine(concat.Invoke(args));

It also enables you to convert any class that implements IEnumerable:


Cons cons = Cons.FromIEnumerable(someRandomThingWithAnEnumeration);

The advantage of this is that whenever something new comes along that needs to be turned into a Cons, it can already be done if there is any standard way to reference all the items.

3. Make it easy to convert nested .Net data structures to Conses

After creating the code to consistently convert enumerable data structures to Conses, one obvious problem is that it won't automatically handle nested data structures. In the following code, the array and the ArrayList have to be converted separately:


ArrayList myarraylist = new ArrayList();
myarraylist.Add(env["double"]);
Cons intCons = Cons.FromArray(new object[] { 1, 2, 3, 4, 5 });
myarraylist.Add(intCons);

Cons args = Cons.FromICollection(myarraylist).Reverse();

Since nested lists -- often, deeply nested ones -- are one of the most basic building blocks of Lisp code, it would be nice if we could convert the whole data structure at once. Forutunately, if we extend the FromIEnumerable method we just built, there's a straightforward solution:


public static Cons FromIEnumerable(IEnumerable enumerable, bool isRecursive)
{
    object list = null;
    foreach (object o in enumerable)
    {
        if (isRecursive && o is IEnumerable) {
            o = FromIEnumerable(o as IEnumerable, true);
        }
        list = new Cons(o, list);
    }
    return (Cons)list;
}

Now we can easily convert the data structure to a nice, Lispy nested list in one step:


ArrayList myarraylist = new ArrayList();
myarraylist.Add(env["double"]);
int[] intArray = new object[] { 1, 2, 3, 4, 5 });
myarraylist.Add(intArray);

Cons args = Cons.FromIEnumerable(myarraylist, true);

This doesn't have a significant impact on our sample code here, but for cases where there is data that is already in elaborate .Net data structures and needs to be passed to L#, it will make things significantly easier, and avoid the need to walk the data structure tree.

4. Simplify building Conses in C#

Passing nested data to an L# function from C# is still fairly cumbersome. While building arrays and ArrayLists is often more natural for working with C# data, it's cumbersome for packaging up arguments, as we saw in the last section when we built the arguments to pass to map. Consider the code we've just been looking at:


ArrayList myarraylist = new ArrayList();
myarraylist.Add(env["double"]);
int[] intArray = new object[] { 1, 2, 3, 4, 5 });
myarraylist.Add(intArray);

Cons args = Cons.FromIEnumerable(myarraylist, true);

This could be done differently by building a Cons directly, of course:


Cons intCons = Cons.FromArray(new object[] { 1, 2, 3, 4, 5 });
Cons args = new Cons(intCons);
Cons args = new Cons(env["double"], args);

This is simpler, but still harder than it needs to be. Also, it either involves performing the steps out of the usual order -- this is normal in Lisp, but unusual in C# -- or adding a Reverse() operation each time.

By contrast, in L#, the equivalent code is simply:


(map double '(1 2 3 4 5))

Obviously, if we could simplify the C# version, it would be a lot easier to package arguments and call L# functions. For example, this code would be much closer to ideal:


Cons args = Cons.Build(env["double"], Cons.Build(1, 2, 3, 4, 5));

That can be implemented relatively straightforwardly by creating a Build method that can take any number of arguments and simply build a list out of them:


public static Cons Build(params object[] items)
{
    Object cons = null;
    for (int i = items.Length - 1; i >= 0; i--)
    {
        cons = new Cons(items[i], cons);
    }
    return (Cons)cons;
}

This simplifies the example code to:


LSharp.Environment env = new LSharp.Environment();
Console.WriteLine(Functions.Load(new Cons("mylib.ls"), env));

// call map
Cons args = Cons.Build(env["double"], Cons.Build(1, 2, 3, 4, 5));
Function map = (Function)env["map"];
Console.WriteLine((Cons)map(args, env));

// call concat
object[] myarray = { "foo", "bar", 42, "baz" };
args = Cons.FromIEnumerable(myarray, false);
Closure concat = (Closure)env["concat"];
Console.WriteLine(concat.Invoke(args));

5. Provide an identical interface for functions or closures

Wouldn't it be nice if you could have one simple way to call an L# operation, regardless of whether it was a system function, a user-defined function (closure), or a special form? And wouldn't it be nice if you could pass the operation whatever data structure you had without converting it?

For example, here's what the code to call map and concat might look like if you could do that:


Cons result = (Cons)map(args);

string sresult = (string)concat(myarray);

That's a lot simpler than the earlier example, right? Well, with delegates, it's easy to produce that interface:


public delegate Object fn(IEnumerable arguments);

There's really only one problem with this delegate: it doesn't match the signatures of Closure or Function, so you can't use it, at least directly. Fortunately, there's a way around that problem:

6. Build an implementation class that can make function calls simple

Although the interface to Closure and Function doesn't match the fn delegate signature, it's straightforward to make an adapter class that makes them match. Basically, the idea is to automatically translate data structures to Conses, and automatically bind functions to the current environment. The class then contains a method with the proper signature for the fn delegate, and a property that will return it.


public class FunctionBinding
{
    private Environment _env;
    private Function _f;
    private Closure _c;
    private bool _isClosure;

    public FunctionBinding(Environment env, Function f)
    {
        _f = f;
        _env = env;
        _c = null;
        _isClosure = false;
    }

    public FunctionBinding(Closure c)
    {
        _f = null;
        _env = null;
        _c = c;
        _isClosure = true;
    }

    private Object Impl(IEnumerable arguments)
    {
        // special handling for closures that take no
        // arguments and functions with empty lists
        if (arguments == null)
        {
            if (_isClosure)
                return _c.Invoke();
            else
                return _f(new Cons(null), _env);
        }

        // build a cons out of the argument
        Cons cons = null;
        if (arguments is Cons)
            cons = arguments as Cons;
        else
            cons = Cons.FromIEnumerable(arguments, true);

        // invoke the closure or function
        if (_isClosure)
            return _c.Invoke(cons);
        else
            return _f(cons, _env);
    }

    public fn BoundFunction
    {
        get { return Impl; }
    }

    public static fn Bind(Environment env, Function f)
    {
        FunctionBinding fb = new FunctionBinding(env, f);
        return fb.BoundFunction;
    }
}

This makes it really simple to call L# functions, but complicates retrieving them. The problem is that we have to build a FunctionBinding object around the return value we get from the environment. At this point, our code would work like this:


LSharp.Environment env = new LSharp.Environment();
Console.WriteLine(Functions.Load(new Cons("mylib.ls"), env));

// call map
Cons args = Cons.Build(env["double"], Cons.Build(1, 2, 3, 4, 5));
fn map = (new FunctionBinding(env, (Function)env["map"])).BoundFunction;
Console.WriteLine(map(args));

// call concat
object[] myarray = { "foo", "bar", 42, "baz" };
fn concat = (new FunctionBinding((Closure)env["concat"])).BoundFunction;
Console.WriteLine(concat(myarray));

7. Wire things up so FunctionBindings are created automagically

Fortunately, we can simplify things. By modifying the string indexer we built earlier, we can automatically bind functions and closures, and return the easy-to-call bound functions.


public object this[string s]
{
    get
    {
        object o = GetValue(Symbol.FromName(s));
        if (o is Function)
            o = (new FunctionBinding(this, (Function)o)).BoundFunction;
        else if (o is Closure)
            o = (new FunctionBinding((Closure)o)).BoundFunction;
        return o;
    }
    set { Assign(Symbol.FromName(s), value); }
}

This will also convert our call to the double function as well, which means the interpreter needs to be able to handle fn delegates:


public static object Apply (object function, object arguments, Environment environment) 
{
    if (function.GetType() == typeof(Function)) 
    {
        return ((Function) function) ((Cons)arguments,environment);
    }

    // If function is an LSharp Closure, then invoke it
    if (function.GetType() == typeof(Closure)) 
    {
        if (arguments == null)
            return ((Closure)function).Invoke();
        else
            return ((Closure)function).Invoke((Cons)arguments);
    }

    if (function is fn)
    {
        return ((fn)function)((IEnumerable)arguments);
    }
    else 
    {
        // It must be a .net method call
        return Call(function.ToString(),(Cons)arguments);
    }
}

After this final change, our code simplifies to this:


LSharp.Environment env = new LSharp.Environment();
Console.WriteLine(Functions.Load(new Cons("mylib.ls"), env));

// call map
Cons args = Cons.Build(env["double"], Cons.Build(1, 2, 3, 4, 5));
fn map = (fn)env["map"];
Console.WriteLine(map(args));

// call concat
object[] myarray = { "foo", "bar", 42, "baz" };
fn concat = (fn)env["concat"];
Console.WriteLine(concat(myarray));

And if you want, you can simplify it even further, though it makes the code a bit denser:


LSharp.Environment env = new LSharp.Environment();
Console.WriteLine(Functions.Load(new Cons("mylib.ls"), env));

// call map
Cons args = Cons.Build(env["double"], Cons.Build(1, 2, 3, 4, 5));
Console.WriteLine(((fn)env["map"])(args));

// call concat
object[] myarray = { "foo", "bar", 42, "baz" };
Console.WriteLine(((fn)env["concat"])(myarray));

Conclusion

Implementing these changes should make it much easier to call L# code from inside C#, and without resorting to building evaluation strings. Hopefully, this will make it more practical to take sections of code that can be implemented much more easily in L# than in C# and build a multiple-language implementation. For example, in a program that has a section that requires elabortate list manipulation, but other sections that need to be in C#.

With these changes, we were able to simplify our sample code from


LSharp.Environment env = new LSharp.Environment();
Console.WriteLine(Functions.Load(new Cons("mylib.ls"), env));

// build the arguments for map
ArrayList myarraylist = new ArrayList();
myarraylist.Add(env.GetValue(Symbol.FromName("double")));
Cons intCons = Cons.FromArray(new object[] { 1, 2, 3, 4, 5 });
myarraylist.Add(intCons);

// call map
Function map = (Function)env.GetValue(Symbol.FromName("map"));
Cons args = Cons.FromICollection(myarraylist).Reverse();
Console.WriteLine(map(args, env));

// call concat
object[] myarray = { "foo", "bar", 42, "baz" };
args = Cons.FromArray(myarray);
Closure concat = (Closure)env.GetValue(Symbol.FromName("concat"));
Console.WriteLine(concat.Invoke(args));

to


LSharp.Environment env = new LSharp.Environment();
Console.WriteLine(Functions.Load(new Cons("mylib.ls"), env));

// call map
Cons args = Cons.Build(env["double"], Cons.Build(1, 2, 3, 4, 5));
Console.WriteLine(((fn)env["map"])(args));

// call concat
object[] myarray = { "foo", "bar", 42, "baz" };
Console.WriteLine(((fn)env["concat"])(myarray));

Of course, to do the equivalent in L#, the code is


(load "mylib.ls")

(prl (map double '(1 2 3 4 5)))

(prl (concat "foo" "bar" 42 "baz"))

So L# functions still aren't nearly as easy to work with from C# as they are natively. Even keeping this in mind, these changes should make it a lot more practical to embed direct calls to L# code in C#.

The main thing this proposal hasn't touched on, of course, is macros. If you have macros embedded in L# functions, this won't make any difference -- the macros get expanded at read time, and your function can be called from C# just like any other function. But what if you want to call a macro directly? Or what if you want to embed a macro call inside the arguments to the function you call?

The techniques discussed in this article won't handle macros correctly, because by the time a function is being called, L# assumes all macros have been expanded. In a future post, I'll discuss how to use similar C# techniques with macros.

Finally, in the future, adding compiled code will be an important way to make L# code more accessible from C#. That will allow L# libraries to be attached at compile time and statically bound, rather than loaded and called at runtime. While that will be a powerful technique for some situations, the ability to easily call interpreted L# at runtime will still have its place in many other scenarios.

Tags: , , , ,

09 September 2006

5 Reasons Why Joel's Business (Probably) Isn't Like Yours

Joel Spolsky has had a lot of interesting things to say about software and the software business, including this week when he had a whole series on hiring. In this week's series, he said lots of things that programmers are bound to like: treat your developers like rockstars, give them all Aeron chairs and super-expensive monitors, build an elaborate atrium with a fountain just to impress your developers.

You have to admire Joel's approach, because it says right on his site that his business is all about programmers:

Best Working Conditions => Best Programmers => Best Software => Profit!

If you're a programmer, how can you not like that?

I'm sure his recent series (and maybe his blog in general) has left a lot of people thinking: wow, my company isn't like that. Joel is so much cooler than my company. And, well, maybe he is. But before you go in to your boss and start complaining about how he's cheap, and if he would just run his business like Fog Creek, things would be so much better, here are a few reasons to take his ideas with a grain of salt and think about whether they would really benefit the company where you work.

By the way, I'm not really an expert on Fog Creek or anything, so I'm inferring a lot of this as I go along. And I'm not doing this to trash Joel, because I love reading his writing, so if you're reading this to get to the good parts where I really rip him a new one, well, sorry.

1. Fog Creek is a software business

In a software business, the developers drive the products. Programmers are always going to be pretty vital to a software company. By contrast, at an automaker or a bond trading firm, it's going to be people like car designers or bond traders that are the rockstars -- they're the ones that make the business go.

By the way, here's a corollary: all other things being equal, it's more fun for a programmer to work at a software company than to write software for a business in another industry. Any software company that cares about its products cares about its programmers.

2. Joel doesn't have 2000 people

This is the "not everyone can be a rockstar" rule. The larger a company gets, the harder it is to only hire the kind of superstars Joel talks about looking for. It's not clear that his model scales to bigger businesses.

Then again, he learned the software business at Microsoft which always used to have a rep for only hiring brilliant people, so maybe it's not impossible to scale this model, but merely really difficult.

3. Joel started up with a good-sized chunk of capital

This is just speculation -- I could be wrong. But his basic premise of starting a really cool business with Aeron chairs and an atrium and expensive monitors makes it sound like he (and maybe his occasionally-mentioned business partner, who I don't know anything about) had a bunch of Microsoft options that vested and brought in a bunch of money as seed capital.

I don't mean that in a dismissive way, such as "well, anyone could start a successful software business if they had that kind of capital." That's actually not at all true. Most Microsoft people who vested and started their own businesses completely failed. What it means is, if you start a business by maxing out a Visa card, for god's sake, don't spend the money on an Aeron chair.

Also, because I presume that the capital was mostly his, he didn't have people pressuring him to show a large immediate return. If you have Venture Capitalists breathing down your neck, don't go hook yourself up with an office with a fancy atrium, just because Joel told you to. Keep your burn rate low, like everyone learned from the dot com crash.

4. Fog Creek makes shrinkwrap software

Joel wrote a really good essay a few years ago about the 5 Worlds of Software Development. One of them is shrinkwrap software, which is what Fog Creek does.

Shrinkwrap software is great. Everyone actually pays you to use your software, which, if you do it right, makes it a lot easier to pay your bills than if you write open source and hope to live off support contracts or build a cool website and hope to live off Google AdWords.

Not only do you get paid up front, but the best part about shrinkwrap is that it scales like crazy. If you work in an IT department and build internal software, you work closely with your client, and you usually can't easily add any more clients. If you write software with a heavy customization component, you pretty much have to add new fleets of consultants or VARs as fast as you add clients. But if you sell shrinkwrap, then as you add more clients, the money from the new sales mostly goes straight to the bottom line.

What this means is that if you have a shrinkwrap company, you have the best possible situation to actually generate a return on hiring rockstar programmers and throwing expensive perks at them. You have a situation where you get paid for producing software people want, and there's a significant asymmetric relationship between the work that goes in and the money that comes back if you're successful. The only thing you have to watch out for is secondary costs like technical support, which merely reinforces that you have to build and test it right in the first place.

Here's the thing, though: as evidenced by the fact that there aren't many startups doing shrinkwrap anymore, it's a tough business to succeed at. As you would expect from the asymmetric returns, and as evidenced by Microsoft, it tends to be a winner-take-all business. More sales => more money to invest in products and marketing => even more sales. So what does it take to be successful? Well, one is good products up front, so you can sell some in the first place. This is where the whole rockstar programmers thing comes in, and why it makes sense for Joel to give everyone Aeron chairs.

The other big one, of course, is simple market awareness: get your product in front of people. And that's where the blog with millions of hits comes in. Which brings us to my last point:

5. Joel's target market is programmers

If you've ever heard MBAs wet themselves talking about synergy and thought that it was all a pile of B.S., well, sometimes even MBAs are right. Because Joel's business is just one giant perpetual motion machine of synergy. He clearly spends a ton of time writing blog entries about software development and software businesses, but it all pays off, because his target market is other programmers. Soccer moms and MBAs surely don't have any use for bug tracking software and remote-support applications -- developers do. So Joel's investment of his time in his blog pays off in massive brand awareness among his target market.

Joel writes all these things about hiring, and how programmers should be treated as rockstars. And one benefit is that he must have one monster applicant pool. But another is that it ties right into what his target market wants to hear. Not only that, but after all the time he spends talking about how he only hires the best of the best, who wouldn't want to at least evaluate his software if they need bug tracking? I mean, what kind of schmuck would settle for ordinary software when they can have code written by supergeniuses (even if it is in VBScript)?

Now, someone is going to read this last point and think I'm saying that Joel is evil: he's just doing all of this to trick us into buying his software. But that's not my point at all. The guy has built a business where he gets to go to work in a fun environment and write code with lots of other smart people. He gets to be famous in the blogosphere, and have lots of people hanging on his every word. And as icing on the cake, he gets to profit from the whole thing on top of it. There ain't nothing wrong with that other than that we can't all be that lucky (though really, it isn't luck -- it's a smart business plan).

But like I said at the start, your business (probably) isn't like that. So if you don't have the Aeron chair and the fountain and the 30" monitor, it probably has something to do with one of these reasons.

06 September 2006

A Simple Library of L# Utility Functions

As I've been playing around with L#, I ran across a few pretty universally useful things that don't ship with the release. So I whipped up a basic library that contains a few useful things. I should have a permanent home for this soon, but I don't at the moment, so I'm just going to post it here for now.

I'm far from a Lisp expert, so if anyone wants to work out a better implementation of some of this code, I'll be happy to include it in the next version.


; basics.ls -- implements some universally used functions in
;              the L# language
;
; modified 9/7/06: replaced references to nil with null,
;                  simplified filter, map, and reduce,
;                  added get function.
;
; Copyright (c) 2006, Andrew Norris
;
; This file doesn't contains anything especially remarkable,
; and anyone is free to reuse this in whatever fashion you
; like, and include it in a product licensed under any terms.
; Please retain the copyright attribution. Thanks.

; reasonably efficient list concatenation function
(= concat (fn (&rest items)
    (let sb (new system.text.stringbuilder)
      (foreach elem items
        (call append sb elem))
      (tostring sb ))))

; recovers the string value of an object for use in code
(= str (fn (obj)
    (writetostring printer obj)))

; applies function f to a list of items and returns the
; result
(= map (fn (f lst)
    (if (== lst null)
      null
      (cons (f (car lst)) (map f (cdr lst))))))

; returns the items in the list for which f(item) is true
(= filter (fn (f lst)
    (if (== lst null)
      null
      (if (f (car lst))
        (cons (car lst) (filter f (cdr lst)))
        (filter f (cdr lst))))))

; returns the result of applying a group function over
; the list lst
;
; for example, (reduce (fn (a b) (* a b)) '(2 3 4 5) 1)
; will initialize with the value 1, and return the
; product of all of the values of the list, i.e.
; 1 * 2 * 3 * 4 * 5 = 120
(= reduce (fn (f lst init)
    (if (== lst null)
      init
      (f (car lst) (reduce f (cdr lst) init)))))

; pushes item on to the front of list lst, altering the
; actual list passed in
(defmacro push (lst item)
  `(= ,lst (cons ,item ,lst)))

; pushes item on to list lst, altering the actual list
; passed in
; note: uses a goofy variable, because 1.2.1 doesn't have
; gensym
(defmacro pop (lst)
  `(let obscure-item-name (car ,lst)
    (= ,lst (cdr ,lst))
    obscure-item-name)))

; traverses a property list and finds a matching symbol
(= get (fn (plist sym)
    (if (== plist null)
      null
      (if (eq (car plist) sym)
        (cadr plist)
        (get (cddr plist) sym)))))

Update: I fixed a couple of bugs, made the code a little more elegant, and added the get function. I don't have a permanent home for this yet, so I just applied the changes here.

Tags: , , , , ,

03 September 2006

First Impressions of L#

Introduction

Lately I've been fooling around with Lisps. I've done some work on Linux in SBCL, an open source Common Lisp implementation that seems like a good, solid piece of code. But I've spent most of my time writing code in L#, an interpreted Lisp dialect for the .Net CLR. Lots of people are familiar with Common Lisp, but not all that many people seem to be familiar with L# yet, so I thought I would share my first impressions.

Like apparently everyone else who has started learning Lisp in the last five years, I got interested by reading Paul Graham. In particular, I didn't like the idea of programming in Blub without knowing more about some of the available alternatives.

So I've read Peter Seibel's Practical Common Lisp and I've started reading Paul Graham's On Lisp, and trying to learn how Lisp works.

Of course, if you keep reading Paul Graham, you also notice that he doesn't exactly endorse Common Lisp. He's been working on a newer, sexier Lisp called Arc, which of course you can't use yet. In particular, he argues the importance of having powerful, modern libraries.

It was somewhere around then that I ran across L#. L# was designed by Rob Blackwell with a lot of Graham's published ideas for Arc in mind. To me, at least, the language looks a little cleaner. In addition, it's designed to run on top of .Net, which means that even in its early stages, it comes with complete with a massive set of libraries for everything from XML to database access to user interfaces. When you add in the fact that I'm employed as a C# developer, so I already know those libraries pretty well, L# seemed like a no brainer to try out. So naturally, I did.

There's a lot to like about L#

If you're still with me at all, you're probably ready for me to move past the preamble and actually talk about L#. So here we go.

The single best thing about L# is exactly what you would expect: it combines a Lispy (and very clean) syntax with the massive library support of .Net. Want to do regular expressions? There's a .Net library that does all the heavy lifting. Hate all the gratuitous verbosity you get in .Net? Write a 5-line wrapper function and go to town.

Here's a really simple but real-life example: if L# comes with a concatenation operator, I haven't found it yet. I was doing a whole lot of string concatenation operations, and wanted an easy way to do it. In C# there are two basic ways to do string concatenation. The expensive way with clean syntax is to do:

mystring = foo + bar + baz + "\n";

which instantiates a new string for each concatenation operation. Meanwhile the efficent way with heinous syntax is to use a StringBuilder, which has a buffer so you don't have to reallocate space every time:

StringBuilder sb = new StringBuilder();
sb.Append(foo);
sb.Append(bar);
sb.Append(baz);
sb.Append("\n");
mystring = sb.ToString();

You could encapsulate a function for doing this, of course, and end up with something like:

string[] myarray = { foo, bar, baz, "\n" };
string mystring = MyStringLibrary.Concatenate(myarray);

That's a little better, but it's still kind of an eyesore. You can't declare the array inline as an argument to the function, and you have to carry around a class to hold the function name.

It won't surprise anyone familiar with Lisp, but L# makes it easy to make it easy. Here's my simple function to concatenate a list. I expect a Lisp guru could do something cooler, but I'm nowhere near a Lisp guru yet:

(= concat (fn (&rest members)
    (let sb (new system.text.stringbuilder)
      (foreach elem members
        (call append sb elem))
      (tostring sb))))

This yields a nice clean syntax for string concatenation, with all the advantages of the efficient, ugly way in C#, and a syntax as simple as adding strings together:

(concat foo bar baz "\n")

Actually, I seem to be using string concatenation enough that I think I'm going to rename the function ct, which should make things even more terse.

So anyway, that's just the simplest, scratch-the-surface example. Once you get into macros and things like that, I'm pretty sure that the differences will be even more dramatic.

L# shows enormous potential as a glue language for .Net applications: scripting, configuration, things like that. For scripting tasks, you can easily write simple, terse scripts that work with the code you've already built. I feel a little silly recommending a variant of possibly the most powerful programming languages ever devised as a way of scripting something written in C#, but it's a good way to get a foot in the door and add L# to your toolchain.

Steve Yegge has made a pretty compelling case for Lisp as a replacement for XML in things like config files and logs. I don't have much to add to that, but if you've ever gotten tired of messing around with XSLT transforms, L# offers an easy way to abandon them forever.

So utility programming in L# is pretty cool, but can you do more than that? Can you write real apps? I don't know yet. But I plan to find out. A language that's really powerful, but still fundamentally easy and fun to work with is worth experimenting with to find out how far you can take it.

I'm sure I won't be able to rip out years of production code and replace them all with L# -- really, I wouldn't want to, even if I could. But there's no reason not to look at it for new projects and interoperation. Currently, there's a few missing pieces for seamless interoperation with other .Net languages (mainly, L# doesn't compile into callable assemblies yet), but there's nothing standing in the way of getting those problems solved except time and effort.

L# Tools

The current tools for developing in L# aren't everything you could want, but they're not bad. First off, you can use Visual Studio, but there are no direct integration tools yet. Even without syntax highlighting, etc., this is still a pretty good solution. The main reason it works pretty well, is that it gives you the ability to debug your code. Well, sort of.

Actually, what you can do is load the C# source code for the LSharp interpreter into Visual Studio and debug the interpreter. You set breakpoints in places like Eval() and Call() and follow your code as it evaluates. This took a little adjusting to, but it actually works pretty well. You can drill down and see exactly what it's evaluating and how it's failing. Did you forget to quote something and send an unquoted symbol by mistake? It's all right there in the interpreter -- you can see what it's doing with your code.

Outside the debugger, the main advantage of using Visual Studio is that if you're integrating it with C# in the same solution, you can manage all the files in the same place. But really, to Visual Studio your L# files are just opaque text files.

Another tool I've worked with is TextPad, which is a feature-rich text editor for Windows that I use anyway. There's a syntax file for L# for TextPad, so it gives you syntax highlighting and parentheses matching (with ctrl-M). I often edit L# files in TextPad at the same time that I have the project open in Visual Studio, and Visual Studio has a handy reload dialog that pops up when files have changed.

There are other tools for working in C#, but I haven't had a chance to put them through their paces yet. xacc.ide is an integrated development environment being built that not only has support for L# syntax, it apparently uses L# as a scripting language. That certainly implies that xacc.ide will be a good tool for L# development. Finally, emacs support for L# has been developed, so if you're comfortable working in emacs, that may be just the ticket.

Conclusion

Of course, not everything in L# is ideal. I've run across some annoyances as I've been working in L#, as you would imagine for a young language. Nothing that is a showstopper, but a few things I wish worked a little better. The main thing, though, is that I haven't run into anything that has made me even want to consider abandoning L# for another language.

I'm hopeful that at least most of the annoyances can simply be fixed, so I'm going to write a followup post that goes into these in more detail, along with some ideas about what to do about them. This post is long enough as it is.

In the meantime, I hope this helps people know more about what to expect with L#, and I hope that people will go ahead and try it out. It's a solid language, and it potentially adds a whole new dimension to .Net projects.

Tags: , , , ,