While all these upcoming new and exciting features are really nice, I would like to take a step back today and focus a bit more on one of the basic issues that a developer may run into when switching languages and frameworks: the Blub paradox. Blub, a fictive language discussed in a classic Paul Graham essay, represents a language XYZ that a programmer is used to work in. For me, this has been Java in the past, but it could be pretty much anything else. The Blub paradox states that programmers are "satisfied with whatever language they happen to use, because it dictates the way they think about programs." As a corollary, when having to work in a different language, programmers tend to mentally code in what worked best in "Blub" instead of what works best in the new language. It's like trying to speak Spanish by translating English sentences word by word: something will come out of it, but it is not really ideal.
I recently ran into the paradox myself when I gave bad advice to a fellow developer who pinged me with a question:
Which would you say would be preferred way of implementing the following.
I want to instantiate a subclass of db.Model from a dictionary of values, and I also want to make sure that an instance has valid values for a subset of required fields before saving the object. Should I:
1) Have an __init__ method on the subclass that accepts a dictionary (assuming that is allowed) and have the property fields declared as required=true and set the values from the dictionary in the constructor, or
2) As is usually true for Java for persistent classes, allow for zero-argument constructors, and implement a static create() method on the class to create the object from the dictionary and a static add() method on the db.model subclass that checks for required fields before saving the instance via a put(), or
3) Something completely different, perhaps involving the validation capabilities of properties.
The question (and especially approach number 2) pretty much demonstrates the early stages of switching frameworks: I have problem XYZ and a couple of approaches how to solve this in a language A, but I need to use language B for this project. How do I translate the code?
I have to admit that my initial response to the question was not particularly bright: rather than questioning the rational behind it, I simply responded with a slight tweak to the code translation:
I would probably go with an __init__ method with an optional parameter, something like
def __init__(self, from_dict={})
for key, value in from_dict.items():
setattr(self, key, value)
While this looked like it should work, the devil turned out to be in the details. A few hours later, I got another mail:
This turned out to be much trickier than I thought, especially for a beginning Python programmer.
What ended up working was:
def __init__(self, plistDict=None, **kwargs):
if plistDict != None:
for key, value in plistDict.items():
kwargs[key] = value
db.Model.__init__(self, **kwargs)
What made this so tricky was that the implementation of db.Model.__init__ REQUIRES that the values of any properties be in kwargs.
Just thought I'd pass that on.
At this point in time, it hit me like a ton of bricks: I had fallen into the trap! The App Engine team contains some of the brightest minds in the python world -- they would never build a framework that makes such a simple task so hard!
After a little extra research I realized that in order to make his use case work, my fellow programmer actually needed exactly zero lines of customization: support for dictionaries was already baked into the standard Model constructor:
someInstance = SomeDbModelSubclass(**myDictionary)
The double asterisks, as described in the Python documentation allows me to inject arbitrary key/value pairs as arguments into the constructor. To be precise, here is the quote from the documentation:
A function call always assigns values to all parameters mentioned in the parameter list, either from position arguments, from keyword arguments, or from default values. If the form "*identifier" is present, it is initialized to a tuple receiving any excess positional parameters, defaulting to the empty tuple. If the form "**identifier" is present, it is initialized to a new dictionary receiving any excess keyword arguments, defaulting to a new empty dictionary.
If we construct a Model instance in App Engine like
MyModel(prop_1=13, prop_2='hello world')
then the Model constructor actually uses this very same mechanism to populate properties. It is a standard feature of the python language, beautifully utilized in App Engine's API -- and completely novel to anyone who has only done Java code before! I just got hit by the blub paradox!
Having run into this made me wonder: how much unnecessary code do I have in my other python-based programs? Have I reinvented the wheel? I have decided that it does not matter too much, as long as my code works and I learn from my mistakes. It does show me however that I should keep learning new languages or frameworks on a regular basis. By getting introduced to new concepts I can broaden my perspective and produce cleaner and better code. By just doing "what I've always done", I will stagnate and become a less effective programmer.
So, what should the next thing be? I currently tend towards GWT, because I think it is really cool and fits well into the program-web-apps-with-App-Engine theme. I'm open towards recommendations though. Pleast post comments.
10 comments:
Another excellent post, Jens. As the "fellow developer" you mention, it was precisely the "Blub paradox" that you mention that caused me to reach out to you for help.
The combination of features of the GAE framework (support for required properties that are required to be present at __init__ time, not just put()) made me think that option (2) in my original email was incorrect, but I was too new to Python and the GAE to figure out the proper way to pass along the dictionary of values to __init__.
In our (tiny) defense, I would add that it didn't help that none of the GAE documentation or examples I have come across use this kind of initialization to create db.Model entities.
One thing that the GAE team might consider would be to put together a tutorial on GAE database interaction that is geared toward developers who are already familiar with common Java approaches such as the Spring/Hibernate/DAO approach.
Thanks again for your help!
Might be obvious, but here is a simple way to get a dictionary of key/value from a GAE entity 'obj'.
obj_dict = dict((k,getattr(obj, k)) for k in obj.properties().keys())
Thought this might be handy since its basically the opposite operation, class->dict
For your intermediate solution, you could have just used "kwargs.update(plistDict)" instead of a loop.
Jens, if I had the time to do something new purely to broaden the mind, I'd look at Haskell or Erlang or even Javascript [the advanced way some programmers use it]. GWT seems useful to you, since you already know Java and it's a Googley thing. I'm using jquery and Extjs instead, since I don't see what's so bad with javascript.
Hi Bill,
reg.: Javascript "advanced ways": are there any blogs/tutorials you can point to on what exactly you mean? Or do you refer to using a framework like JQuery?
reg. Haskell/Erlang: I have to admit I am a little bit prejudiced here. I think the concept is really cool, but I I would like to choose something that also provides me with interesting new ideas for this blog. Javascript (or "more" javascript compared to what I currently know) and Gwt have both the advantage of "synergies" with App Engine. So far, this blog has been pretty backend-centric, and it might be fun to mix that up ;-)
Hmm... speaking of... what do you think of focusing HTML 5?
Cheers,
Jens
I'm trying to get more proficient in both python and javascript. On javascript, I'd look at books like Crockford's "Javascript: The Good Parts." He describes how to use closures to create modules, and pretty good javascript hackers use that pattern to create some fairly complete javascript GUIs. The best javascript GUI I've seen is Extjs, and they also have another framework that builds on GWT called Ext GWT.
If you weren't a google guy, I'd also suggest looking at Adobe AIR because you can build cross-platform client-side apps using Javascript, Flash, or Flex. I think Google Gears (maybe that's what you should explore) competes with AIR in some respects.
> I think Google Gears [...] competes with AIR in some respects.
Well... they probably have some things in common while others are different. That's why I mentioned Html 5 previously, which hopefully standardizes some of the aspects like offline storage. Frankly, I don't care much whether the technology is from Google or not -- as long as I can see the potential for doing fun stuff in it and it teaches me something new. If I have a choice however, I prefer open standards or at least open source.
The reason I am not a big fan of becoming a "Javascript guy" is that debugging Javascript is quite painful at times. Firebug is a nice tool, but it still has its limitations (and it doesn't exist for all browsers). Browser quirks aren't cool either -- and yes, some frameworks abstract a lot of that pain away, but the issue still exists.
Then again -- maybe it's just Blub all over again: I don't know Javascript that well, so it could be that I am more suspicious than I should. I guess I'll think about it some more :-)
Excellent blog, just stumbled on it. If you're looking at learning javascript as your next project, I'd suggest looking at YUI or Ext. Before I started working with appengine (and learning python for the first time myself), I spent a lot of time learning the various javascript libraries. I played with GWT, dojo, jquery, YUI, and yui-ext to ext as it made it's transition.
The conclusion I came to is for websites, YUI is the way to go. For web applications Ext was a great platform.
People say that scala is worth learning (at least, it takes a significantly different approach). Anyways, just wanted to say that I can relate to your experience as the first 2 days I've started learning python (coming from Java as well) were a total WTF?!
You should also be very careful with mutable default arguments to functions in Python. If the argument is changed in the body, it will be changed for all subsequent calls as well, which is probably not what you want.
So instead of
def foo(some_arg={}):
# do stuff
it's generally preferable to do
def foo(some_arg=None):
if some_arg is None:
some_arg = {}
# do stuff
Post a Comment