Monday, May 7, 2012

function(groovy) #1


function(groovy) #1

If all you have is a hammer, everything looks like a nail.

Abstract: If my Groovy builder code is described as declarative then why is the remainder of my code not declarative? If declarative programming means I can put more emphasis on what I wish to achieve and less emphasis on how it is achieved then I should follow this principle throughout all my code. But I don't. Why not?

Ken Barclay
kenbarc@googlemail.com

Introduction

In this series of blogs I describe my experience of fusing a functional programming style with the object oriented features of Groovy. Incorporating functional programming constructs into Groovy arose from a disappointing experience I had when developing a range of applications originally intended to showcase some of Groovy's great features.

The applications had two major features in common. They all processed information from web services and they all presented that data in a GUI. For example applications might obtain meteorological data from Google's weather service, financial data from Yahoo, photographs from Flickr, videos from Youtube, or music data from iTunes. GUIs allowed the user to interact with that data.

Groovy's builder technology was an obvious choice for developing the UIs. Initially SwingBuilder was adopted, but later a custom FXBuilder was developed so I could leverage the richer components of JavaFX. Either way, the satisfaction of adopting a Groovy builder is the declarative nature of the resulting code. Declarative code places more emphasis on the what rather than the how. Example 1.1 reminds us how this would appear.

Example 1.1: GUI builder

def mainStage = builder.stage(title: "Example 01", x: 100, y: 100, width: 400, height: 300) {
  scene(stylesheets: "file:///C:/css/glib.css") {
    borderPane() {
      top(alignment: CENTER, margin: [10.0, 10.0, 10.0, 10.0]) {
        text(text: 'Example 01', id: 'text20')
      }
      center(margin: [10.0, 10.0, 10.0, 10.0]) {
        gridPane(hgap: 4, vgap: 4) {
          text(text: 'Please enter your name:', id: 'text16')
          textField(ID: 'locationTF', text: '', onAction: controller.onName)
         
          // ...
        }
      }
      bottom(alignment: CENTER, margin: [10.0, 10.0, 10.0, 10.0]) {
        button(text: 'Close', onAction: controller.onClose)
      }
    }
  }
}

In Example 1.1 factory methods, such as button and textField, are used to define the JavaFX components while nested closures define the structure and hence the appearance of the UI. The factory method calls are decorated with named parameters that set the various properties on the components. The builder aims to do as much of the heavy lifting, reducing the effort required by the developer. For example, the builder translates the factory method calls and their parameters into code that creates and initializes the components. The builder also arranges for the UI components defined in a closure to be added as child components of the enclosing container component.

That was the good part. What followed was the disappointment. The applications were completed by developing the supporting classes. They were anything but declarative, especially the method bodies that were assembled from the usual control flow statements. If statements within while statements and for statements within switch statements. I never thought of myself as a sloppy programmer but the implementation of these methods seemed to stand in sharp contrast with the declarative builder code.

Decomposing complex methods and providing private support methods or other support classes helped a little but still the code had a smell about it. I seemed to be operating at too low a level of abstraction when compared with Groovy's builders. Interestingly the implementation for the Groovy builders were themselves not especially declarative. Hmmm!

Using a search engine for declarative programming picked out many interesting articles often with attempts at informal definitions. Usually the what versus the how was cited. Declarative systems such as Yacc and SQL were compared to imperative programming languages. Maybe an imperative language like Groovy is not expected to be declarative because the programmer has to spell out the actions and their order.

When looking for actual illustrations most articles referred the reader to functional programming. Some characterized functional programming languages as examples of declarative programming along with logic programming languages and constraint programming languages.

So what is it that makes these languages declarative and what lessons might be learnt? The builder code above gives some pointers. First, state does not seem to play a major part in this code. There is state in the code. The named parameters on the factory method calls are initializing the state properties of the UI components. But, significantly, there is no explicit state transitions.

Second, there are no binding of results to variables. The builder code is like a single expression which, at the top level, delivers a Stage object. Expressions in the sub-levels deliver the various sub-components that form the UI. The sub-expressions are used to support assembly of the enclosing expression.

Perhaps then I should aim to make my Groovy coding much less dependent on state and state transitions. What place then for my use of control flow such as if statements and for loops? Usually they manipulate some state. Perhaps finding a solution for this will address my concern that I was operating at too low a level of abstraction.

All this may seem at odds with an imperative programming language such as Groovy. However, I can see immediately that if I have less state transitions I will have less testing to do since a significant part of testing is to ensure the transitions are correct.

What other aspects of functional programming might make a contribution to a more declarative style. What contribution might Groovy's closures make? How will my use of control flow statements such as for loops change? What can be done to reduce my use of state? Let's see where this takes us...



No comments: