Just got back from OSCON 2011. There were some really interesting talks, and “the big three” were all over the place: Google, Twitter, and Facebook. I attended the Twitter talk on how they moved from Ruby to the JVM, as well a talk by Rob Pike (of Google) on Go. I use Twitter and Google a lot, and enjoyed hearing about how they work (Facebook, on the other hand, I use begrudgingly).
My first reaction to these talks was “Wow, it sure sounds like a lot of fun to work at these places!” From inventing programming languages to using Scala to scale a massive architecture, it sounds like developer shangri-la. Until recently, I was slinging Spring MVC code in vanilla Java. Sucks for me, right?
This is lazy thinking, and I haven’t fallen victim to it in quite a while. Ask yourself: “Why would Google spend the time and money to get Rob Pike to work for them and invent an entirely new programming language?” Ask: “Why would Twitter spend the time and money to design and deploy an elegant, scalable architecture using the JVM and Scala?”
An awesome new programming language, or an impressive, scalable architecture is not an end to itself. These are tools that serve some other purpose.
In the case of Google, Twitter, and Facebook, that purpose is to serve ads.
Although Steve Yegge’s keynote features him “quitting” his project at Google (which he describes as “sharing cat pictures”) to work on some higher calling (still within Google), he’s just trading one ad-serving system for another.
Google+ isn’t about sharing cat pictures, it’s about serving ads. Twitter’s massive network of 140-character bits of information isn’t about connecting people across the globe or to view current trends in worldwide thinking, it’s about serving ads. Facebook isn’t about entertaining yourself with games or sharing interesting links, it’s about serving ads.
So, why are the best and brightest in the industry busting their asses to work at these places? If all you care about is watching performance metrics increase, or playing with something new, or just watching lots of tests pass, I guess it’s nice to get paid well. But, how can this be truly fulfilling? For me, it wouldn’t be. I need the tools I’m building, the products I work on, and the architecture I’m helping to create to all serve something I’m interested in.
I’m not saying that my code needs to save the world or support humanitarian causes, but it should at least be something that I like, or I would use, or that I care about.
And I can’t fucking abide advertisements. Honestly, I don’t know anyone who does. Except, apparently, a lot of really smart software engineers.
Co-worker sent out an email today, letting everyone know about a handy Mac app called cdto. You drag it to Finder and, if you
click it, it will open a Terminal window where the current working directory is the folder you’re viewing in Finder. Pretty handy.
Merlin Mann has said this more than a few times on his great podcast with Dan Benjamin, and it
really strikes a chord with me. Not that I play video games (I don’t), but the underyling message really speaks to me: “you don’t need to schedule time to do
things you really want to do.”
One of the assertions I added to the talk, based on my work on the book is the specific advice to not start off in bash…use Ruby. This was questioned by a member of the audience (who I realized later was Blake Mizerany of Heroku/Sinatra fame, i.e. someone far more knowledgable than me about CLI apps, Ruby, and shell programming).
My answer was about as eloquent as a middle-school debate team closing argument and amounted to “Me hate bash.”
All personal stuff only on iPad; no twitter, email, or RSS on work computer. Will it keep me more focused?
Essentially, what I decided to try doing was to bring my iPad to work every day, and use it for Twitter, RSS, and personal emails. The only “personal” stuff I would be doing on my work machine would be IM, simply because the IM experience on iPad is not very good, and the immediacy of it would be lost on a computer that’s not right in front of me. I don’t do a lot of personal IM’ing anyway, so this seemed like a good compromise.
Yesterday, while waiting for guests to arrive at my mostly-annual New Year’s Eve party, I found a txt file I created called 2010_goals.txt. It’s contents:
In Java, I almost never make type errors. The type errors that are possible in Java fall roughly into two categories:
Using an object of the wrong type (this is caught by the compiler)
Casting an object to a more specific type at runtime (this can only be caught by tests or users :)
I’d make error #1 on occasion, but the compiler catches it. As to #2, before generics, I can count on my hands the number of times I got a bona-fide ClassCastException at runtime. After generics? Never.
I don’t mean just that I didn’t experience these runtime type errors, but that they didn’t even make it to the compiler. If you think about how Java syntax works, it’s no wonder:
I had to type almost the exact same thing twice. After about 2 days of using generics, my muscle memory literally prevents me from making type errors. To even simulate one requires a pre-generics library call, or some herculean efforts. An arguable win for static typing, if at the price of verbosity.
Of course, in Ruby, I make type errors all the time, especially when using new libraries I’m not familiar with. Ruby libraries rarely document the types of things (though they are frequently liberal with what they will accept). The solution here is just better unit tests. And that’s a pretty good thing. So, a slight negative for dynamic typing that leads us to a better tested system, reduced verbosity, and better productivity once the learning curve is dealth with.
This pretty well illustrates the tradeoffs between dynamic and static typing. Case closed, right?
Enter Scala. With Scala, I make type errors just as much as I do with Ruby. The only difference is that the compiler catches them. Here’s the Scala equivalent to the Java code above
varlist=List("foo","bar")
Notce how I haven’t specified a single type? It’s nearly identical to the Ruby version:
list=["foo","bar"]
These examples are obviously simplistic, but in a more complex system, Scala’s type inferencer tends to be one
step ahead of me. While it’s handy that I have a compiler to catch these type errors, the fact remains that, despite Scala being a statically typed language, I’m making far more type errors than I would in Java.
This seems kindof odd, but I think it’s ultimately a win: I get the brevity and productivity of a dynamically typed language, but the safety of the compiler catching my type errors for me.
Scala puts a subtle spin on the “static vs. dynamic” debate, because you aren’t annotating your types nearly as much as with Java, but you still get all the benefits. I’ve certainly heard many criticisims of static typing, but having the compiler check types for you wasn’t one of them.
Of course, sometimes you do need to tell Scala what your types are, but, they seem to be exactly where you’d want them anyway:
/** get users with the given name and age */defgetUsers(name:String,age:Option[Int]):List[User]
This says that we take a requires String and an optional Int and will return a list of User objects. To give the same information in Ruby, you’d need to:
# Gets the users with the given name and age## name - a String# age - an Int or nil if not searching by age## Returns an array of User objectsdefget_users(name,age=nil)...end
(Sure, you could leave off the comment, but do you really hate your fellow developers (*and* future you) that much?)
Now which language is more verbose? Perhaps the static/dynamic typing debate is really just about entering text?
In a recent blog entry, Cédric Beust calls out scala’s Option class as nothing more significant than null checks. Commenters rightly set him straight that the thesis of his blog post was marred by an ignorance of idiomatic use of the class.
But, it’s hard to really blame Cédric, when you look at what he had to go on. Odersky’s book states that one should use pattern matching with Option, and the scaladoc for Option is just abysmal:
This class represents optional values. Instances of Option are either instances of case class Some or it is case object None.
That is the entire description of the class, no examples, nothing. Worse, the method that commenters called out as idiomatic, flatMap, has the following description:
If the option is nonempty, return a function applied to its value. Otherwise return None.
This is not what Option#flatMap actually appears to do (nor is documented to do; it’s documented to return an Option[B]!):
It certainly doesn’t call itself out as “the way” to use Option. A simple example in the scaladoc could have gone a long way.
So, angry post + helpful commenters == problem solved, right?
Wrong.
The Option class is, really, an implementation of the NullObject pattern, and a more elegant way to handle optional values. In scala, we might have this method signature:
/**
* Updates the full name
* @param lastName the last name
* @param firstName the first name
*/defupdateName(lastName:String,firstName:Option[String])
This means “update my name; lastName is required and firstName is optional”. In java, this method might look like this:
/**
* Updates the full name
* @param lastName the last name, may not be null
* @param firstName the first name, may be null
*/publicvoidupdateName(StringlastName,StringfirstName){if(lastName==null){thrownewIllegalArgumentException("lastName required");}StringBufferb=newStringBuffer(lastName);if(firstName!=null){b.append(", ");b.append(firstName);}this.fullName=b.toString();}
So, what’s the right way to do it in Scala? According to the commentors:
Yech. Does anyone else think that calling a method called “foreach” on our “optional value” is just nonsensical? Or that the idiomatic way to treat an optional value is as a collection, e.g. by using the for comprehension? This just feels hacky. Naming is one of the most important (and challenging) things in software engineering, and Option’s API is an utter failure (even its name is wrong; when one has an option, one typicaly has many choices, not just one or nothing. Optional is really what is meant here, so why are we afraid of adding a few more letters? Especially given how “precise” some of the documentation is, mathematically speaking, why are we not being precise with English?). If Option is just shorthand for a “list of zero or one elements”, and we get no better methods than what comes with List, then what’s even the point of the class?
I’m not saying we remove all the collection methods from Option, but how about a throwing us a bone to make our code readable and learnable without running to the scaladoc (or REPL) to see what’s going on? I mean, there’s a method on Option called withFilter whose documented purpose (I’m not making this up) is: “Necessary to keep Option from being implicitly converted to Iterable in for comprehensions”. Am I expected to believe that it’s ok to have this hacky pile of cruft, but we can’t get a readable method for “do something to the contents if they are there”?
Which would be less surprising? Couple this with some better scaladoc:
/** This class represents an optional value.
*
* To use as a null object:
* val optional = getSomePossiblyOptionalValue
* <pre>
* option.ifValue { (actualValue) =>
* // do things with the value, if it was there
* }
* </pre>
* or
* <pre>
* optional.unlessValue { log.debug("missing optional value") }
* </pre>
*
* To use as a Monad or collection:
* <pre>
* val first8upper = option.flatMap( (y) => Some(y.toUpperCase) ).
* flatMap( (y) => Some(y.substring(0,8)) )
* </pre>
*/classOption[T]{// etc.}
With these examples, you cover the two main uses of this class, show newcomers how to use it, and demonstrate its superiority over null checks.
It’s frustrating to see this, because Scala has so much potential to be a lucid, accessible, readable language, but API usability and learnability are just not prioties. Scala needs to take some lessons from Ruby in terms of API design (and Java in terms of API documentation).
Of course, none of this does save you from null, because Scala is perfectly happy to assign null to anything. It kinda makes the whole thing seem a bit pointless.
I’ve been working on the app more than my recent lack of blog entries indicates. At this point, I have what could roughly be called a beta version; almost all the features are there, and things seem to be generally working pretty well.
User Experience
The biggest change in the UX is the ability to add tasting notes, date tasted, and location tasted. This is a new screen accessible from the main entry screen. The most obvious way to do this in my mind, was a big button at the bottom:
In designing the new screen,
the “where tasted” and “when tasted” were straightforward; I used stock controls. For the tasting notes, I needed a UITextView, which is akin to an HTML TEXTAREA. The visual appearance of this control is pretty lacking compared to the UITextField; there is no nice beveled edge, no rounded corners, and no placeholder text. I really just wanted a multi-line field much like the UITextField, but there is nothing available to create that.
So, I hacked something together.
An option for the UITextField’s appearance is to have a beveled edge with square corners. In this configuration, you can adjust the height of the text field. So, I placed such a field on the screen and sized it about the size of my tasting notes field and made the background color white. I then put the tasting notes field on top of it, with a clear background color, and, well, it looked pretty good:
I then implemented some UITextViewDelegate methods to give the apperance of placeholder text:
I considered using some third-party controls that mimic this behavior, but didn’t want to get side-tracked adding new frameworks to my app at this point.
User Testing
Once I had this, I handed my phone to Amy for some more user testing; She brought up a few obvious things that I had completely internalized and begun ignoring:
Clicking “Save” on the details screen brought you back to the “new wine” screen, instead of just saving and bringing you back to the top. A minor but obvious annoyance.
She kept tapping on the “Choose Varietal” text field, thinking that would bring up the varietal chooser, instead of clicking the much smaller blue “disclosure” button
She was a bit sad that the wines we had entered in the app would not be available on our shared Google Spreadsheet
To smooth the navigation after saving, I used a stock feature of the UINavigationController to “pop” more than once up the chain. Since my design of the details screen used Apple’s delegate pattern (essentially, the “add new wine” view controller was the delegate to the details view’s lifecycle; when you click “Save” on the detail view, it triggers a callback in the “add new wine” view controller; the perfect place to save before the detail view controller popped back two screens).
The problem with the “Choose Varietal” control had bugged me, too, but I got used to it and didn’t think about it. The solution was very simple, though hacky. I placed a clear button on top of the field the exact size of the field and had it trigger the same action as the blue disclosure button. Problem solved.
As to maintaining the list up on Google Docs, I think I may need to implement this sooner rather than later; I think it’s important to be able to get your data out of an application, and Google Docs is a reasonably user-friendly way to do it (as opposed to emailing some CSV file).
Other Random Bits
I still didn’t get around to setting up iCuke for testing; I really should because I don’t know what missing retain calls might be lurking. I also finally created an icon, using a picture I took in Napa. Not sure I like it, but it beats the white blob:
Finally, the app no longer starts up on the actual device. A seemingly serious problem that I assume would be remedied by a re-install from scratch, however I have a few wines that I’ve added and don’t particularly wanted to lose them. Not sure how I could gain access to the SQL database to get them out, but I’m currently downloading the 4.0.1 update for my phone and the 2+ GB SDK update (!).
As a followup, I had to re-install the application from scratch, though I was able to access the SQLite database from an iTunes backup. I *really* need to implement a quicker backup/export mechanism…
The past week was spent trying to understand the best way to expand my application’s features without have a ton of duplicated code or UI.
It was also a learning experience on Core Data. Thankfully, Stackoverflow and its amazing contributers were very helpful.
Core Data Blunders
It started as I made my first foray into implementing the search screen (i.e. the home screen of my app):
Three of the searches (most recent wines, wines by rating, and all wines) end up doing the same thing more or less: Query for some data, sort it a certain way, and show it in a UITableView. I ended up creating a custom table cell view and I wanted all three to use it:
Access to Core Data-provided entities is done through objects of the class NSFetchedResultsController, which takes what amounts to a database query and provides many ways of accessing the results, including caching, callbacks, and iteration. When using a table view, you typically have your view controller handle callbacks for this class, which allows the table to update itself when items are added, removes, or changed. All of this boilerplate is given to you at the start of the project. So far so good.
My plan was to create additional NSFetchedResultsController instances inside my table view controller class and then switch between them. A fine idea that lead that numerous random difficult-to-reproduce crashes.
It turns out the basic idea of what I was doing was good, but my implementation exposed a sore lack of understanding of how all this stuff fit together. I’m still not sure I fully grasp it (and wish the book I bought had a bit of a deeper dive into Core Data), but after some more reasoning, I got around it. Essentially, leaving the unused NSFetchedResultsController instances connected to my table view and having caching turned on and not properly reloading data when switching searches created a situation where they were all pretty confused about the underlying state of the database.
With some judicious management of these instances, as well as disabling caching, I now have a fully-functional “Recent Wines”, “Wines By Rating” and fancy indexed “All Wines” view (index meaning I can jump to wines by letter, a la the Contacts application).
Unfortunately, the result of turning off caching is that it takes a noticable blip of time to summon any of these views. I may just come full circle and end up with three different UITableViewController instances and NIBs just so I can leave Caching on.
User Experience
Once I had gotten back to a stable app, I loaded it up on my phone and headed into the field. A few nights ago, Amy and I ended up at one of our favorite restaurants/wine bars in DC, Proof. We’ve been there several times, and many of their wines-by-the-glass are in the Wine Brain. While they rotate their selections, I was curious as to what I’d had there previously. Unfortunately, I had yet to implement the search-by-location feature :) Combing through the “All Wines” view was a bit frustrating. This made obvious several features that are now on the top of my list (some planned previously, some not):
Find wines by location
Actually entering the location at which I had a wine
Location-aware assistance of location (e.g. “Are you at Proof? Here’s what you’ve had there…”)
Ability to text-search the “all wines” list
The good news is that my cursory reading of my book and the API docs indicates that these things are going to be very easy to implement.
Objective-C
There’s a lot to like about Objective-C, and a lot to dislike. Even though the handling of multiple arguments is a bit strange, I find it actually results in fairly readable code. It feels very Apple; different, but usable. Even though it’s a lot to type out something like:
It is pretty readable, regardless of what programming language you are coming from. That being said, stack traces and error messages on crashes are nigh-useless, and the overall testability of iPhone apps is pretty behind the times. This months issue of Prag Prog’s magazine has an interesting article on iCuke, which I look forward to trying out. I’ve resulted to keeping a text file of test cases that I have to manually run through to make sure I haven’t broken anything, and it feels, well, 1997. The rumours of a Apple’s switch to Ruby are too good to be true, but one can always dream.