Starting Anew

Is this experience familiar? You decide you need to start keeping a journal again. You open up your journal and begin writing. “It’s been too long since I last wrote,” you begin. You continue with a quick catch up of where you are in life. You comment on your desire to improve. And you commit to keeping a journal regularly.

As you finish, you leaf back through the pages to see when you last wrote. It was more than a year ago. Here’s what you said, “It’s been too long since I last wrote…,” And you closed that entry with a promise to keep a regular journal.

Journaling is a habit, and – like any habit – it takes concentrated effort to form at first. It’s a habit that I don’t have. Though, it’s a habit I’d like to establish.

I started this blog several years ago to fill a requirement in a business class. Each week, I was given a prompt to address here, in this blog. I’ve tried to bury those early posts because they no longer fully reflect who I am or what my priorities are. This being the Internet, I’m sure they’re recoverable.

As the class that prompted this blog came to its end, so did my regular upkeep of it. That’s a shame. I pay for the domain. And I think there’s some value to having a “vanity” domain to stamp something of my own mark on the Internet.

In the intervening years, I’ve tried to write here now and then. I’ve tried to establish some identity for this blog. But my efforts have been spurious at best.

The challenge, I think, is that I’ve always tried to scope the blog to some particular purpose or identity. And when I do that, I recognize two things. First, nobody really knows this blog exists. It’s public, so of course people can find it, but they’re not gonna find it unless they’re looking for it. Second, given my inconsistency, virtually any subject I might write on is almost certainly covered more thoroughly and reliably elsewhere.

But I need to create. I need a place to put my thoughts down. I want to have some identity beyond the circle of my close friends and associates. And so, against my own doubt that it will actually go anywhere, I will give this all another shot. And here’s what I plan to do.

Rather than try to tailor my site and my writing to one particular purpose, I’m going to make this more of a traditional blog or journal. In nearly every week of my life, there is at least one day, one hour, or one moment when I have a thought or opinion or experience that I think I’d like to share with others. That’s what I’ll do here. I will strive to write something every week, reflecting on my week, and capturing that opinion or experience I want to share.

Could I write more frequently? Sure. And maybe I will. But my only commitment at this point is to write once a week. Will my thoughts coalesce around a particular topic? Maybe. But that’s not my goal. My goal is simply consistency.

My hope is that, as I write consistently, there will be certain topics or ideas that become natural focuses for my writing. When, and if, there are, then I may spin those off into their own separate sections for this site, or I may redefine this site in terms of those topics. For now, though, consistent writing about my weekly thoughts and experiences is what I am committed to deliver.

Here goes nothing…

Starting Anew

My Philosophy: EAFP vs. LBYL in Python

In Python, there are two philosophies when it comes to accessing items in a dictionary. The first is EAFP (“Easier to Ask Forgiveness than Permission”), which says to access the key and handle a KeyError if one arises. The other is LBYL (“Look Before You Leap”), which says to check for a key’s existence in the dictionary before trying to access it. As with many programming philosophies, different people have different opinions about which approach to use.

This post on StackOverflow that first brought these philosophies to my attention pointed me to the Python glossary entry on EAFP. The Python glossary seems to prefer EAFP in its definition of the term.

In the past, though, I’ve tended to prefer LBYL. The author of Effective Python: 90 Specific Ways to Write Better Python seems also to lean that way in his recommendation, “Prefer get Over in and KeyError to Handle Missing Dictionary Keys.”

I decided on a compromise between the approaches based on a sidebar at RealPython.

My philosophy is to use EAFP when we expect the key to be in the dictionary in most cases but can handle it if the key is absent, and to use LBYL when we don’t expect the key to be in the dictionary in most cases but want to do something if it is present. A recent situation illustrates the philosophy in action.

I have a function that accepts a dictionary as a parameter and uses it to perform some action, e.g.:

def my_function(my_data: Mapping[str, Any]) -> None:
    write_to_table(my_data['id'], my_data['name'])

This is an extremely simplified example of a function that uses the dictionary, my_data, to provide arguments to another function.

I want to add some optional data to the dictionary. When present, the function should take additional action based on that data. Most of the time, though, this optional data will not be in the dictionary. This is where I would use the LBYL approach because:

  • The key is optional
  • The normal case is for the key to be absent

So, I end up with the following:

def my_function(my_data: Mapping[str, Any]) -> None:
    write_to_table(my_data['id'], my_data['name'])
    other_data = my_data.get('other')
    if other_data:
        write_to_other_table(other_data)

I have another function that gets some data from a database and cleans it up a little before returning it to the caller, e.g.:

def read_from_table(my_id: int) -> Mapping[str, Any]:
    my_data = get_from_db(my_id)
    try:
        del my_data['id']
    except:
        pass
    return my_data

In this function, there is a key I want to delete from the dictionary. In this case, I expect the key to be present most (actually, all) of the time, but I can keep going even if the key is absent (since I want to delete the key anyway). I use the EAFP approach here because:

  • The key is optional
  • The normal case is for the key to be present

In summary, when it comes to choosing between EAFP or LBYL, my philosophy is to use the approach that best expresses my expectation for what is the normal case, i.e. what I expect to be true most often:

  • Use EAFP when you expect the key to be present most of the time
  • Use LBYL when you expect the key to be absent most of the time

I believe this makes it explicit to a reader what I expect the usual case to be, and being explicit in my intent is Pythonic.

My Philosophy: EAFP vs. LBYL in Python

Error-handling in Go

One of the tiresome aspects of Go is its standard idiom for error-handling. In that idiom, functions return errors, callers check those errors, and then take action accordingly. Typically, that action is for the caller to return the error to its own caller, gradually passing it up the call-stack for something else to handle. In practice, this results in a lot of boilerplate code that looks like this:

func MyFunc() (int, error) {
  n, err := someOtherFunc()
  if err != nil {
    return nil, err
  }

  if err := anotherFunc(n); if err != nil {
    return nil, err
  }
 
  return n, nil
}

The code above illustrates a couple of variations on the technique, but the pattern should be evident. Call after call, you check the err to see if it’s nil.

This has two implications. First, there’s a lot of redundancy; it can quickly become tedious typing the same code again and again. Second, it gets hard to see what’s going on with the code because it’s hidden behind all of this error handling code. I’ve found there are a couple of things I can do.

If I strive for functional cohesion, I tend to end up with functions that have fewer lines of code because they focus on specific tasks. As a result, I end up having to write less of this boilerplate code per function simply because there’s less code per function generally.

For more complex functions, such as those with procedural cohesion, I can often use some sort of centralized error to make the procedure in the function more apparent by hiding the error-handling. Let me provide a couple of examples of this.

Central Error Handling in a Builder

In a current project, I have a Builder type similar to the GoF builder pattern. Something like this:

type Builder struct{
...
}

func (b Builder) AddObjA(a ObjA) error {
...
}

func (b Builder) AddObjB(b ObjB) error {
...
}

func (b Builder) constructPartA() (Product, error) {
...
}

func (b Builder) constructPartB(p Product) (Product, error) {
...
}

func (b Builder) finishConstruction(p Product) (Product, error) {
...
}

func (b Builder) GetFinishedProduct() (Product, error) {
  product, err := b.constructPartA()
  if err != nil {
    return nil, err
  }
  product, err = b.constructPartB(product)
  if err != nil {
    return nil, err
  }
  return b.finishConstruction(product)
}

In the actual code, the GetFinishedProduct function does a lot more, but the idea is the same: GetFinishedProduct calls several functions in a specific sequence to build the final product from parts that were added to the builder earlier on. The sequence of GetFinishedProduct was obscured by all the error-handling code.

To make it clearer, I added a central error field to the Builder, and then adjusted my “construction” methods to check that error field before they did anything. If the error field was non-nil, the methods simply did nothing. As a result, my GetFinishedProduct could simply call the construction methods in sequence without worrying about their errors. It looks like this:

type Builder struct{
...
  err error
}

...

func (b Builder) constructPartA() Product {
  ...
  product, err := doSomeWork()
  if err != nil {
    b.err = err
    return nil
  }
  return product
}

func (b Builder) constructPartB(p Product) Product {
  if b.err != nil {
    return nil
  }
  ...
}

func (b Builder) finishConstruction(p Product) Product {
  if b.err != nil {
    return nil
  }
  ...
}

func (b Builder) GetFinishedProduct() (Product, error) {
  product := constructPartA()
  product = constructPartB(product)
  product = finishConstruction(product)
  return product, b.err
}

Error-handling is still going on, but it happens in construction functions that tend to be very focused, so they have few lines of code and thus less error-handling to do. Additionally, every construction function stores any of its errors in b.err; this puts the Builder into an “error” state so that future construction functions become no-ops. Each construction function (except the first) checks to see if the Builder is in an error state and simply skips any work if it is.

As a result of these changes, the GetFinishedProduct method becomes far simpler and its construction procedure is more apparent. It doesn’t need to do any error-handling itself since the construction functions are taking care of that. It simply returns whatever the construction functions produced along with the Builder’s error state.

Central Error Handling in a Function

I arrived at a similar approach recently using anonymous functions. I had a scenario where I was creating an implementation of the io.WriterTo interface, i.e. a function with the signature

func (o MyObject) WriteTo(w io.Writer) (int64, error) {...}

One of the challenges with this function was the need to count how many bytes I’d written when I was using other objects to do a lot of the writing and, depending on the methods called, I was getting back an int count of lines written in some cases and int64 in others. This, in addition to error handling, was making the code unwieldy.

What I arrived at was the use of some anonymous methods to help with the accumulation of total bytes written and managing the error-handling in a fashion similar to what I used in the Builder. The final product looked something like this:

func (o MyObject) WriteTo(w io.Writer) (int64, error) {
  total, err := int64(0), error(nil)

  print := func(s string) {
    if err != nil { return }
    var n int
    n, err = fmt.Fprint(w, s)
    total += int64(n)
  }
 
  write := func(wt io.WriterTo) {
    if err != nil { return }
    var n int64
    n, err = wt.WriteTo(w)
    total += n
  }

  // Actual procedure
  print(headerText)
  for _, child := range o.children { // children are io.WriterTo
    write(child)
  }
  print(footerText)

  return total, err
}

Here, the central error is a variable, err, that is accessible to the anonymous functions (assigned to the print and write variables) via closure. As in the Builder’s construction methods, these assign any errors to the err variable and they check to see if that variable is nil before doing any work, becoming “no-ops” if the error is non-nil.

This approach, again, allows the function to express its central procedure more clearly since it doesn’t have to handle errors after every function call.

The driver in this case wasn’t really the central error handling. The real driver was wanting a way to deal with the fact that fmt.Fprint returns the count of written bytes as an int while io.WriteTo returns it as an int64. This required having multiple variables to capture the intermediate counts before adding them to the total. That drove me toward the use of anonymous functions to wrap that work, and once they were in place, it was a simple step to centralizing the error handling.

Conclusion

While I wish that Go gave us the option to use structured exception handling (i.e. try…catch… blocks), there are ways to deal with its native approach without having to write a ton of redundant code in each function. Keep your functions functionally cohesive (i.e. focused on doing one thing) and they’ll have fewer lines of code, and hence, fewer calls that can result in errors. For functions with sequential or procedural cohesion, where the function’s work is more about calling other functions in sequence, look for opportunities to “centralize” your errors so that you can reduce the error-handling and make the procedure clearer.

Error-handling in Go

Go is not an object-oriented language

I’ve spent the better part of my career working with object-oriented languages. I was exposed to them first through Visual Basic. From there, I learned Java, then C#, and then C++. Along the way, I made a deliberate study of object-oriented design, drinking deeply from the design patterns promulgated by the Gang of Four, and learning to reshape everything I did in terms of object-oriented design. It’s the way I think about, decompose, and solve problems.

When I started learning Go (https://golang.org), I approached it from this same mindset, and at first it felt like I was making some traction. In time, though, I discovered it wasn’t working. I kept running up against brick walls where it felt like I was almost there, but the wall between what I was trying to accomplish and what Go could do was simply too high.

The problem wasn’t Go. The problem was that I was trying to treat Go like an object-oriented language when it isn’t. Some ideas that commonly appear in object-oriented languages, like interfaces, misled me. The sort of “pseudo-inheritance” that happens when you embed one structure into another made me feel like maybe I could treat the embedded structure as a base class and the embedding structure as a subclass. But while these things hinted at or “smelled” like object-orientation, they really weren’t.

I had to approach Go on its own terms. It is not an object-oriented language. Structures in Go aren’t not polymorphic the way that objects are in C# or C++. Embedding is not the same thing as subclassing. Structures are not classes.

I have a way to go still. 20+ years of an object-oriented habit is hard to break. But it is essential to succeed in using Go. As I approach Go on its own terms, I find myself less frustrated, and I start to see the patterns of use in Go that will help me achieve my goals. I start to see Go’s strengths more than its weaknesses.

My advice to anyone coming to Go from an object-oriented background like mine is to leave that thinking at the door lest you find yourself making things needlessly complicated and thoroughly frustrating. Approach Go with an open mind.

Go is not an object-oriented language