[Python-ideas] Ruby-style Blocks in Python Idea [with examples]

tav tav at espians.com
Mon Mar 9 19:47:18 CET 2009


Dear all,

Here's another stab at making a case for it. I'll avoid referring to
Ruby this time round -- I was merely using it as an example of where
this approach has been successful. Believe me, there's no Ruby-envy
here ;p

The motivation:

1. Having to name a one-off function adds additional cognitive
overload to a developer. It doesn't make the code any cleaner and by
taking away the burden, we'd have happier developers and cleaner code.

2. This approach is more descriptive and in line with the code flow.
With blocks the first line says "I'm about to define a function for
use with X" instead of the existing way which says "I'm defining a
function. Now I'm using that function with X."

3. DSLs -- whether we like them or not, they are in mainstream use.
Python already has beautiful syntax. We should be leveraging that for
DSLs instead of forcing framework developers to create their own
ugly/buggy mini-DSLs. This will enable that.

The proposed syntax:

  using EXPR do PARAM_LIST:
    FUNCTION_BODY

Examples:

# Django/App Engine Query

Frameworks like Django or App Engine define DSLs to enable easy
querying of datastores by users. Wouldn't it better if this could be
done in pure Python syntax?

Compare the current Django:

  q = Entry.objects.filter(headline__startswith="What").filter(pub_date__lte=datetime.now())

with a hypothetical:

  using Entry.filter do (entry):
      if entry.headline.startswith('What') and entry.pub_date <= datetime.now():
          return entry

Wouldn't the latter be easier for a developer to read/maintain?

Let's compare this App Engine:

  composer = "Lennon, John"
  query = GqlQuery("SELECT * FROM Song WHERE composer = :1", composer)

with:

  composer = "Lennon, John"
  using Song.query do (item):
      if item.composer == composer:
          return item

Again, being able to do it in Python syntax will save developers the
hassles of having to learn non-Python DSLs.

# Event-driven Programming

Right now, event-driven programming like it's done in Twisted is
rather painful for many developers. It's filled with callbacks and the
order in which code is written is completely inverted as far as the
average developer is concerned.

Let's take Eventlet -- a nice coroutines-based networking library in
Python. Their example webcrawler.py currently does:

  urls = ["http://www.google.com/intl/en_ALL/images/logo.gif",
          "http://us.i1.yimg.com/us.yimg.com/i/ww/beta/y3.gif"]

  def fetch(url):
      print "%s fetching %s" % (time.asctime(), url)
      httpc.get(url)
      print "%s fetched %s" % (time.asctime(), url)

  pool = coros.CoroutinePool(max_size=4)
  waiters = []

  for url in urls:
      waiters.append(pool.execute(fetch, url))

Wouldn't it be nicer to do this instead:

  pool = coros.CoroutinePool(max_size=4)

  for url in urls:
      using pool.execute do:
          print "%s fetching %s" % (time.asctime(), url)
          httpc.get(url)
          print "%s fetched %s" % (time.asctime(), url)

I'd argue that it is -- but then I have bias =)

# SCons

SCons is a make-esque build tool. In the SConstruct (makefile) for
Google Chrome, we find:

  def WantSystemLib(env, lib):
      if lib not in env['all_system_libs']:
          env['all_system_libs'].append(lib)
      return (lib in env['req_system_libs'])
  root_env.AddMethod(WantSystemLib, "WantSystemLib")

Which we could hypothetically do as:

  with root_env.WantSystemLib do (env, lib):
      if lib not in env['all_system_libs']:
          env['all_system_libs'].append(lib)
      return (lib in env['req_system_libs'])

As someone who's used both make and SCons, I found SCons terribly
verbose and painful to use. By using the proposed do statement, SCons
could be made extremely pleasant!

# Webapp Configuration

Configuration in web applications is generally a real pain:

  application = webapp([('/profile', ProfileHandler), ('/', MainHandler)],
                                   debug=True)
  run(application)

Compare to:

  using webapp.runner do (config, routes):
      routes['/profiles'] = ProfileHandler
      routes['/'] = MainHandler
      config.debug = True

I think the latter is more readable and maintainable.

Please let me know if more examples would help...

I really do believe that a block syntax would make developers more
productive and lead to cleaner code.

-- 
love, tav

plex:espians/tav | tav at espians.com | +44 (0) 7809 569 369
http://tav.espians.com | http://twitter.com/tav | skype:tavespian



More information about the Python-ideas mailing list