Whilst doing some major cleaning up / throwing away of some of my accumulated junk this weekend, I came across my scientific calculator from college.
"Big deal", you say? Maybe so. But this thing is 23-24 years old, which is about 2400 in computer years, which puts it close to the technical definition of "antique". (Author's Note: We won't get into what that makes me...)
Still not impressed? The thing still works. With the original batteries.
You read that right. After a year or so of use, every time I turned the thing on I would wonder if this was the day I was going to have to change the batteries. Nearly 25 years later, that day still hasn't come. Now granted, for the past several years I wasn't even sure I still had it. But when I came across it in my junk-cleaning I couldn't resist the temptation to try it out. I suppose I should've been surprised, but I wasn't really.
I'm telling you, this is the Calculator That Time Forgot. This is the Calculator of Dorian Gray. I'm going to have to hold some kind of ceremony when it finally does give up the electronic ghost. But I'm not holding my breath.
Monday, November 12, 2007
Tuesday, November 06, 2007
Strike!
Dear Striking Writers Guild of America People,
First off, let me just say that I in no way want to impede your constitutionally protected right to effectively quit your jobs and be guaranteed that you'll get them back once your demands are met. That is some constitutionally protected right, right there. Anyways, God love you and I hope you get your residuals. Power to the proletariat, and all that.
But I do have a favor to ask. You see, the only new thing on TV for a while is going to be news footage of your picketing, and I think you owe it to yourselves (not to us, don't get me wrong here; you're on strike, I get that) to punch up your picketing chants a little. Maybe it's just me, but I kind of expect your strike chants to be more...I don't know...pithy? Trenchant? Heart-wrenchingly but ultimately stirringly observational of the Human Condition?
I mean (this is an actual WGA picket line chant): "Why make the viewers wait? Why won't you negotiate?". Really? That's the best the WGA -- the WGA! -- could come up with? I realize you're doing what you do best -- rehash old material -- but come on. You have so much better old material to work with. Some suggestions:
You could go the nostalgic route, and remind us of everything you've done for us in the past, and what we'd be missing without you:
Or, and this is my favorite option, you could go beyond the traditional chant-based approach and actually stage your picket lines like the different genres you write for. You guys are writers, for pity's sake! You need to keep your chops up. Everyone who isn't actually walking a picket line at the moment could meet in the bullpen:
Fade In. Cue cutesy beginning-of-scene jingle. A group of writers are sitting around a table.
Head Writer: Okay, people, we've got a strike to write here. Who's got some ideas? Let's shoot it around the room!
Writer 1: We could get the cast of Friends to sit around on some comfy chairs on the picket line and make wisecracks about how lame it is that the producers won't give us any more money! David Spade could guest star, which will eventually get us Heather Locklear! And Jerry Stiller could walk in and start yelling his lines incoherently until Jason Alexander gets so worked up that he has to leave. Let's leave what's-his-name-the-guy-who-played-Kramer out of this one, though, huh?
Writer 2: Oooh! Crime drama! We could get David Caruso to do that thing he does in, like, every SINGLE SCENE where he has any dialogue! You know -- he stares moodily into the distance while delivering the line and then, at the very end, just when you think he's not going to, HE SWIVELS HIS HEAD to look directly at whoever he's talking to! It's brilliant, I tell you!
Smash cut. David Caruso is standing at the counter at a Starbucks.
Perky Starbucks Babe: (perkily) Can I take your order, sir?
David Caruso: (staring moodily into the distance) Yeeessssssss. I believe I'll have a (swivel) latte.
Wipe transition. We are back in the bullpen.
Other Writers: (all at once)...Caruso...brilliant...NYPD...movie "career"...hahahahahahahahahahahahahahahahaha!
Writer 3: How about a reality TV angle? We could have one of the picketing writers get ragged on by all the others until he snaps and puts Billy's hand in warm water while he's sleeping and then schemes with Maggie and Amber to get Chuck and Bruce voted out of the picket line only to double-cross them by "accidentally" tripping Maggie during their mambo and telling Amber she's too fat for him to marry her so she bursts into tears and can't do her Billy Idol tribute number so she runs off to build houses and stuff for the poor until she gets noticed by Donald Trump who makes her his new wigtender! Simon Cowell could be the judge!
Writer 4: I've got it! Let's get the writers for Saturday Night Live! They've been on strike for, like, eight years now, only no one told Lorne Michaels so they still get paid...
Fade out. Mercifully.
Remember now, I'm asking this favor for your sake, not mine. I'm not going to be affected one way or the other. You think you're "hunkered down for a long one"? Kid, I've got all the Harry Potters and the Lord of the Rings trilogy on DVD. Extended Director's Cut Version With Commentaries and Making-Of Featurettes. You won't be seeing me for months!
First off, let me just say that I in no way want to impede your constitutionally protected right to effectively quit your jobs and be guaranteed that you'll get them back once your demands are met. That is some constitutionally protected right, right there. Anyways, God love you and I hope you get your residuals. Power to the proletariat, and all that.
But I do have a favor to ask. You see, the only new thing on TV for a while is going to be news footage of your picketing, and I think you owe it to yourselves (not to us, don't get me wrong here; you're on strike, I get that) to punch up your picketing chants a little. Maybe it's just me, but I kind of expect your strike chants to be more...I don't know...pithy? Trenchant? Heart-wrenchingly but ultimately stirringly observational of the Human Condition?
I mean (this is an actual WGA picket line chant): "Why make the viewers wait? Why won't you negotiate?". Really? That's the best the WGA -- the WGA! -- could come up with? I realize you're doing what you do best -- rehash old material -- but come on. You have so much better old material to work with. Some suggestions:
We won't write another role! *bleep* you, *bleep*hole!
More new films for Jean-Claude Van Damme? Frankly, my dear, I don't give a damn!
Did Tony Soprano's family get whacked? We won't tell you, 'cause we're so hacked!
No more Die Hard on Blu-Ray! Mother *bleep*er, yippie-ki-yay!
Won't give us the money we lack?! Then the Terminator will never be back!
You could go the nostalgic route, and remind us of everything you've done for us in the past, and what we'd be missing without you:
Star Trek! Numb3rs! Stand By Me! No Wil Wheaton on TV!
Or, and this is my favorite option, you could go beyond the traditional chant-based approach and actually stage your picket lines like the different genres you write for. You guys are writers, for pity's sake! You need to keep your chops up. Everyone who isn't actually walking a picket line at the moment could meet in the bullpen:
Fade In. Cue cutesy beginning-of-scene jingle. A group of writers are sitting around a table.
Head Writer: Okay, people, we've got a strike to write here. Who's got some ideas? Let's shoot it around the room!
Writer 1: We could get the cast of Friends to sit around on some comfy chairs on the picket line and make wisecracks about how lame it is that the producers won't give us any more money! David Spade could guest star, which will eventually get us Heather Locklear! And Jerry Stiller could walk in and start yelling his lines incoherently until Jason Alexander gets so worked up that he has to leave. Let's leave what's-his-name-the-guy-who-played-Kramer out of this one, though, huh?
Writer 2: Oooh! Crime drama! We could get David Caruso to do that thing he does in, like, every SINGLE SCENE where he has any dialogue! You know -- he stares moodily into the distance while delivering the line and then, at the very end, just when you think he's not going to, HE SWIVELS HIS HEAD to look directly at whoever he's talking to! It's brilliant, I tell you!
Smash cut. David Caruso is standing at the counter at a Starbucks.
Perky Starbucks Babe: (perkily) Can I take your order, sir?
David Caruso: (staring moodily into the distance) Yeeessssssss. I believe I'll have a (swivel) latte.
Wipe transition. We are back in the bullpen.
Other Writers: (all at once)...Caruso...brilliant...NYPD...movie "career"...hahahahahahahahahahahahahahahahaha!
Writer 3: How about a reality TV angle? We could have one of the picketing writers get ragged on by all the others until he snaps and puts Billy's hand in warm water while he's sleeping and then schemes with Maggie and Amber to get Chuck and Bruce voted out of the picket line only to double-cross them by "accidentally" tripping Maggie during their mambo and telling Amber she's too fat for him to marry her so she bursts into tears and can't do her Billy Idol tribute number so she runs off to build houses and stuff for the poor until she gets noticed by Donald Trump who makes her his new wigtender! Simon Cowell could be the judge!
Writer 4: I've got it! Let's get the writers for Saturday Night Live! They've been on strike for, like, eight years now, only no one told Lorne Michaels so they still get paid...
Fade out. Mercifully.
Remember now, I'm asking this favor for your sake, not mine. I'm not going to be affected one way or the other. You think you're "hunkered down for a long one"? Kid, I've got all the Harry Potters and the Lord of the Rings trilogy on DVD. Extended Director's Cut Version With Commentaries and Making-Of Featurettes. You won't be seeing me for months!
Monday, November 05, 2007
LaTeX For The Win!
Yeah, yeah -- I feel a little silly making a big deal about having just now become a convert to LaTeX. It's like wandering into a room of World-of-Warcraft-playing geeks and bragging about "this super-cool game I found; it's called 'NetHack'!"
But dang! I can't believe I made it through two-thirds of a Master's degree in computer science without even trying this brilliant tool. I mean the quality of the output (okay -- the quality of the typsetting; the actual semantic content is still GIGO) makes even the most mundane homework assignment look like something that's going to be published in some major journal any day now.
The key was in resolving to take the first step. I was dreading it, but I found that forcing myself to do that first assignment in LaTeX let me pick up probably 80% of the formatting tricks I'll ever need. Definitely worth the effort.
LaTeX strikes me as a perfect example of what Kathy Sierra used to blog about (Author's Note: Hi, Kathy! Come back! We miss you!): software that lets you (me) KICK A**! It's a software tool, but it has one key characteristic of the best physical tools as well -- it takes a modest force as input (your (my) typing), and amplifies and concentrates that force to shape an output that you (I) could not have achieved with your (my) bare hands.
Simply put: LaTeX rocks.
I just hope TeXShop -- which is the front-end I use on my Macs -- plays well with Leopard. There aren't a lot of issues I can think of that would keep me from upgrading, but losing LaTeX/TeXShop would definitely be a showstopper.
But dang! I can't believe I made it through two-thirds of a Master's degree in computer science without even trying this brilliant tool. I mean the quality of the output (okay -- the quality of the typsetting; the actual semantic content is still GIGO) makes even the most mundane homework assignment look like something that's going to be published in some major journal any day now.
The key was in resolving to take the first step. I was dreading it, but I found that forcing myself to do that first assignment in LaTeX let me pick up probably 80% of the formatting tricks I'll ever need. Definitely worth the effort.
LaTeX strikes me as a perfect example of what Kathy Sierra used to blog about (Author's Note: Hi, Kathy! Come back! We miss you!): software that lets you (me) KICK A**! It's a software tool, but it has one key characteristic of the best physical tools as well -- it takes a modest force as input (your (my) typing), and amplifies and concentrates that force to shape an output that you (I) could not have achieved with your (my) bare hands.
Simply put: LaTeX rocks.
I just hope TeXShop -- which is the front-end I use on my Macs -- plays well with Leopard. There aren't a lot of issues I can think of that would keep me from upgrading, but losing LaTeX/TeXShop would definitely be a showstopper.
Thursday, November 01, 2007
Fun With Scala: Reading Scala
No, I haven't given up on my quest to learn more about Scala by perusing the guts of the liftweb source code. It's just that Iteration 3 at work, along with Hallowe'en with my two-year-old and Exam Two in Theory of Automata have all combined to kick my butt for the past couple of weeks. Not to mention all of the other life stuff that's intervening.
But no excuses! Let's have a look at some specific Scala code and see what we can figure out in general from it.
Today's snippet of Scala code is brought to you by
Besides the lack of semicolons (yea!) and the use of some standard Java classes (likewise yea!), one of the first things to notice is that
Finally, the definition of
Try entering the above code, line by line, in a
Which says that
Now you can start using
Even more fun is this: now you have a result (
(Author's Note: Have you ever wanted an
So what about the difference between declaring with
Uh-oh. Looks like Scala considers
It also looks like some constants are more constant than others. But before you start thinking you can just reassign
Whatever that means. I'm going to have to track that one down and report on it later. Right now it's time for more life (read: "sleep") to intervene.
But no excuses! Let's have a look at some specific Scala code and see what we can figure out in general from it.
Today's snippet of Scala code is brought to you by
net.liftweb.util.Helpers.scala
, for those of you following along in the book. I've elided a lot of it, for the purposes of this post. Here are the relevant lines of code, suitable for entering into your scala
interpreter:
import java.text.SimpleDateFormat
import java.util.TimeZone
val utc = TimeZone.getTimeZone("UTC")
def internetDateFormatter = {
val ret = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss z")
ret.setTimeZone(utc)
ret
}
Besides the lack of semicolons (yea!) and the use of some standard Java classes (likewise yea!), one of the first things to notice is that
utc
is declared as a val
, while internetDateFormatter
is declared as a def
. You can probably intuit the difference, but we'll see some examples in a moment of just how they're different. Also, notice that neither utc
nor internetDateFormatter
is given an explicit type. Each has a specific type, determined by the type of what's on the right hand of the assignment, but the compiler doesn't need to see the type declared on the left hand side. This has some consequences that we'll see too.Finally, the definition of
internetDateFormatter
ends simply with the line ret
. This idiom will be familiar to the Ruby programmers in the audience, as it simply declares what the ultimate value is of the block that constitutes the right hand side of the assignment to the variable internetDateFormatter
. That is, in Scala, as in Ruby, the value of a block is the value of the last statement executed within the block.Try entering the above code, line by line, in a
scala
interpreter. The imports aren't that interesting, but notice what you get as a result of entering the declaration of utc
:
scala> val utc = TimeZone.getTimeZone("UTC")
utc: java.util.TimeZone = sun.util.calendar.ZoneInfo[id="UTC",
offset=0,dstSavings=0,useDaylight=false,transitions=0,lastRule=null]
Which says that
utc
is of type java.util.TimeZone
, and in particular it is equal to a sun.util.calendar.ZoneInfo
with the corresponding id, offset, etc. Whereas when you finish declaring internetDateFormatter
, you simply get:
scala> def internetDateFormatter = {
| val ret = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss z")
| ret.setTimeZone(utc)
| ret
| }
internetDateFormatter: java.text.SimpleDateFormat
Now you can start using
internetDateFormatter
as you would any instance of SimpleDateFormat
. For example:
scala> internetDateFormatter.toPattern()
res8: java.lang.String = EEE, d MMM yyyy HH:mm:ss z
Even more fun is this: now you have a result (
res8
, or whatever pops up for you), which is a java.lang.String
, and can be treated as such:
scala> res8.split("\\s")
res9: Array[java.lang.String] = [Ljava.lang.String;@66718
scala> res9.toString()
res10: String = Array(EEE,, d, MMM, yyyy, HH:mm:ss, z)
(Author's Note: Have you ever wanted an
irb
(the interactive ruby interpreter) for Java? Download Scala, and get one for free today!)So what about the difference between declaring with
val
and declaring with def
? Let's find out:
scala> utc = TimeZone.getTimeZone("GMT-7")
line19$object.$iw.$iw.utc = TimeZone.getTimeZone("GMT-7"):6: error: assignment to non-variable
val res12 = {utc = TimeZone.getTimeZone("GMT-7");utc}
^
Uh-oh. Looks like Scala considers
val
s to be constant. Although:
scala> val utc = TimeZone.getTimeZone("GMT-7")
utc: java.util.TimeZone = sun.util.calendar.ZoneInfo[id="GMT-07:00",offset=-25200000,dstSavings=0,useDaylight=false,transitions=0,lastRule=null]
It also looks like some constants are more constant than others. But before you start thinking you can just reassign
internetDateFormatter
with impunity:
scala> internetDateFormatter = "impunity":5: error: value internetDateFormatter_= is not a member of object $iw
val res13 = {internetDateFormatter = "impunity";internetDateFormatter}
^
Whatever that means. I'm going to have to track that one down and report on it later. Right now it's time for more life (read: "sleep") to intervene.
Monday, October 22, 2007
Fun With Scala: Things I'm Learning From Lift #2
Thanks to astute reader Ken (Author's Note: I have a reader! An astute one! Who knew?!), I learned something about a feature I was bragging about in my last post; namely, the ability to import members of a class. As it turns out, not only is this feature present in Java 5 -- see the documentation for static imports -- the Scala version of it is pretty much equivalent.
Ken points out that my example of
Here's some lame, contrived, forced-example, somewhat-minimal Scala code to demonstrate:
As described here,
Bonus Discovery: While researching this, I also learned that Eclipse with the Scala plugin is not as smart as it should be about building my bogus project. If I start with a clean compile (i.e., defining Mixee as an object), Eclipse is happy. If I then change the definition of Mixee from
Anyone got a line on that? Ken? Anyone?
Anyway, just for the record, my purpose with these posts is not to bury Java, but to praise Scala and learn how it's different. I'll leave the determination of whether Scala is "better" than Java up to you, my readers. Both of you (hi, Mom!). ;-)
Ken points out that my example of
lift
importing the members of the net.liftweb.util.Helpers
"class" maps exactly to the corresponding Java import. I waved my hands around the fact that Helpers
is actually an object
, which is, simultaneously and at the same time, Scala's way of denoting a singleton and providing static members (all of the members of an object
are effectively static). But it turns out that the import
syntax I was bragging about works precisely because Helpers
is an object
and not a class
.Here's some lame, contrived, forced-example, somewhat-minimal Scala code to demonstrate:
Mixee.scala
package bogus;
class Mixee {
def weird: String = {"Weird Al!"}
def twentySeven: String = {"27!"}
}
Mixer.scala
package bogus;
import Mixee._;
object Mixer {
def main(args: Array[String]): Unit = { Console.println(weird); Console.println(twentySeven) }
}
As described here,
Mixer.scala
will refuse to compile, with the error: "not found: value Mixee". This is because Mixee is defined as a class
. Change class
to object
and it compiles (and runs) just fine.Bonus Discovery: While researching this, I also learned that Eclipse with the Scala plugin is not as smart as it should be about building my bogus project. If I start with a clean compile (i.e., defining Mixee as an object), Eclipse is happy. If I then change the definition of Mixee from
object
to class
, the error doesn't show up until I force a rebuild by selecting Project -> Clean... If I then change class
back to object
, the error goes away without my forcing the rebuild.Anyone got a line on that? Ken? Anyone?
Anyway, just for the record, my purpose with these posts is not to bury Java, but to praise Scala and learn how it's different. I'll leave the determination of whether Scala is "better" than Java up to you, my readers. Both of you (hi, Mom!). ;-)
Tuesday, October 16, 2007
Fun With Scala: Things I'm Learning From Lift #1
So I'm starting somewhere around the very beginning with the Liftweb framework:
Fun Thing #1: The very first import is
How's that for interoperability with Java? I haven't gotten that far yet, but this tells me that whatever Lift uses for its request/response implementation is going to conform to the usual Java interfaces. And how do you like the syntax for this style of import? I like it a lot, because it lets you do the equivalent of:
...on a single line (it's a single line in the original source; I've split it here for friendlier rendering). Without resorting to importing everything in the
Fun Thing #2: There's a syntax for importing not just classes, but members of a class. Like so:
This basically says "make everything in the
For example, there's a bit of code in
This makes it appear that
Fun Thing #3: No, the lack of semicolons at the end of the code samples is not a typo. The Semicolon is Dead! Long Live the Semicolon! No, scratch that -- Long Live Scala!
LiftServlet.scala
, which is in the net.liftweb.http
package. This looks like Lift's answer to a Rails controller, or a Struts Action (Author's Note: probably with some caveats that I haven't found yet; (pleasant) comments welcome if I'm wrong about this).Fun Thing #1: The very first import is
import javax.servlet.http.{HttpServlet, HttpServletRequest ,
HttpServletResponse, HttpSession}
How's that for interoperability with Java? I haven't gotten that far yet, but this tells me that whatever Lift uses for its request/response implementation is going to conform to the usual Java interfaces. And how do you like the syntax for this style of import? I like it a lot, because it lets you do the equivalent of:
import javax.servlet.http.HttpServlet
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse
...et cetera...
...on a single line (it's a single line in the original source; I've split it here for friendlier rendering). Without resorting to importing everything in the
http
package with a wildcard. This way you get to see exactly what you need. Yeah, I know a good Java IDE will manage the imports for you, so it's not as much of a pain as it used to be, but I like the compactness of this syntax. As it happens, the current version of the Scala plugin for Eclipse doesn't have the equivalent of "Organize Imports", so this syntax is definitely a boon.Fun Thing #2: There's a syntax for importing not just classes, but members of a class. Like so:
import net.liftweb.util.Helpers._
This basically says "make everything in the
Helpers
class (actually it's a singleton object
, but think of it as a class for now) available to this class without my having to qualify it with 'Helpers.
' all the time".For example, there's a bit of code in
LiftServlet
that goes like this:val md = parseInternetDate(mod)
This makes it appear that
parseInternetDate()
is defined in the LiftServlet class. But it's not. It's defined in Helpers
. If you're used to modules/mixins in Ruby, this should seem very familiar to you. If not, now might be a good time to get used to them, because they're way cool.Fun Thing #3: No, the lack of semicolons at the end of the code samples is not a typo. The Semicolon is Dead! Long Live the Semicolon! No, scratch that -- Long Live Scala!
Sunday, October 14, 2007
Fun With Scala: Liftweb Getting Started
Job one: checking out and Eclipsify-ing lift.
The instructions at the CircleShare lift blog are still current up to a point, at least as far as the prerequisites are concerned. I'm running from trunk, so my source code checkout was a little different:
After that, the available instructions need a little (ahem) updating.
For a quick look at the kinds of things you can do with lift, run
You can also
To Eclipsify, make sure you have the Scala Development Tools plugin installed, then run
Happy Lifting!
The instructions at the CircleShare lift blog are still current up to a point, at least as far as the prerequisites are concerned. I'm running from trunk, so my source code checkout was a little different:
svn co http://liftweb.googlecode.com/svn/liftweb
After that, the available instructions need a little (ahem) updating.
For a quick look at the kinds of things you can do with lift, run
mvn install
in the liftweb
(top level) directory to build everything. When that's done, cd sites/project_name
, where project_name
is one of example
, hellolift
, or skittr
. From there, run mvn jetty:run
to start a Jetty web server running on some port on localhost. At this writing, example
and hellolift
run on port 8888, while skittr
runs on 8889. Don't ask me why.You can also
mvn install
individual projects if you want, but I don't see any harm in building them all in one shot.To Eclipsify, make sure you have the Scala Development Tools plugin installed, then run
mvn eclipse:eclipse
in the project you want to be able to work with in Eclipse. You can do this for the lift source code itself (under the liftweb/lift
subdirectory), or any of the example projects (liftweb/sites/project_name
where project_name
is as above). Happy Lifting!
Fun With Scala: Liftweb
In my last post, I poked some serious fun at a bunch of mainstream languages (actually, their Internet communities), but left Scala pretty much alone. I chose Ruby and Java as my main characters because those are the languages I read the most about, and whose pundits' positions I felt like I could parody most accurately.
I've been interested in Scala since I first came across it, but I haven't really had to time to do much with it. I like its functional leanings, and I am intrigued by its ability to run on the JVM and interoperate seamlessly with existing Java libraries. Although when I say "interoperate", I have to acknowledge that it's a one-way street, as far as I know. Scala code can certainly invoke methods on Java classes. I have yet to figure out how to make the reverse happen (not that I've tried very hard). If I can, I will definitely try to make use of that capability.
So I've decided to do something real with Scala, to force myself to get to know it better. I've chosen Liftweb as my target project, largely because it's there and it's the only substantive chunk of Scala code I've found. You may have read about its claims to be able to implement a full-on Twitter clone in less than 1000 lines of code, with the capability of handling one million or more users on a single commodity PC.
Attention has been paid.
I've been interested in Scala since I first came across it, but I haven't really had to time to do much with it. I like its functional leanings, and I am intrigued by its ability to run on the JVM and interoperate seamlessly with existing Java libraries. Although when I say "interoperate", I have to acknowledge that it's a one-way street, as far as I know. Scala code can certainly invoke methods on Java classes. I have yet to figure out how to make the reverse happen (not that I've tried very hard). If I can, I will definitely try to make use of that capability.
So I've decided to do something real with Scala, to force myself to get to know it better. I've chosen Liftweb as my target project, largely because it's there and it's the only substantive chunk of Scala code I've found. You may have read about its claims to be able to implement a full-on Twitter clone in less than 1000 lines of code, with the capability of handling one million or more users on a single commodity PC.
Attention has been paid.
Wednesday, October 03, 2007
The Last Language War / Language Trolling Post You'll Ever Need To Read (Hopefully)
Moderator: Greetings, and welcome to the First-And-Possibly-Last-Ever Pan-Computer-Programming-Language Conference (FAPLEPCPLC). I am joined on stage tonight by many distinguished, high-profile computer programming languages. Each is highly regarded by its devotees, and I for one look forward to hearing what each has to say.
Ruby (grabbing the microphone): Um so yeah I'd just like to kick this bad boy off by saying that THE REST OF YOU SUCK A**!!! Yeah, I said it! The A-word! A**! Oh yeah! Boom, baby! Woo! Ruby FTW!
Java (rolling its eyes): Oh, real mature. I, on the other hand, would like to state that I have important work to get done in the Enterprise, so let's not waste everyone's time. I suggest we proceed using the Computer Programming Languages Discussion Pattern as implemented in JSR-6942, the Java™ Absolutely Void-of-Acronyms Talking About Languages at Konferences (JAVATALK™ which by the way is not an acronym for anything) specification.
Ruby: Dude! I just wrote a full working clone of Google while you were giving your riveting little speech there!
Moderator: Oh, bravo, Ruby! I'd like to see that. Where is it deployed?
Ruby: Umm....
Lisp: In the beginning, there was the Lambda. And John McCarthy saw the Lambda. And John McCarthy saw that the Lambda was very good.
Ruby (rolling its eyes): Here we go...
Lisp: And John McCarthy spake, and lo! the tongue he spake was sexp...
Ruby: He said sex! Heeheeheeheehee!
Erlang: If I may, I've been running some ideas by the other panelists, all at the same time of course, not that that's any big deal...
Ruby (rolling its eyes): And here we go with the concurrency...
Java: Gosh darn it, Ruby! There's no need to pooh-pooh everything someone else says, eh?
Ruby: He said poo-poo! Heeheeheeheehee!
Java: Ruby, I swear, one of these days...
Ruby: Hey, don't give me any of your static! Heeheeheeheehee! See what I did there?! "Static"?! 'Cause I'm so dynamic?! GET IT?!!! DAMN, where's my BAWLS Guarana...?
Ruby (seconds later): I said balls! Heeheeheeheehee!
C#: Developers! Developers! Developers! Developers!
Erlang: ...I'd share my results with you, but it's going to take a while to retrieve them from the filesystem...
COBOL: keels over, only to be frantically revived by about three banks.
Basic: Actually, I have a question for Ruby...
Haskell: Me too...
ML: Hey, Ruby, what's your answer to...
Ruby: Hey, hey, hey now! Not too many at a
Java: smiles
bash:
The Lambda Calculus: Actually, if we could all just take a moment to reflect on the implications of the Church-Turing thesis...
Everyone Else: Oh, SHUT UP!!!!!!
Scala: says nothing, but sits quietly observing, taking notes, and learning a lot.
Moderator: Well, I think it's about time to bring some closure to this, um, lively...discussion...
Java (sharply): Hey! We're working on it!
Lisp: ))))))))))))))))))))))
Ruby (grabbing the microphone): Um so yeah I'd just like to kick this bad boy off by saying that THE REST OF YOU SUCK A**!!! Yeah, I said it! The A-word! A**! Oh yeah! Boom, baby! Woo! Ruby FTW!
Java (rolling its eyes): Oh, real mature. I, on the other hand, would like to state that I have important work to get done in the Enterprise, so let's not waste everyone's time. I suggest we proceed using the Computer Programming Languages Discussion Pattern as implemented in JSR-6942, the Java™ Absolutely Void-of-Acronyms Talking About Languages at Konferences (JAVATALK™ which by the way is not an acronym for anything) specification.
Ruby: Dude! I just wrote a full working clone of Google while you were giving your riveting little speech there!
Moderator: Oh, bravo, Ruby! I'd like to see that. Where is it deployed?
Ruby: Umm....
Lisp: In the beginning, there was the Lambda. And John McCarthy saw the Lambda. And John McCarthy saw that the Lambda was very good.
Ruby (rolling its eyes): Here we go...
Lisp: And John McCarthy spake, and lo! the tongue he spake was sexp...
Ruby: He said sex! Heeheeheeheehee!
Erlang: If I may, I've been running some ideas by the other panelists, all at the same time of course, not that that's any big deal...
Ruby (rolling its eyes): And here we go with the concurrency...
Java: Gosh darn it, Ruby! There's no need to pooh-pooh everything someone else says, eh?
Ruby: He said poo-poo! Heeheeheeheehee!
Java: Ruby, I swear, one of these days...
Ruby: Hey, don't give me any of your static! Heeheeheeheehee! See what I did there?! "Static"?! 'Cause I'm so dynamic?! GET IT?!!! DAMN, where's my BAWLS Guarana...?
Ruby (seconds later): I said balls! Heeheeheeheehee!
C#: Developers! Developers! Developers! Developers!
Erlang: ...I'd share my results with you, but it's going to take a while to retrieve them from the filesystem...
COBOL: keels over, only to be frantically revived by about three banks.
Basic: Actually, I have a question for Ruby...
Haskell: Me too...
ML: Hey, Ruby, what's your answer to...
Ruby: Hey, hey, hey now! Not too many at a
[segfault]
Java: smiles
bash:
kill -9 self
The Lambda Calculus: Actually, if we could all just take a moment to reflect on the implications of the Church-Turing thesis...
Everyone Else: Oh, SHUT UP!!!!!!
Scala: says nothing, but sits quietly observing, taking notes, and learning a lot.
Moderator: Well, I think it's about time to bring some closure to this, um, lively...discussion...
Java (sharply): Hey! We're working on it!
Lisp: ))))))))))))))))))))))
Java 1, Intuition 0
Pop quiz: If you were given the following snippet of code:
If it helps, the contract for
My intuition was that it would return an empty
I mean think about it: what does it mean to split the empty string, on any character? The empty string has length 0, so what can you split it into? Two strings of length 0? But then you could do that ad infinitum, which would be silly.
So what Java actually does is return an array with one string: the empty string, or one string of length 0. Which implies that Java thinks the beginning of a string -- represented by the regex
What do you think?
"".split("\\s");...what would you expect the result to be?
If it helps, the contract for
String.split()
is that it takes a regular expression (in this case \s
means "anything that could reasonably be considered whitespace, like spaces, tabs, et cetera"), and returns a String[]
.My intuition was that it would return an empty
String[]
. (Author's Note: which would have been better than a null, but even that's not what I got.)I mean think about it: what does it mean to split the empty string, on any character? The empty string has length 0, so what can you split it into? Two strings of length 0? But then you could do that ad infinitum, which would be silly.
So what Java actually does is return an array with one string: the empty string, or one string of length 0. Which implies that Java thinks the beginning of a string -- represented by the regex
\A
, or ^
if you know the string you're regex-ing doesn't contain newlines -- is significant, but the end of a string (\Z
or $
) is "whitespace". I guess you could make a case for this making sense, but it doesn't match up with my intuition. What do you think?
Thursday, September 20, 2007
Scala Alert: bootclasspath/a Considered Canadian
Author's Note: Please forgive what is, essentially and unfortunately, a very bad pun. It's late, and I've had a long week, what with my Automata exam coming up and all. And, for the record, I have nothing against Canadians. James Gosling is Canadian. So is William Shatner. I once lived in Vancouver. Please don't hate.
So I was looking at
This points out some interesting interestingness. First off is: Scala executables are ultimately run by...
(Author's Other Note: Scala code gets compiled to JVM-compliant bytecode. Which is proof that real closures are theoretically possible in Java(TM). Although they are not likely to happen in your lifetime. But I digress.)
Second off is: buried amongst the options to
P.S.: It turns out there's also a
P.P.S.: I like the option to provide a default value for some environment variable that may not exist on a given system (e.g.,
P.P.P.S.: EMACS??? What the heck does that have to do with running a Scala executable? Time for some more research, eh?
So I was looking at
bin/scalac
and bin/scala
, the Scala toolset equivalents of javac
and java
, and I found myself learning some stuff. For instance, scala
(which executes a Scala, um, executable) is simply a shell script that sets a bunch of shell/environment variables and finally runs this command:
${JAVACMD:=java} ${JAVA_OPTS:=-Xmx256M -Xms16M} -Xbootclasspath/a:"$BOOT_CLASSPATH" -cp "$EXTENSION_CLASSPATH" -Dscala.home="$SCALA_HOME" -Denv.classpath="$CLASSPATH" -Denv.emacs="$EMACS" scala.tools.nsc.MainGenericRunner "$@"
This points out some interesting interestingness. First off is: Scala executables are ultimately run by...
java
! Or what passes for it on your system. Pretty neat, huh? Although it makes total sense if you think of java
as the command that starts up a JVM on some arbitrary .class
file, not the command that runs a Java(TM) language program.(Author's Other Note: Scala code gets compiled to JVM-compliant bytecode. Which is proof that real closures are theoretically possible in Java(TM). Although they are not likely to happen in your lifetime. But I digress.)
Second off is: buried amongst the options to
java
is the non-standard -Xbootclasspath/a:"$BOOT_CLASSPATH"
. Unlike plain ol' -Xbootclasspath
, this option lets you append some arbitrary jars to the regular bootclasspath (hence the /a
). In this case, what it appends is $SCALA_HOME/lib/scala-library.jar
if it exists, which gives the executable access to the Scala standard library. Which includes among other things the package that implements Scala Actors. P.S.: It turns out there's also a
-Xbootclasspath/p
, which stands for "prepend", but is not as funny when applied to Canadians.P.P.S.: I like the option to provide a default value for some environment variable that may not exist on a given system (e.g.,
${JAVACMD:=java}
).P.P.P.S.: EMACS??? What the heck does that have to do with running a Scala executable? Time for some more research, eh?
Friday, September 07, 2007
Recommended Reading: The Linguist
Do yourself a favor: start reading The Linguist, by Steve Kauffman, who speaks 9 languages and founded thelinguist.com as a resource for those who want to learn a new language. I particularly like the following quote, from the entry Why children learn languages better than adults:
I took Spanish for three years in high school (Hola, Srta. Chambless!), and I was very fortunate that my teacher taught the class in Spanish, and required us to participate by speaking Spanish. After the first six weeks of the first year, no English was allowed, and precious little during that time. It was brilliant. It forced us to learn Spanish the way kids in Spanish-speaking families learn it - by experiencing it and figuring things out, not by sitting at a desk with a vocab list and a dictionary.
I've often wondered why no one has developed a computer language training curriculum modelled after some of the more effective human language training methods. But that's beside the point of Kauffman's thesis above. Ultimately, we learn what we love. And we love what we find out for ourselves as the result of exploring the things we find interesting, not what is hammered into us by some random instructor.
Children have not yet been converted from naturally curious language explorers into teacher-dependent grammar learners.
I've often wondered why no one has developed a computer language training curriculum modelled after some of the more effective human language training methods. But that's beside the point of Kauffman's thesis above. Ultimately, we learn what we love. And we love what we find out for ourselves as the result of exploring the things we find interesting, not what is hammered into us by some random instructor.
Friday, August 17, 2007
Scala: The Next Next Java!
Yeah, yeah. I know. It's only been recently that Erlang has started being touted as "the next Java". I just thought I'd get a jump on crowning the next next Java. Oh, sure, no one gets to officially be The Next Anything without a Pragmatic Bookshelf book backing it, but I'm sure it's just a matter of time before a Scala book is announced.
After all, Scala shares a lot of the advantages of Erlang -- principally, its support for Actor-based concurrent programming and hence its powers of super-scaling. But it also has a secret weapon that Erlang doesn't have: it runs on the JVM! Yes, that JVM! It also has access to the multitudinous multitudes of existing Java libraries out there, making for a (potentially) easier migration path.
No need to wait for Java 7 (or (possibly much (much)) greater) for closures in Java! Write some Scala scaffolding around your existing Java classes and have them today!
Le roi mort! Vive le roi!
After all, Scala shares a lot of the advantages of Erlang -- principally, its support for Actor-based concurrent programming and hence its powers of super-scaling. But it also has a secret weapon that Erlang doesn't have: it runs on the JVM! Yes, that JVM! It also has access to the multitudinous multitudes of existing Java libraries out there, making for a (potentially) easier migration path.
No need to wait for Java 7 (or (possibly much (much)) greater) for closures in Java! Write some Scala scaffolding around your existing Java classes and have them today!
Le roi mort! Vive le roi!
Thursday, August 09, 2007
The Good, The Snide, and The Ugly (More on Autoboxing)
Wow. You know you've hit the big time when famed Java geek author Norman Richards gets all snarky on your sh*t. I would like to point out, though, that I never said autoboxing was "evil". I just said it was !cool. And no amount of Richards' sarcastic aping of my (deliberately) satirical-yet-lighthearted take on it is going to convince me otherwise.
The problem, of course, lies in the fact that, pre-autoboxing, there was no question of an assignment to a variable of type
Pre-autoboxing, Eclipse would have flat out disallowed the assignment of an
In true famed Java geek author style, though, Richards totally misses the pedagogical point when he uses as his sarcastixample the potential NPE-ness of code that deals with
I've actually been waiting for someone to snark on my (deliberately) satirical-yet-lighthearted insinuation that I rely too much on my IDE. That was the first point I took away from this whole exercise, and believe me I will be more wary in the future. I'm pretty sure I'm not the first to be burned by such an over-reliance. But in this case, even if I were using Emacs and the command line, I would still have fallen victim to this problem. The fault lies in the language, not in my reliance on any tool.
Bottom line: I don't think it should be too hard to excuse someone who, when seeing an NPE in his stack trace, doesn't leap immediately at the assignment to the
But maybe that's just me.
The problem, of course, lies in the fact that, pre-autoboxing, there was no question of an assignment to a variable of type
int
throwing an NPE. For the first 87% of Java's numerical-version-life you just couldn't do it. The concept had no meaning, like the questions "what is north of the North Pole?", or "what color is up?". Or "can I borrow your copy of 'XDoclet in Action'?" (Author's note: because no one in my office has one).Pre-autoboxing, Eclipse would have flat out disallowed the assignment of an
Integer
to an int
. And here's the important point: Eclipse would have disallowed it, because the compiler would have disallowed it. Because you couldn't assign an object reference to a primitive type, inadvertently or advertently. It wouldn't be a legal assignment, so it wouldn't compile, so there would be no way for it to throw an NPE in production. That's what static typing is supposed to ensure.In true famed Java geek author style, though, Richards totally misses the pedagogical point when he uses as his sarcastixample the potential NPE-ness of code that deals with
String
. Which is an object type. Which has since the beginning of Java time been subject to NPE's. As opposed to primitive types. Like int
. Which have not. Until autoboxing rocked our world and broke static typing.I've actually been waiting for someone to snark on my (deliberately) satirical-yet-lighthearted insinuation that I rely too much on my IDE. That was the first point I took away from this whole exercise, and believe me I will be more wary in the future. I'm pretty sure I'm not the first to be burned by such an over-reliance. But in this case, even if I were using Emacs and the command line, I would still have fallen victim to this problem. The fault lies in the language, not in my reliance on any tool.
Bottom line: I don't think it should be too hard to excuse someone who, when seeing an NPE in his stack trace, doesn't leap immediately at the assignment to the
int
variable.But maybe that's just me.
Tuesday, August 07, 2007
Autoboxing == !Cool
So you're finally getting used to the idea of Java 5's "feature" of converting between primitive types and their corresponding object wrappers (e.g., int -- Integer). You're getting to the point where you kind of trust it. You're even getting to the point where you kind of rely on it. Life is good.
Then you go too far and try something weird and exotic like, oh, I don't know:
No squiggly red line in your Eclipse editor. Cool! That must mean this will work! Because Java has static typing, and you can count on that! Yea you!
The problem is, it will work. It will work very nearly all the time. It will work until your method returns a
Not that you're bitter.
Then you go too far and try something weird and exotic like, oh, I don't know:
int sucker = thisMethodReallyReturnsAnIntegerNotAnInt();
No squiggly red line in your Eclipse editor. Cool! That must mean this will work! Because Java has static typing, and you can count on that! Yea you!
The problem is, it will work. It will work very nearly all the time. It will work until your method returns a
null
instead of an actual Integer
. At which point you'll get a NullPointerException buried in your Struts 2 / OGNL / JSP project's Eclipse console stack trace, followed by a two-hour detour into the debugger wondering what the heck is going on. I mean after all, your code compiled, right? It worked for the first hour, right? Static typing is good, right? Autoboxing works, right?Not that you're bitter.
Monday, July 09, 2007
Starting up a VPS on Slicehost
Author's Note: None of the following is intended to portray any of the companies named in an unflattering light. There were good things about all of them, along with the not-so-good. They all have their fans and their detractors. However, you know that phrase we geeks use when talking about our experiences with things technical? YMMV? Well, my mileage varied, considerably. To the point where I changed vehicles.
My first experience with running Rupp Consulting Services, LLC on VPS (Virtual Private Server) was with Rimuhosting. This was after some, shall we say, sub-optimal experiences with, in chronological order, Yahoo!, Textdrive, and Dreamhost. I went to VPS primarily to relieve the pain of memory/resource constraints/contention, and the inevitable shared downtime that comes with running a shared server.
Rimuhosting, I must say, were great about functionality and support. I left them only reluctantly, having decided to farm out my email and my blog to Google, and not seeing the need to pay for my web presence. Well, that lasted about a month, and I've decided to jump back in. I considered going back to Rimu, but something I read on a blog convinced me to give Slicehost a shot. So far I'm glad I did.
For $20/month, I get root access to my very own "slice" with 256MB memory, 10GB storage, and 100GB bandwidth/month. This compares to 96-128MB / 4GB / 30GB bandwidth on Rimu for the same price. You have to move up to Rimu's $40 plan to even get close to these specs (224MB / 4-8GB / 60GB). So space and bandwith are not a problem. However I do expect to bump up against my memory limit once I start doing any serious Rails development, which is my primary reason for getting back in the VPS game.
One of the reasons for this post, in fact, is to document some of the extra work I had to do to get Ruby and Rails going once my slice was up and running. First, I followed the instructions here to lockdown logins and SSH ports and stuff.
This is one of the first chances Slicehost had to shine. You see, I stupidly locked myself out after the last step (installing the iptables firewall). Slicehost provides an Ajax-based console for just such an occasion. Unfortunately, that wasn't working for me (note to self: enable cookies in Safari next time!), so I needed an even more drastic solution: rebuilding my slice. Fortunately, I was only backing out 10 minutes of work, so this was a viable option. Again, Slicehost really shines here by letting you rebuild automatically from an option on their management screen. A few clicks of the mouse and five minutes of waiting and I got to start fresh, this time being more careful with iptables and actually proofreading my changes and stuff. Amazing what that will do.
Then it was on to installing Ruby, et al. The yum package manager is your friend here. I considered downloading and building everything from source, but that takes a lot of time, and I'm actually becoming a fan (thanks to MacPorts on my home machines) of the whole package management concept. So a quick
Apparently, though, Slicehost installs a very slimmed-down version of Fedora, as I discovered when I tried to
Long story short, here's what I ended up having to
My first experience with running Rupp Consulting Services, LLC on VPS (Virtual Private Server) was with Rimuhosting. This was after some, shall we say, sub-optimal experiences with, in chronological order, Yahoo!, Textdrive, and Dreamhost. I went to VPS primarily to relieve the pain of memory/resource constraints/contention, and the inevitable shared downtime that comes with running a shared server.
Rimuhosting, I must say, were great about functionality and support. I left them only reluctantly, having decided to farm out my email and my blog to Google, and not seeing the need to pay for my web presence. Well, that lasted about a month, and I've decided to jump back in. I considered going back to Rimu, but something I read on a blog convinced me to give Slicehost a shot. So far I'm glad I did.
For $20/month, I get root access to my very own "slice" with 256MB memory, 10GB storage, and 100GB bandwidth/month. This compares to 96-128MB / 4GB / 30GB bandwidth on Rimu for the same price. You have to move up to Rimu's $40 plan to even get close to these specs (224MB / 4-8GB / 60GB). So space and bandwith are not a problem. However I do expect to bump up against my memory limit once I start doing any serious Rails development, which is my primary reason for getting back in the VPS game.
One of the reasons for this post, in fact, is to document some of the extra work I had to do to get Ruby and Rails going once my slice was up and running. First, I followed the instructions here to lockdown logins and SSH ports and stuff.
This is one of the first chances Slicehost had to shine. You see, I stupidly locked myself out after the last step (installing the iptables firewall). Slicehost provides an Ajax-based console for just such an occasion. Unfortunately, that wasn't working for me (note to self: enable cookies in Safari next time!), so I needed an even more drastic solution: rebuilding my slice. Fortunately, I was only backing out 10 minutes of work, so this was a viable option. Again, Slicehost really shines here by letting you rebuild automatically from an option on their management screen. A few clicks of the mouse and five minutes of waiting and I got to start fresh, this time being more careful with iptables and actually proofreading my changes and stuff. Amazing what that will do.
Then it was on to installing Ruby, et al. The yum package manager is your friend here. I considered downloading and building everything from source, but that takes a lot of time, and I'm actually becoming a fan (thanks to MacPorts on my home machines) of the whole package management concept. So a quick
yum install -y ruby
got me started.Apparently, though, Slicehost installs a very slimmed-down version of Fedora, as I discovered when I tried to
yum install -y rubygems
. I had problems with missing libraries, which I was able to cure with yum install -y ruby-devel
. Which exposed some other missing goodies. Rinse, lather, repeat.Long story short, here's what I ended up having to
yum install
to be able to build native extensions for gems and do other *nix hackery I'm used to being able to do out of the box:- wget
- make
- which
- tar
- gzip
- gcc
I've also installed nginx
(vice Apache), mongrel
, subversion
, and mysql
- the usual suspects. nginx
works as advertised, and one Mongrel instance handles my Mephisto-generated site traffic nicely. Just that, though, is enough to get me close to 200MB, and that's without the Trac instance I plan to install later. So I may be upgrading my plan even sooner than I expected.
Wednesday, June 27, 2007
On the Importance of Being Multi-Lingual
My day job is currently focused on porting an Eclipse RCP ("Rich Client Platform") Java application -- essentially a WORA desktop app -- to a Struts2/JSP/Hibernate webapp. We're using a fair amount of JavaScript (via Prototype and Scriptaculous, along with some home-grown code) for the things you'd normally use JavaScript for. Nothing out of the ordinary there.
One of the home-grown JavaScript bits that I've been working with recently is a text-box filter. You type in the text you want to filter on in a text-box, and the table rows (or li's, or what-have-you) that don't contain that text get hidden, leaving only those that pass the filter. Again, pretty standard stuff.
The fun started when I wanted to add the ability to filter not just on text, but on a time range chosen from a dropdown menu, like, say, "show me only the results that were posted within the last two hours". I wanted to re-use the existing filter code as much as possible, because a lot of it is exactly what I need, but I needed a way to specify comparing two times (the time of each post versus the time range selected) instead of the default behavior of checking for a match on the input text.
(Author's Note: Those of you who are familiar with Ruby probably already know exactly where I'm going with this. Bear with me while I build the suspense a little longer).
If the filter were written in Java (pre-closures Java, anyway <wink/>), I would have no choice but to jump through some serious refactoring hoops -- implement the new behavior, probably as a new set of methods, and then add in some way to designate which behavior I wanted either by introspecting on my arguments, or by an explicit switch, or...yuck. And if I were only aware of Java idoms, there would be nothing stopping me from taking the same approach in JavaScript.
Fortunately, I spent the bulk of last year doing some serious Ruby programming, which forced me to get used to the idea of blocks. Equally fortunately, I've taken some time to get familiar with idiomatic JavaScript programming, and learned that functions are first-class datatypes in JavaScript, which means they can work pretty much like blocks do in Ruby.
And so I was able to solve my problem with minimal impact to the existing codebase, by adding a parameter to the constructor (an optional block/function, defaulting to the existing "match" behavior), and making the code that does the actual filtering call that anonymous function rather than explicitly calling "match". One optional parameter and one (tiny) layer of indirection later, and I've got a solution that will never need extending again. The next time I come up against a type of filter that I haven't already implemented, I can do so on-the-fly, by building a function that implements the exact comparison behavior I want and passing it to the generic filter.
Do yourself a favor: stop hating on Ruby because you're irritated by all the attention it gets. Stop assuming that if you learn idiomatic Ruby you're going to have to turn in your "Java ROOLZ, Ruby DROOLZ" badge and your "I (heart) static typing" secret decoder ring. Free your mind, and the rest will follow. And whatever other inspirational pop lyric you care to apply here.
Learning another language (doesn't have to be Ruby; could be Python, or Lisp, or Erlang, or Scala, or...) can only increase your problem-solving capability. And, after all, isn't that what we're all about? Solving problems?
One of the home-grown JavaScript bits that I've been working with recently is a text-box filter. You type in the text you want to filter on in a text-box, and the table rows (or li's, or what-have-you) that don't contain that text get hidden, leaving only those that pass the filter. Again, pretty standard stuff.
The fun started when I wanted to add the ability to filter not just on text, but on a time range chosen from a dropdown menu, like, say, "show me only the results that were posted within the last two hours". I wanted to re-use the existing filter code as much as possible, because a lot of it is exactly what I need, but I needed a way to specify comparing two times (the time of each post versus the time range selected) instead of the default behavior of checking for a match on the input text.
(Author's Note: Those of you who are familiar with Ruby probably already know exactly where I'm going with this. Bear with me while I build the suspense a little longer).
If the filter were written in Java (pre-closures Java, anyway <wink/>), I would have no choice but to jump through some serious refactoring hoops -- implement the new behavior, probably as a new set of methods, and then add in some way to designate which behavior I wanted either by introspecting on my arguments, or by an explicit switch, or...yuck. And if I were only aware of Java idoms, there would be nothing stopping me from taking the same approach in JavaScript.
Fortunately, I spent the bulk of last year doing some serious Ruby programming, which forced me to get used to the idea of blocks. Equally fortunately, I've taken some time to get familiar with idiomatic JavaScript programming, and learned that functions are first-class datatypes in JavaScript, which means they can work pretty much like blocks do in Ruby.
And so I was able to solve my problem with minimal impact to the existing codebase, by adding a parameter to the constructor (an optional block/function, defaulting to the existing "match" behavior), and making the code that does the actual filtering call that anonymous function rather than explicitly calling "match". One optional parameter and one (tiny) layer of indirection later, and I've got a solution that will never need extending again. The next time I come up against a type of filter that I haven't already implemented, I can do so on-the-fly, by building a function that implements the exact comparison behavior I want and passing it to the generic filter.
Do yourself a favor: stop hating on Ruby because you're irritated by all the attention it gets. Stop assuming that if you learn idiomatic Ruby you're going to have to turn in your "Java ROOLZ, Ruby DROOLZ" badge and your "I (heart) static typing" secret decoder ring. Free your mind, and the rest will follow. And whatever other inspirational pop lyric you care to apply here.
Learning another language (doesn't have to be Ruby; could be Python, or Lisp, or Erlang, or Scala, or...) can only increase your problem-solving capability. And, after all, isn't that what we're all about? Solving problems?
Monday, June 25, 2007
meme.lolcats.die.die.die
The title says it all, really. But I hate one-liner blog posts, so here are some other "memes" that I'd really, really like to see go away:
Whenever I see a job posting like this:
However, we both know that at the end of the day, you're still going to be a clock-watching, interchangeable-human-resource-administrating, two-martini-lunch-taking, office-chair-warming executive type with a pathological distrust of technology. And I'm still going to be a code monkey. So let's just call a spade a spade, shall we?
"Incentivize". "Monetize". "Reify".
Gagize me.
Look, if I really want you to know about my third lung or the time I gave CPR to that baby seal in Alaska, chances are you're my wife or my physician and I've already told you. Otherwise, buzz off.
meme.edgy.job.posting.die.die.die
Whenever I see a job posting like this:SELECT * FROM applicants WHERE knows_php=1 AND has_life=0I simply:
ORDER_BY tattoo_count DESC;
DELETE FROM jobs_i_am_interested_in WHERE employer=that_loser;I mean think about it. If you're that darn clever, what do you need me for? And what happens if you do hire me and I turn out not to be clever enough for you? Why should I leave my current position (which is at a pretty awesome startup, btw) for the eventual:
INSERT INTO ranks_of_the_unemployed VALUES(that_chump_who_answered_our_lame_job_posting);Same goes for any company looking for any kind of "programming god", "rock star", or "l33t hax0r". In fact, if any part of your job post is ultra-snarky, ultra-hip, or smacks of leetspeak, there's a good chance I will sprain my finger deleting it. I know you're trying to be, like, all Web-Too-Oh and everything, but who do you think you're fooling? In RL (author's note: "Real Life") you're in your late 40's, and the closest thing you have to "l33t cr3d" is the time you virtually hit on that brooding emo chick whose profile you happened across while prowling MySpace. (Which, by the way -- ewww).
However, we both know that at the end of the day, you're still going to be a clock-watching, interchangeable-human-resource-administrating, two-martini-lunch-taking, office-chair-warming executive type with a pathological distrust of technology. And I'm still going to be a code monkey. So let's just call a spade a spade, shall we?
meme.verbing.die.die.die
"Incentivize". "Monetize". "Reify".Gagize me.
Verbing weirds language -- Calvin, of Calvin and Hobbes
meme.meme.tag.die.die.die
Look, if I really want you to know about my third lung or the time I gave CPR to that baby seal in Alaska, chances are you're my wife or my physician and I've already told you. Otherwise, buzz off.
Friday, June 22, 2007
Suggested Reading: "Rich Programmer Food"
Steve Yegge: "Rich Programming Food", in which Steve explains why Compilers is the second most important course you can take as a computer science major, and should be required in any self-respecting computer science curriculum. I couldn't agree more.
I had to fight the temptation to label this "Required Reading", because I know that some of you who follow this link will be turned off by Steve's style, which I appreciate but which even I find a bit rambly at times. And I expect that his sense of humor, which resonates with mine and I therefore appreciate, is not for everyone.
But I do consider this worth a read, primarily because I whole-heartedly agree with Steve's contention that knowing how compilers work is key to understanding computer science. It is also, not coincidentally, key to getting better as a programmer.
I used to think that those two concepts were largely orthogonal. I still believe it's possible to be a successful programmer in industry without a good understanding of computer science. I myself have managed nearly 20 years of gainful employment on the strength of a computer science degree, some native ability, and the credibility that naturally goes with that much experience.
But it's been only recently that I've really started to get it, to understand the why's and wherefore's. Understanding state machines, and parse trees, and Big-O notation, and ... all that stuff I couldn't be bothered with as an undergrad ... understanding it as a grad student has made my life as a professional programmer a lot easier.
If you do follow the link, prepare to be challenged. And even if you don't make it all the way through the post, make sure to scroll down to the bottom for the punch line. Again -- I couldn't agree more.
I had to fight the temptation to label this "Required Reading", because I know that some of you who follow this link will be turned off by Steve's style, which I appreciate but which even I find a bit rambly at times. And I expect that his sense of humor, which resonates with mine and I therefore appreciate, is not for everyone.
But I do consider this worth a read, primarily because I whole-heartedly agree with Steve's contention that knowing how compilers work is key to understanding computer science. It is also, not coincidentally, key to getting better as a programmer.
I used to think that those two concepts were largely orthogonal. I still believe it's possible to be a successful programmer in industry without a good understanding of computer science. I myself have managed nearly 20 years of gainful employment on the strength of a computer science degree, some native ability, and the credibility that naturally goes with that much experience.
But it's been only recently that I've really started to get it, to understand the why's and wherefore's. Understanding state machines, and parse trees, and Big-O notation, and ... all that stuff I couldn't be bothered with as an undergrad ... understanding it as a grad student has made my life as a professional programmer a lot easier.
If you do follow the link, prepare to be challenged. And even if you don't make it all the way through the post, make sure to scroll down to the bottom for the punch line. Again -- I couldn't agree more.
Tuesday, June 19, 2007
Hear, Hear! (A Postscript to My Take on Closures in Java)
From Revised Report on the Algorithmic Language Scheme, Introduction, Paragraph 1, Sentence 1:
P.S.: This was written 'way back in 1998. Another fun quote:
Programming languages should be designed not by piling
feature on top of feature, but by removing the weaknesses
and restrictions that make additional features appear necessary.
P.S.: This was written 'way back in 1998. Another fun quote:
Those who cannot remember the past are condemned to repeat it. -- Santayana
Wednesday, June 13, 2007
Hacking JRuby: More on Method Arguments
I closed my last post with this observation:
In the comments, Ola Bini -- ThoughtWorker and JRuby committer -- graciously pointed out that there are, in fact, two static methods that we can use to simulate the function of
As Ola points out, this still is not quite the equivalent of MRI
I do plan to use
"...JRuby does not have an equivalent for rb_scan_args(), or at least not one that is called on a per-method basis."
In the comments, Ola Bini -- ThoughtWorker and JRuby committer -- graciously pointed out that there are, in fact, two static methods that we can use to simulate the function of
rb_scan_args()
, both found in the org.jruby.runtime.Arity
class: checkArgumentCount()
, and scanArgs()
.checkArgumentCount()
does just what it says. You pass it an array of argument values, along with the minimum and maximum number of all arguments, and it verifies that the actual number of arguments falls within that range (inclusive). So this provides some basic sanity checking of the number of arguments passed to the method you're implementing. If the number of arguments is valid, checkArgumentCount()
returns the actual number of arguments passed. I guess you could argue that this isn't very useful, since you already know the number of arguments you expect, as well as the number given to you. But it is used in a number of places in the JRuby source code.scanArgs()
will do a little more work for you. It will do the same sanity checking (it actually calls checkArgumentCount()
to do so), although you specify the numbers slightly differently, passing the number of required arguments along with the number of optional arguments. If the actual number of arguments passed is valid, scanArgs()
then creates a new array of length required + optional, copies the values of any arguments passed in (all required arguments plus any provided optional arguments), sets the values of any non-provided optional arguments to nil, and returns the new array. This actually is useful, as it transforms a variable-length array of arguments into one of fixed length, where the fixed length is always equal to the maximum number of arguments your method will accept.As Ola points out, this still is not quite the equivalent of MRI
rb_scan_args()
, in that it doesn't handle "rest" or "block" arguments. But it does offload from us some of the burden of handling variable length argument lists.I do plan to use
scanArgs()
to finish up my implementation of BigDecimal.mode()
, but in order to so meaningfully I'm also going to have to change mode()
's method definition from taking a fixed number of arguments to taking a variable number of arguments (the first argument is required, the second is optional). That's going to involve digging into JRuby's mechanism for defining Ruby methods, which I've researched and found to be pretty cool, but it's going to take another few blog posts to sort out. Stay tuned...
Tuesday, June 12, 2007
Hacking JRuby: BigDecimal and Ruby Internals
I've submitted another patch for JRuby (viewable here), to implement the
The first parameter to
If the first argument is a Fixnum that is not equal to
Simple, huh?
Introducing:
One of the first things MRI does (in a lot of methods, as it turns out) is to call the function
The format string consists, minimally, of two digits. The first digit is the number of required arguments, the second is the number of optional arguments.
For example,
BigDecimal.mode()
class method. In doing so, I learned quite a bit about JRuby's implementation strategy, as well as the internals of the C source code for MRI (Matz's Ruby Implementation) Ruby.BigDecimal.mode()
explained
BigDecimal.mode()
is a funky little method in the BigDecimal
module, which is not part of the core API but part of the standard library that ships with Ruby. It's kind of multi-variate -- what it does, exactly, depends on how it's called. The first parameter to
BigDecimal.mode()
is required, and it must be a Fixnum representing either the constant BigDecimal::ROUNDING_MODE
or the exception mode to be set (more on that later). If it's BigDecimal::ROUNDING_MODE
and there is no second argument, then mode()
just returns the current rounding mode. If a second argument is present, it must also be a Fixnum, and it must equate to one of the seven rounding modes Ruby recognizes (e.g., BigDecimal::ROUND_UP
, BigDecimal::ROUND_FLOOR
, etc.). In this case, mode()
sets the rounding mode (for all BigDecimal
s, remember, since this is a class method) to the value of the second argument.If the first argument is a Fixnum that is not equal to
BigDecimal::ROUNDING_MODE
, then it is expected to have one of its bits set to correspond to one of the known exception modes (e.g., BigDecimal::EXCEPTION_INFINITY
). Again, if there is no second argument, mode()
simply reports the current exception mode(s) (each bit in the returned value corresponds to a single exception mode set). If there is a second argument, it must be one of 'true' or 'false'. If 'true', mode()
sets the mode passed in the first argument. If 'false', mode()
unsets (i.e., turns off) the mode passed in the first argument.Simple, huh?
Not So Fast...
When I picked up this task,mode()
was just a default stub that printed a message to the console and returned nil
. Not a lot to go on there. So I turned to the MRI source code to figure out just what it was supposed to do. Introducing: rb_scan_args()
One of the first things MRI does (in a lot of methods, as it turns out) is to call the function rb_scan_args()
), which is implemented in the file class.c
with the following signature:int rb_scan_args(int argc, const VALUE *argv, const char *fmt, ...)It takes the number of arguments passed, a pointer to a structure containing the values of those arguments, a format string of some sort, and...some other stuff. The number and values of the arguments are self-explanatory, but the format string and the trailing "other stuff" are decidedly not, so let's take a look at them.
The format string consists, minimally, of two digits. The first digit is the number of required arguments, the second is the number of optional arguments.
rb_scan_args
parses the format string to find these numbers, then it walks the list of argument values and stuffs each value into its corresponding reference (which is what the "other stuff" in the signature actually is: a group of references to store the values of the arguments in).For example,
BigDecimal.mode()
makes this call to rb_scan_args
:if(rb_scan_args(argc,argv,"11",&which,&val)==1) val = Qnil;In English:
- get one required argument and store its value in the variable
which
- get the optional second argument if it exists and put its value in
val
- if
rb_scan_args
returned 1 (i.e., only one argument was provided), then set the value of the optional argument to its default ofnil
Meanwhile, Back in JRuby...
This has gone on a bit long, so I'll just close by saying that JRuby does not have an equivalent forrb_scan_args()
, or at least not one that is called on a per-method basis. The runtime is responsible for bundling arguments and calling the appropriate Java method based on the number of arguments actually present. This causes a bit of a problem right now for class methods that take optional arguments (as BigDecimal.mode()
does), but that's a subject for another post.
Thursday, June 07, 2007
Umm, What Exactly Is Google Trying To Tell Me Here?
Dear Google,
Look, I know this is a weblog about programming and Java and stuff, and I know you're just trying to help out with the targeted, "relevant" ads. And I'm okay with that. Really, I am.
However...
I think the whole "help out the poor, socially inept Revenge of the Nerds rejects" vibe is a bit much, huh?
Love,
David
Look, I know this is a weblog about programming and Java and stuff, and I know you're just trying to help out with the targeted, "relevant" ads. And I'm okay with that. Really, I am.
However...
I think the whole "help out the poor, socially inept Revenge of the Nerds rejects" vibe is a bit much, huh?
Love,
David
Kickin' it Old School: Inspecting $CLASSPATH with sed and grep
Here's a fun
Note that the <return> above means to actually hit the return key following the backslash. This bit of awkwardness is
I needed this in the context of figuring out a broken Ant build while testing some changes I'm making to JRuby. Unfortunately, even the pretty-printed version of my Ant classpath was too long to sift through with the naked eye, so I turned to
That is, break up the classpath into one line per entry, and show me only the entries for jruby.jar. With this, I was able to determine that I had an older version of jruby.jar on my classpath that is incompatible with the current trunk. Problem solved!
I love a happy ending.
sed
one-liner that I used today to break up the entries in my $CLASSPATH:
echo $CLASSPATH | sed 's/:/\<return>
/g'
Note that the <return> above means to actually hit the return key following the backslash. This bit of awkwardness is
sed
's way of specifying a literal newline as part of the substitution string (how literal can you get?). The net effect is to replace the colon characters with newlines, resulting in a display of my classpath with one entry per line.I needed this in the context of figuring out a broken Ant build while testing some changes I'm making to JRuby. Unfortunately, even the pretty-printed version of my Ant classpath was too long to sift through with the naked eye, so I turned to
grep
to look for exactly what I needed:
echo $CLASSPATH | sed 's/:/\<return>
/g | grep jruby.jar'
That is, break up the classpath into one line per entry, and show me only the entries for jruby.jar. With this, I was able to determine that I had an older version of jruby.jar on my classpath that is incompatible with the current trunk. Problem solved!
I love a happy ending.
Tuesday, June 05, 2007
Rant: JSP + OGNL + Collections == Train Wreck
The Story So Far
In my day job I'm using Struts 2, with JSP as the view templating mechanism. I have a collection whose size I'd like to report on the page. Unfortunately for my sanity, I have recent experience with Ruby on Rails, in which such a thing is as simple as:<%= @myCollection.size %>
But no.
Problem #1
Struts 2 exposes properties on the Action class via OGNL, which has a nice, clean, property-based syntax, much like RHTML (which is what Rails uses for its view templates). So I should be able to ask for something like this:${myCollection.size}
Assuming, that is, that I have a method
getMyCollection()
defined on my action. Which I do. The problem here is that I also have to have a method called
getSize()
defined on whatever getMyCollection()
returns. Which is a java.util.Set
. Which, for some reason, does not have a getSize()
method. It has a size()
method instead. Apparently the designers of the Java 2 Collections API were feeling a mite saucy when they went a-designin', and were daring their overlords to punish them for ignoring the Java Beans method naming convention that, as it turns out, OGNL relies on heavily.D'oh!
No problem, though. OGNL doesn't require method names to be bean-compliant, it just prefers it in its chain of figuring out what the heck you're asking for. You can invoke any method directly, as in:
${myCollection.size()}
Problem solved! Let's save everything and reload:
Struts Problem Report
Struts has detected an unhandled exception:
Messages:
view.jsp(40,109) The function size must be used with a prefix when a default namespace is not specified
org.apache.jasper.JasperException: view.jsp(40,109) The function size must be used with a prefix when a default namespace is not specified
What the...?! Oh. The JasperException must mean that the
${...}
expression is being interpreted as JSP EL instead of OGNL. Bummer. Oh well. I'll just let the EL engine handle it.Which brings me to:
Problem #2
The JSP EL engine can't handle it. Neither the property syntax nor the method syntax works. The property syntax wants to use the (non-existent)getSize()
method too (go figure). And the method syntax doesn't exist. So. Since it looks like JSP EL trumps OGNL when the page is rendered, I'll just turn off JSP EL evaluation (in web.xml) and let OGNL handle everything.
Or not. That results in a ton of errors from pages in my app that rely on JSP EL.
Solution
I'll try to wrap this up. It turns out that the answer is JSP functions. "All" I have to do to get the number of items in my collection is write a public function class and implement a static method -- which I get to name anything I want! Just like the Collections API designers! -- that takes the collection as a parameter and returns its size.Oh, and I have to write a snippet of XML in the form of a .tld file that tells the JSP where to find this function.
Oh, and I have to declare the .tld as a taglib at the top of the JSP. And that's all I have to do.
Sheesh.
Conclusion
Fortunately, it turns out that the hardest part of this work has been done for me, in the form of the JSTL implementation (documented here) of several useful JSP functions, includinglength()
.It doesn't help my mood any that in Ruby -- if the problem existed to begin with, which it doesn't -- I could have solved it simply by adding a
getSize()
method, aliased to the size()
method of the Set
class, and everyone would be happy.Grumble.
Monday, June 04, 2007
Required Reading: Enforcing Strict Model-View Separation in Template Engines
So I've been reading Terence Parr's excellent addition to the Pragmatic Programmers catalog: The Definitive ANTLR Reference. This book is a lot of fun for me (I recommend it highly, btw), because it's gotten me thinking again about languages and language implementation issues in a way that I haven't had to since my first compilers class. Dr. Parr has a relaxed and informative writing style that can make you forget you're reading about some pretty hardcore computer science.
I wanted to do some more reading on StringTemplate, the templating engine ANTLR uses to emit the results of its translation, because I read that it can be (is) used to build websites as well. My searching led me to Dr. Parr's paper from 2004: Enforcing Strict Model-View Separation in Template Engines. In it, Parr explains -- and most importantly formalizes -- his ideas about the need for strict separation in view templating mechanisms, and how the vast majority of existing solutions (e.g., JSP, Velocity) fall far short, ironically because of the Turing-complete power they provide.
We Java programmers who have been around since the introduction of JSP have already been informally exposed to some of these concepts, in the form of the Model 1 -> Model 2 architecture evolution. One of Dr. Parr's key points, though, is that it's not enough simply to recommend avoiding using the full computational power available in (for example) JSP scriptlets. The temptation to use that power, even in seemingly innocuous ways, is simply too strong to be resisted for long. Parr's insight that having full access to the Turing-complete power inherent in JSP/Velocity/what-have-you actually gets in our way as developers reminds me of the 37Signals take on embracing constraint.
Mind you, I don't expect to be organizing a revolution around replacing my company's use of JSP with StringTemplate anytime soon. But I do plan to develop the personal discipline of following Dr. Parr's advice and embracing constraint by voluntarily avoiding the constructs that violate strict separation. That statement may seem to contradict the statement above about temptation being too strong, but I think that if enough individuals had the same resolve, we could improve the situation somewhat until a move to StringTemplate or its equivalent became politically feasible.
I wanted to do some more reading on StringTemplate, the templating engine ANTLR uses to emit the results of its translation, because I read that it can be (is) used to build websites as well. My searching led me to Dr. Parr's paper from 2004: Enforcing Strict Model-View Separation in Template Engines. In it, Parr explains -- and most importantly formalizes -- his ideas about the need for strict separation in view templating mechanisms, and how the vast majority of existing solutions (e.g., JSP, Velocity) fall far short, ironically because of the Turing-complete power they provide.
We Java programmers who have been around since the introduction of JSP have already been informally exposed to some of these concepts, in the form of the Model 1 -> Model 2 architecture evolution. One of Dr. Parr's key points, though, is that it's not enough simply to recommend avoiding using the full computational power available in (for example) JSP scriptlets. The temptation to use that power, even in seemingly innocuous ways, is simply too strong to be resisted for long. Parr's insight that having full access to the Turing-complete power inherent in JSP/Velocity/what-have-you actually gets in our way as developers reminds me of the 37Signals take on embracing constraint.
Mind you, I don't expect to be organizing a revolution around replacing my company's use of JSP with StringTemplate anytime soon. But I do plan to develop the personal discipline of following Dr. Parr's advice and embracing constraint by voluntarily avoiding the constructs that violate strict separation. That statement may seem to contradict the statement above about temptation being too strong, but I think that if enough individuals had the same resolve, we could improve the situation somewhat until a move to StringTemplate or its equivalent became politically feasible.
Closures in Java: Continued
In the comments section of my last post, user sanity makes the following astute observation, re: my closing remark about Java being a Turing complete language:
Well, pretty-much every programming language is Turing complete, so by that argument, why bother adding any feature to any language?
To which I reply: "Bravo!" and "Well said." I'll even take it one step further: anything that can be done in a high-level programming language can be done in assembler language, or even machine code. I know from hard experience, having implemented an encapsulation/data-hiding scheme using IBM mainframe assembler language, and having patched code using the raw machine opcodes in the same environment. Ah, the good old days!
So I'll go back to programming in assembler language, and the rest of you can either join me or take a well-earned break. Thanks for playing.
All right -- as much fun as the reductio ad absurdum game is, all it does is reduce the argument to an absurdity. It doesn't really point to a constructive resolution. Although, in this case, it does raise the question (note: notice I did not say, "begs the question," which is a solecism that I will reserve for another post) of the degree to which the effort involved in constructing a higher-level language offsets the effort involved in writing code in a low-level language.
I think we can all agree that programming in C or C++ is more pleasant and more efficient in terms of our time than programming in assembler language. You give up some flexibility (e.g., the ability to stuff a specific value in a specific register, direct access to all non-protected memory), but you gain recursion, stack management, register coloring -- all that good stuff.
Move up to Java, or Objective-C (version 2.0; coming soon!) and you get the added relief of automatic memory management (allocation and garbage collection). With Java and its ilk, you also get checked exceptions, which you may or may not appreciate.
Move up to Ruby (JRuby), or Python (Jython), or LISP, and you get dynamic typing and language/runtime-level support for first-class functions/blocks/closures.
So switching between these flavors of Turing-completeness makes sense. If you need close-to-the-metal performance with some measure of portability, use C. If you need rapid prototyping, use Ruby or Python. If you need to justify spending $300+ on IntelliJ IDEA, use Java. (Sorry, couldn't resist).
My point (and I do have one) is that the difference between Turing-complete-Java and Turing-complete-Java-now-with-closures! is just not worth the effort it looks like adding this nice-but-not-essential feature to the language will require.
Closures in Java will muddy an already-dense syntax even further. They will require more code butchery and internal gymnastics on behalf of the compiler. They will be devilishly hard to add in a way that makes sense, given Java's baked-in typing restrictions and flow control. And, in the end, they're going to satisfy the demands of a vocal minority. The masses will still take the path of least resistance, and will probably avoid closures like the plague. And those of us who make a point of being aware of non-Java programming idioms will appreciate the differences between the many available languages, and will continue to strive to use the best tool for the job at hand.
Well, pretty-much every programming language is Turing complete, so by that argument, why bother adding any feature to any language?
To which I reply: "Bravo!" and "Well said." I'll even take it one step further: anything that can be done in a high-level programming language can be done in assembler language, or even machine code. I know from hard experience, having implemented an encapsulation/data-hiding scheme using IBM mainframe assembler language, and having patched code using the raw machine opcodes in the same environment. Ah, the good old days!
So I'll go back to programming in assembler language, and the rest of you can either join me or take a well-earned break. Thanks for playing.
All right -- as much fun as the reductio ad absurdum game is, all it does is reduce the argument to an absurdity. It doesn't really point to a constructive resolution. Although, in this case, it does raise the question (note: notice I did not say, "begs the question," which is a solecism that I will reserve for another post) of the degree to which the effort involved in constructing a higher-level language offsets the effort involved in writing code in a low-level language.
I think we can all agree that programming in C or C++ is more pleasant and more efficient in terms of our time than programming in assembler language. You give up some flexibility (e.g., the ability to stuff a specific value in a specific register, direct access to all non-protected memory), but you gain recursion, stack management, register coloring -- all that good stuff.
Move up to Java, or Objective-C (version 2.0; coming soon!) and you get the added relief of automatic memory management (allocation and garbage collection). With Java and its ilk, you also get checked exceptions, which you may or may not appreciate.
Move up to Ruby (JRuby), or Python (Jython), or LISP, and you get dynamic typing and language/runtime-level support for first-class functions/blocks/closures.
So switching between these flavors of Turing-completeness makes sense. If you need close-to-the-metal performance with some measure of portability, use C. If you need rapid prototyping, use Ruby or Python. If you need to justify spending $300+ on IntelliJ IDEA, use Java. (Sorry, couldn't resist).
My point (and I do have one) is that the difference between Turing-complete-Java and Turing-complete-Java-now-with-closures! is just not worth the effort it looks like adding this nice-but-not-essential feature to the language will require.
Closures in Java will muddy an already-dense syntax even further. They will require more code butchery and internal gymnastics on behalf of the compiler. They will be devilishly hard to add in a way that makes sense, given Java's baked-in typing restrictions and flow control. And, in the end, they're going to satisfy the demands of a vocal minority. The masses will still take the path of least resistance, and will probably avoid closures like the plague. And those of us who make a point of being aware of non-Java programming idioms will appreciate the differences between the many available languages, and will continue to strive to use the best tool for the job at hand.
Wednesday, May 30, 2007
Closures in Java: Why Bother?
Yes, I know Closures are Cool. All the "cool", "new" dynamic languages, like Ruby (which is actually older than Java, BTW), have them, so if Java is going to be "cool", Java needs closures.
Or does it?
My original title for this entry was "Closures in Java: Lipstick on a Pig?", but I decided against that because I don't want to imply any disdain for Java. I've programmed professionally in everything from IBM mainframe assembler language to (shudder) COBOL to Java to Ruby. I spent most of last year developing websites in Ruby on Rails. I currently develop client and server-side solutions in Java.
Professionally, I can't afford to be a language - or even a language-feature - snob. There are things I used to be able to do in assembler language that I can't do in Java, or even (gasp) Ruby. Do you see me writing proposals to add BALR functionality to the Java language? Of course not. Java is not IBM mainframe assembler language. It was never intended to be.
Nor was it intended to be Ruby. Or Python. Or ML. Or LISP. Or whatever your particular favorite non-Java programming language happens to be.
I wish the Java Powers That Be would leave well enough alone, be happy with the success of Java and the JVM, and stop trying to be Everything To Everybody.
Enough with the inferiority complex, already.
So Java doesn't have first-class functions (which some would say aren't essential for closures, but if you're not going to have first-class functions, why bother?). Big deal. It has a large install-base. It has Tomcat. It has the JVM. It has static typing, which is great if you're into that kind of thing. In short, it has everything you need to develop useful, useable, functional programs.
Could Java be a better language? Probably. But could it be worse? Certainly (yes, I'm talking to you, C++).
So why not just let Java be Java? If you really need first-class functions/blocks/closures, use Smalltalk. Or Ruby. Or Python. Or Lisp. If you really need first-class functions/blocks/closures on the JVM, use Smalltalk/JVM. Or JRuby. Or Jython. Or Armed Bear Common Lisp. Or any of the (astonishingly large) number of non-Java languages that have been implemented to run on the JVM.
Here's a thought: look at how JRuby or Jython implement blocks in Java code, and abstract that code into a new library for Java. Or use the existing Jakarta Commons Functor project. There are enough options available that hacking the language spec beyond recognition and bastardizing a decent language in the name of Keeping Up with the Language Jones-es should not have to be considered the only viable option.
Java is a Turing complete language -- by definition, you can do anything in Java that you can in any other Turing complete language. Which means that adding closures to Java, while an interesting theoretical and mental challenge, is not necessary.
So why bother?
Or does it?
My original title for this entry was "Closures in Java: Lipstick on a Pig?", but I decided against that because I don't want to imply any disdain for Java. I've programmed professionally in everything from IBM mainframe assembler language to (shudder) COBOL to Java to Ruby. I spent most of last year developing websites in Ruby on Rails. I currently develop client and server-side solutions in Java.
Professionally, I can't afford to be a language - or even a language-feature - snob. There are things I used to be able to do in assembler language that I can't do in Java, or even (gasp) Ruby. Do you see me writing proposals to add BALR functionality to the Java language? Of course not. Java is not IBM mainframe assembler language. It was never intended to be.
Nor was it intended to be Ruby. Or Python. Or ML. Or LISP. Or whatever your particular favorite non-Java programming language happens to be.
I wish the Java Powers That Be would leave well enough alone, be happy with the success of Java and the JVM, and stop trying to be Everything To Everybody.
Enough with the inferiority complex, already.
So Java doesn't have first-class functions (which some would say aren't essential for closures, but if you're not going to have first-class functions, why bother?). Big deal. It has a large install-base. It has Tomcat. It has the JVM. It has static typing, which is great if you're into that kind of thing. In short, it has everything you need to develop useful, useable, functional programs.
Could Java be a better language? Probably. But could it be worse? Certainly (yes, I'm talking to you, C++).
So why not just let Java be Java? If you really need first-class functions/blocks/closures, use Smalltalk. Or Ruby. Or Python. Or Lisp. If you really need first-class functions/blocks/closures on the JVM, use Smalltalk/JVM. Or JRuby. Or Jython. Or Armed Bear Common Lisp. Or any of the (astonishingly large) number of non-Java languages that have been implemented to run on the JVM.
Here's a thought: look at how JRuby or Jython implement blocks in Java code, and abstract that code into a new library for Java. Or use the existing Jakarta Commons Functor project. There are enough options available that hacking the language spec beyond recognition and bastardizing a decent language in the name of Keeping Up with the Language Jones-es should not have to be considered the only viable option.
Java is a Turing complete language -- by definition, you can do anything in Java that you can in any other Turing complete language. Which means that adding closures to Java, while an interesting theoretical and mental challenge, is not necessary.
So why bother?
Tuesday, May 29, 2007
Hacking JRuby
I recently got to contribute to the JRuby project -- issue #JRUBY-914: "JRuby's BigDecimal needs to round". In doing so, I got to learn a bit about Java's BigDecimal -- particularly how it differs from Java 1.4 to Java 5, implement a bit of code in the JRuby codebase, and test my ideas conveniently and interactively using Jython. Not a bad way to spend a couple hours hacking and contributing to the community.
In a nutshell,
My solution was to use the
Note that this algorithm works for both negative and positive values of scale. It's a little bit of overkill when scale is non-negative, though, so I just implemented it for the interesting case of
In a nutshell,
java.math.BigDecimal
allows you to specify a scale, which is the number of digits to the right of the decimal place. So a BigDecimal with an unscaledValue of 1 and a scale of 1 would evaluate to 0.1. Java 5 allows the scale to take a negative value. So a BigDecimal with an unscaledValue of 1 and a scale of -1 would evaluate to 10. This is significant for rounding because it allows you to round a number to any digit, right or left of the decimal. But Java 1.4 does not allow a negative scale, so we need another approach to allow the same behavior on both versions.My solution was to use the
movePointLeft()
and movePointRight()
methods of the BigDecimal class to achieve the same result. It turns out those methods do permit a negative offset, so moving the decimal point "right" by a negative number actually moves it to the left, and vice versa. This turns out to be all we need to achieve the desired behavior: moving the decimal point to the right scale times positions the digit to be rounded to just to the left of the decimal point. We can then round normally, using whichever Java rounding mode suits our needs. Then we shift the decimal point left scale times to restore the (now rounded) number.Note that this algorithm works for both negative and positive values of scale. It's a little bit of overkill when scale is non-negative, though, so I just implemented it for the interesting case of
scale < 0
.
Subscribe to:
Posts (Atom)