Thursday, January 10, 2013
Why I like the ternary operator
For the uninitiated, the ternary operator is basically syntax sugar in the C family of languages (and Java, since Java is Just A Fancy C++ VM) that allows you to inline if statements.
Basically:
var foo;
if(getGodlyGlobal(me.margaret)) {
foo = "baz"
} else {
foo = "wtf"
}
Becomes:
var foo = getGodlyGlobal(me.margaret) ? "baz" : "wtf";
To me, this feels concise and elegant. But to Mike, it seems to go dangerously in the direction of Perl line noise. I'm sympathetic.
Robert Martin bring up the excellent point, in his Tour de Force Clean Code, that we should Avoid Mental Mapping. He uses this concept specifically with regard to variable/function nomenclature, but it has larger implications in software engineering as well. This concept jibes well with research literature of Cognitive Load Theory in psychology.
Essentially, the more "noise" we create for our brains by having to map compressed pieces of information to larger meaning, the more difficult it is to understand what's going on. This may be why, to many, elegant models for statistics/quantum dynamics just look like "alphabet soup." Too much information compressed into a tiny space requires a lot of outside context in order to make heads or tails of.
Here, I have to know the syntax of the ternary operator, specifically that I replace the if with "?" and the else with ":". This mapping doesn't feel too complex, but any time I have to stop and stare quizzically at a piece of code, I have an opportunity to misunderstand or wasting time I could be using to develop new features.
Some languages, such as Coffeescript, elegantly handle this issue by allowing inline if/else in the evaluation of expressions. That seems reasonable. It obviously biases a programming language (with roots based in math) towards English, but then again, it's not a lot of English. If we embrace Donald Knuth's Literate Programming and try to make our code as expressive as possible, the presence of words is helpful.
Typically I use the ternary operator in a situation like the preceding, where I would just set a variable. But I just experienced another interesting use case, which is probably in line with the thoughts of the Anti-If campaign. Consider the following.
https://gist.github.com/4507897#file-buildcontext-java
This method doesn't look too complicated. It's only 13 lines, though we can clearly notice that despite being named "buildContext" its only doing anything related to a context in the last line (another method call). The rest is actually reading values from a RequestContext. A problem with naming? Sure. But also take a look at lines 40-47.
My first instinct, on looking at this, was to refactor that into a function. It's a fairly self-contained closure that's essentially assigning a variable. Looking at it further, we can see both blocks are setting the same attribute on the request, with a different value in each branch.
Aha. A violation of DRY. There's one argument.
More interestingly, there's the cognitive notion of branching. Because I have a branch in logic here, I have to look both places to determine what will happen. Ultimately, the same thing will happen, just slightly different. But it's easy to imagine this changing, isn't it?
What if a 2 am programming emergency where to come through on a recognition problem? Maybe, if something is recognized, it should be logged. 3 Months down the line, someone decides unrecognized pieces should be logged as well, but recognized pieces should calculate value. In 6 months, the whole RequestContext is replaced with some other logic.
In that kind of code churn, it's very possible to misplace a line or two. Especially something like setting the "pageIdent" attribute in one branch, but not in the other. Perhaps the other branch was refactored into a method, then the method was tweaked, and somewhere along the way the line got lost.
Branches in logic, by definition, attract Change. They invite bifurcation and inevitably confusion. One could argue that this problem could easily be removed by moving the request.setAttribute() outside of the branches...but that's the point. Exactly the point!
In programming, since Programming is Life, it's easy for things to fall into the wrong scope. Some naive developer placed duplication in those scopes without understanding that an implicit invariant was in place: no matter which branch of the if is taken, the "pageIdent" attribute should be set on the request. It can be easy to lose the forest for the trees, especially if you're not following Uncle Bob's First Rule of Functions and have hundreds of lines and multiple nested blocks.
Blocks are cognitive magnets for confusion.
Now consider this snippet:
https://gist.github.com/4507897#file-improvedbuildcontext-java
Straightforward. One can argue I cheated a little bit by refactoring the RequestContext parsing and Validation into super class's method, but even if we add those lines in, we see we've eliminated a branch in logic. Now it's perfectly clear: we're setting parameters on the request, and one particular parameter has two possible values. If we want to change what happens in each branch with this, the optimally logical thing to do is to extract that line out into a method and expand it out into its full if form. But in so doing, we've created an isolation point where such change can be processed easily without influencing surrounding logic. By eliminating a branch, we've manifested our invariant more clearly.
Monday, December 31, 2012
Questions from the Mailbag: On Static Imports
"Something I noticed today while I was looking over the Mockito API...they suggest importing the library statically. So I did, and I noticed JUnit was imported statically as well in Eclipse, but other classes/libraries in my Unit Test class were not. What I'm asking then is why do the static import, why does it seem to be important in testing, and when might one want to do it otherwise, if at all?"
Excellent question!
You know I like my code like I like my porn:
Explicit is Better Than Implicit.
http://www.python.org/dev/peps/pep-0020/
Wednesday, November 28, 2012
I was asked to write a cache for a HTTP request. My first instinct was
to add a static instance variable and be done with it. Then I thought
about staleness. I could've added another field for a Date, and turned
my class into an unmaintainable mess in trying to manage the
freshness...instead I factored out.
I noticed, as I was
writing the new class, that I wasn't in a dry, soulless programming
mood. I could've described everything in terms of caches, maps,
requests, buffers, etc...instead, I decided to make it like telling a
story: the PreviousJourneys of Corey the HttpCourier.
I noticed that by using expressive names like rememberVisit() instead
of putDataInCache(), and expanding out expressions like
hasVisited(location) && tripTakenRecently() into a
method--visitedRecently(locati on)-- I'm able to write a fairly comprehensible story.
It could use some work, of course. It's not perfectly consistent in
names. The logic is 3 am logic. Technically it's probably better to make
a Journey that has place, experience, and date, as it makes more sense
to say "If I haven't visited X in 7 days I probably wanna see what's new
there" rather than "I haven't been anywhere in 7 days so I'm going to
X, damn it!"
But the point of the exercise is that I think most people could follow it.
I think this was the point of the Abbot Method, CRC cards, Use Cases,
and all of OOP. It's what BDD is driving at. If you're writing code
powerfully, it should tell the story of the system in a way that's
obvious to most people who have some idea what's going on. Knuth's
Literate Programming is probably quite far along the spectrum to
Codevana...maybe even farther than LISP. *gasp*
I noticed that by using expressive names like rememberVisit() instead of putDataInCache(), and expanding out expressions like hasVisited(location) && tripTakenRecently() into a method--visitedRecently(locati
It could use some work, of course. It's not perfectly consistent in names. The logic is 3 am logic. Technically it's probably better to make a Journey that has place, experience, and date, as it makes more sense to say "If I haven't visited X in 7 days I probably wanna see what's new there" rather than "I haven't been anywhere in 7 days so I'm going to X, damn it!"
But the point of the exercise is that I think most people could follow it.
I think this was the point of the Abbot Method, CRC cards, Use Cases, and all of OOP. It's what BDD is driving at. If you're writing code powerfully, it should tell the story of the system in a way that's obvious to most people who have some idea what's going on. Knuth's Literate Programming is probably quite far along the spectrum to Codevana...maybe even farther than LISP. *gasp*
Wednesday, September 5, 2012
DON'T WRITE CODE LIKE THIS OR A POOH BEAR WILL PUNCH YOU.
Think about Winnie the Pooh.
Winnie is the sweetest, kindest, lovable ol' Pooh Bear you ever did see.
Winnie the Pooh makes me happy.
You know what doesn't make me happy?
Code that looks like this.
That code looks pretty complex, to new eyes. Especially if you're looking at it after trying to figure out why someone else's library, which you've cloned, doesn't pass all of its regression tests. You might look at that and have a few moments of doubt. Concern. Do I understand how this library is used?
Then you stare at it for a minute. You realize that the intent of this Miracle of Indirectness is to find if a key is contained in a collection of registered services. That's not hard.
One can easily think of doing that in a way that makes sense. You basically have a Map between Service and Configuration. If your test is supposed to modify,or not modify, one of these values, you can easily ask.
Instead, this test prefers to go through the list once, in place, instead of building the map. Ah. Efficiency. How delightful. Except (pun-ish-ment) that the loop uses JUnit's assertEquals() in the body of the
try
block, which throws a ComparisonFailure
when the condition is not true.This means that we're using Exceptions for flow control. DON'T USE EXCEPTIONS FOR FLOW CONTROL!!!
They expect exceptions to be set up often but thrown rarely, and they usually let the throw code be quite inefficient. Throwing exceptions is one of the most expensive operations in Java, surpassing even new. On the other hand, don't forget the first rule of optimization (RulesOfOptimization).So, in trying to be efficient, we've completely destroyed the point of efficiency? Well, at least the bottom of the method is self-documenting.
I know, I know. I shouldn't care so much about what I do. "It's all bullshit anyway", right? If it works, yay! If not, "make it work, whatever it takes." It's not in fashion to try to be, ya know...Elegant. Precise. Crisp.
Whatever. It's not my library. I don't have to care. I just have to try to make a contribution that will fix my problem, and if it inadvertently breaks half the world, e.g. hundreds of other packages that depend on this one....FUCK EM! Right?
Write tests for your new code. Don't worry about the horrible legacy you're trodding upon...But then I have to look at code like this, and just ignore it.
WTF bro? That's not even a test. If you're going to test for the persistence of a dynamically generated file in a transient environment, that's hard. But probably doable. It involves actually writing a condition to test.
But writing tests like this is worse than just leaving fail(). This test, naively, just by looking at a report, looks like it's doing something intelligent. Then you look at it. You know what this causes?
He's coming for your face, honey. He's.coming.for.your.face.
Sunday, August 12, 2012
The Parable of Little Jimmy
Meet Jimmy
Jimmy Goes to War
Save Jimmy!
No Problem…right?
Not So Fast…
The End of Jimmy
Who’s to Blame?
No
How It Might Have Been
But It’s Gotta Get Done!
Bottom Line
Software Architecture and the Parable of the Megapode
This is a story best told in Douglas Adams' delightful voice.
But, if you don't have the time or motivation to listen to a few minutes of adventure, here's the transcription.
"I have a well-deserved reputation for being something of a gadget freak. And I'm rarely happier than when spending an entire day programming my computer to perform automatically a task that it would otherwise take me ten seconds to do by hand. Time is valuable, and ten seconds worth of it is well worth the investment of a day's happy activity working out a way of saving it.
The bird we came across was called a megapode, and it has a very similar outlook on life. It looks a little like a lean, spritely chicken. Though it has an advantage over chickens that it can fly, if a little heavily, and is therefore better able to escape from dragons, which can only fly in fairy stories--and in some of the nightmares through which I was plagued while trying to sleep on Kimodo.
The important thing is that the megapode has worked out a wonderful labour saving device for itself. The labour it wishes to save is the time-consuming activity of sitting on its nest all day, incubating its eggs, when it could be out and about doing things. I have to say at this point that we didn't actually come across the bird itself, though we thought we glimpsed one scuttling through the undergrowth. We did, however, come across its labour-saving device, which is something that is hard to miss.
It was a conical mound of thickly packed earth and rotting vegetation. About 6 feet high, and 6 feet wide at its base. In fact, it was considerably higher than appeared, because the mound would've been built on a hollow in the ground, which would itself have been about 3 feet deep.
I just spent a cheerful hour of my time writing a program on my computer that would tell me instantly what the volume of the mound was. It's a very neat and sexy program, with lots of popup menus and things, and the advantage of doing it the way I have is that in on any future occasion on which I need to know the volume of the megapode nest, given its basic dimensions, my computer will tell me the answer in less than a second, which is a wonderful saving of time! The downside, I suppose, is that I cannot conceive of any future occasion that I am likely to need to know the volume of a megapode nest...but, no matter! The volume of this mound is a little over 9 cubic yards.
What the mound is, is an automatic incubator. The heat generated by the chemical reactions of the rotting vegetation keeps the eggs that are buried deep inside it warm. And not merely warm! By judicious additions or subtractions of material to the mound, the megapode is able to keep it at the precise temperature which the eggs require in order to incubate properly.
So, all the megapode has to do to incubate its eggs is merely to dig 3 cubic yards of earth off the ground, fill it with 3 cubic yards of rotting vegetation, collect a further 6 cubic yards of vegetation, build it into a mound, and then, continually monitor the heat it's producing and run about adding bits or taking bits away. And thus, it saves itself all the bother, of sitting on its eggs from time to time.
This cheered me up immensely."
The next time you're tasked with a big, hairy ball of mud on your task list, ask yourself..."Am I being a Megapode?"
Friday, July 13, 2012
Why is TDD awesome?
Because it gets you solving problems.
I've been reading a lot of papers and shifted my attentions over to self-adaptive systems since the start of the year. I've become familiar with different researcher's thoughts on the subjects, and the methodologies and tools they're trying to develop. They seem too disconnected from practice and abstract to be practical. So I'm going to build my own.
That's been the plan. I've been doing a lot of writing, for my dissertation, on how I'm going to build it. How it's going to be different. Why it's useful and will be a game changer, etc etc.
Then I did a random Google Search today:
http://avalanche123.com/blog/2012/06/13/how-i-built-a-self-adaptive-system/
Son of a bitch. I've been looking at this problem since January. I want to make it the brunt of my research focus. Earlier this year I collaborated with Fractal Innovations LLC to design a prototype...but I hadn't gotten a concrete deliverable out of it. Now Some Dude seems to be moving in the same direction.
OH NOES.
The dreaded graduate student nightmare: Invest a whole bunch of time and energy into a problem. Meet with your committee.
"Are you sure this is a problem? It looks like this problem is already solved..."
Not gonna let that happen.
So I read his post, ended up looking at Fuzzy Sets...and it looked kind of intimidating.
A Fuzzy Logic based Expert System?
The seems fine.
Building a framework to build Fuzzy Logic Based Expert Systems, rapidly, conveniently, and following best practice software engineering principles?
Seems hard, bro.
...What can I do?
But I decided to play with the problem. See what I could come up with. I tried to build a system that could process the first, seemingly simple statement:IF brake temperature IS warm AND speed IS not very fast
THEN brake pressure IS slightly decreased.
Being a framework designer at heart, my mind immediately tried to be a True Computer Scientist.
The True Computer Scientist
You see, the True Computer Scientist's brain is much, much bigger than yours or mine.The True Computer Scientist is able to see the generalizations and abstractions beyond a given problem.
The True Computer Scientist doesn't worry about piddling, puny, simple logic flows about brake pressure and brake temperatures.
The True Computer Scientist analyzes the structure of the statements, derives a general form that applies in almost any context, and builds The System that manifests the heart and soul of complex decision making in an uncertain world.
The True Computer Scientist solves problems at a level so meta that you're thinking of thinking to understand but you're still not quite there yet.
The True Computer Scientist uses recursion and abstract data types with strongly checked invariants to determine optimal solutions, seamlessly wielding matrix mathematics and monads while you're still trying to figure out what the fuck that means.
The True Computer Scientist is a God, Puny Mortal.
...and I am just a dumb chimp.
Do a thousand monkeys put on computers create Google?
But every time my mind comes upon a problem like this, it immediately tries to be the True Computer Scientist. My mind raced to develop a class for Rules, Antecedents,and Consequents. I thought about building an expression engine for evaluating nested rules...and the more I tried to solve these difficult problems analytically, in my head ahead of time,the more scared I became.Woah. This is A Big Problem. I don't think I can do this.
"Slow down there, stupid monkey. Solve this problem. The framework can come later."
Just like that, suddenly I moved from Saving the World and Getting the Girl to Make It to the Next Save Point. Suddenly that fear that I'd felt was subdued for a moment. I started solving the problem.
An hour later, I had a working prototype that solved that problem. Plus, along the way, I'd derived the abstractions I need to build the framework.
How?
Simple. I've studied a lot of software design and architecture. Now, I'm able to think in terms of patterns and principles.Which means that, as I'm making my bar turn green, I also say
"Ah, this is something that's likely to change. Instead of adding another property to this class, I'll add a Value Object that this Entity uses."
I write the domain logic in. When I see patterns in what I'm doing, I immediately refactor to a Strategy or extract interfaces. I end up representing First Class Entities like Sensors, just in trying to keep things DRY. I flow from
getting a temperature reading from a Speedometer --> having an Analyst analyze a Sensor using a Strategy--> to having an Agent act on a Perception using a Strategy-->to having an Agent perform an Action using an Actuator with input from a Perceptor.
Pretty Cool
TDD enables framework design. Rather than trying to come up with the optimal design up front, design emerges from the components involved. Even better, it lets you start solving small problems quickly, adding value as you scale to the intended result.I know that I've become a much better software developer as a result of focusing my technique and being willing to try a different workflow. I regularly meet programmers who are dubious of changing their workflow, and many times default to denouncing innovations in our space as "another Silver Bullet." I highly encourage them to give this material a try.
It also gets better the more you involve and integrate. Reading Evans' Domain Driven Design or Fowler's Patterns of Enterprise Application Architecture are fine, in and of themselves...but when you start building software integrating those concepts, and adding elements such as Kent Beck's Test Driven Development: By Example and Robert Martin's Clean Code, your very coding style and philosophy is tremendously improved. The whole is greater than the sum of the parts.
I'll be illuminating just how much perspectives can change in upcoming posts:
The Android SDK: Yet Another Successful Big Ball of Mud
HTML5 is an Abject and Miserable Failure of Responsibility and Design.
Stay tuned.