Showing posts with label object-oriented programming. Show all posts
Showing posts with label object-oriented programming. Show all posts

Sunday, August 15, 2021

COBOL and Elixir

Someone has created a project to transpile (their word) COBOL to Elixir. I have some thoughts on this idea. But first, let's look at the example they provide.

A sample COBOL program:

      >>SOURCE FORMAT FREE
IDENTIFICATION DIVISION.
PROGRAM-ID. Test1.
AUTHOR. Mike Binns.
DATE-WRITTEN. July 25th 2021
DATA DIVISION.
WORKING-STORAGE SECTION.
01 Name     PIC X(4) VALUE "Mike".
PROCEDURE DIVISION.

DISPLAY "Hello " Name

STOP RUN.

This is "hello, world" in COBOL. Note that it is quite longer than equivalent programs in most languages. Also note that while long, it is still readable. Even a person who does not know COBOL can make some sense of it.

Now let's look at the same program, transpiled to Elixr:

defmodule ElixirFromCobol.Test1 do
  @moduledoc """
  author: Mike Binns
  date written: July 25th 2021
  """

  def main do
    try do
      do_main()
    catch
      :stop_run -> :stop_run
    end
  end 

  def do_main do
    # pic: XXXX
    var_Name = "Mike"
    pics = %{"Name" => {:str, "XXXX", 4}}
    IO.puts "Hello " <> var_Name
    throw :stop_run
  end
end

That is ... a lot of code. More than the code for the COBOL version! Some of that is due to the exception of "stop run" which in this small example seems to be excessive. Why wrap the core function inside a main() that simply exists to trap the exception? (There is a reason. More on that later.)

I'm unsure of the reason for this project. If it is a side project made on a whim, and used for the entertainment (or education) of the author, then it makes sense.

But I cannot see this as a serious project, for a couple of reasons.

First, the produced Elixir code is longer, and in my opinion less readable, than the original COBOL code. I may be biased here, as I am somewhat familiar with COBOL and not at all familiar with Elixir, so I can look at COBOL code and say "of course it does that" but when I look at Elixir code I can only guess and think "well, maybe it does that". Elixir seems to follow the syntax for modern scripting languages such as Python and Ruby, with some unusual operators.

Second, the generated Elixir code provides some constructs which are not used. This is, perhaps, an artifact of generated code. Code generators are good, up to a point. They tend to be non-thinking; they read input, apply some rules, and produce output. They don't see the bigger picture. In the example, the transpiler has produced code that contains the variable "pics" which contains information about the COBOL programs PICTURE clauses, but this "pics" variable is not used.

The "pics" variable hints at a larger problem, which is that the transpiled code is not running the equivalent program but is instead interpreting data to achieve the same output. The Elixir program is, in fact, a tuned interpreter for a specific COBOL program. As an interpreter, its performance will be less than that of a compiled program. Where COBOL can compile code to handle the PICTURE clauses, the Elixir code must look up the PICTURE clause at runtime, decode it, and then take action.

My final concern is the most significant. The Elixir programming language is not a good match for the COBOL language. Theoretically, any program written in a Turing-complete language can be re-written in a different Turing-complete language. That's a nice theory, but in practice converting from one language to another can be easy or can be difficult. Modern languages like Elixir have object-oriented and structured programming constructs. COBOL predates those constructs and has procedural code and global variables.

We can see the impedance mismatch in the Elixir code to catch the "stop run" exception. A COBOL program may contain "STOP RUN" anywhere in the code. The Elixir transpiler project has to build extra code to duplicate this capability. I'm not sure how the transpiler will handle global variables, but it will probably be a method that is equally tortured. Converting code from a non-structured language to a structured programming language is difficult at best and results in odd-looking code.

My point here is not to insult or to shout down the transpiler project. It will probably be an educational experience, teaching the author about Elixir and probably more about COBOL.

My first point is that programs are designed to match the programming language. Programs written in object-oriented languages have object-oriented designs. Programs written in functional languages have functional designs. Programs written in non-structured languages have... non-structured designs. The designs from one type of programming language do not translate readily to a programming language of a different type.

My second point is that we assume that modern languages are better than older languages. We assume that object-oriented languages like C++, C#, and Java are better than (non-OO) structured languages like Pascal and Fortran-77. Some of us assume that functional languages are better than object-oriented languages.

I'm not so sure about those assumptions. I think that object oriented languages are better at some tasks that mere structured languages, and older structured-only languages are better at other tasks. Object-oriented languages are useful for large systems; they let us organize code into classes and functions, and even larger constructs through inheritance and templates. Dynamic languages like Python and Ruby are good for some tasks but not others.

And I must conclude that even older, non-functional, non-dynamic, non-object-oriented, non-structured programming languages are good for some tasks.

One analogy of programming languages is that of a carpenter's toolbox: full of various tools for different purposes. COBOL, one of the oldest languages, might be considered the hammer, one of the oldest tools. Hammers do not have the ability of saws, drills, tape measures, or levels, but carpenters still use them, when the task is appropriate for a hammer.

Perhaps we can learn a thing or two from carpenters.

Thursday, August 13, 2020

Add Functional Programming to Object-oriented Programming, not the other way round

The programming world has multiple paradigms, or styles of programming. The reigning champion is Object-oriented Programming, embodied in C++, Java, and C# and oriented around, well, objects. The prior champion was Structured Programming (or Procedural Programming) represented by C and Pascal and oriented around control structures (and avoiding GOTOs). The challenger is Functional Programming oriented around functions, and in the languages Haskell and F#, among others. An older paradigm is Unstructured Programming (USP), which does use GOTOs and lacks the modern IF/THEN/ELSE and DO/WHILE constructs of Structured Programming.

SP and USP operate in the same space: small and medium-side programs. Since they operate in the same space, one often picks one of them, but does not use both. (One can use both, but it is not easy.)

Object-oriented programming (OOP) operates in "the large" and helps us organize large systems. Structured Programming (SP) and Functional Programming (FP) operate in "the small" and help us build short blocks of code that are reliable and readable. In this view, SP complements OOP and they work well together. SP and FP, on the other hand, compete in the same space. We can build large systems with OOP and SP, or with OOP and FP. (We can build small programs with SP alone, or with FP alone. OOP is not useful with small programs.)

Structured Programming is demonstrably better than Unstructured Programming. Functional programming is arguably better then the older Structured Programming. One would think that we would use the best paradigm, and that we would use Functional Programming. But we don't. It takes time to learn a programming paradigm, and most programmers know the Structured Programming paradigm. Structured Programming is what we have, buried inside many modern systems.

The mix of Object-oriented Programming and Structured Programming makes sense. To use a programming paradigm, or a mix of paradigms, one must have a language that supports the paradigms. C++, Java, and C# all support OOP and SP. Pascal supports only SP; Object Pascal and Delphi support both. Haskell supports FP.

The advocates of Functional Programming have noticed that their preferred languages have achieved modest acceptance, but none have become as popular as the most popular languages C++, Java, and C#, or even Python. That lack of acceptance makes some sense, as a Functional Programming language does not have the Object-oriented Programming concepts that help us organize large systems. (Conversations with practitioners have probably ended with project managers and OOP programmers saying something along the lines of "Functional Programming is nice, but we have a large code base in an Object-oriented Programming language, and we cannot give up that organization." 

I think these conversations have spurred the advocates of Functional Programming to merge OOP into FP languages, the hope that project managers and programmers will accept the new combination. The combination of object-oriented programming and functional programming is a nice trick, and it works.

But this approach is, in my view, the wrong approach.

When programmers and managers say that they want a programming language that supports OOP and FP, they don't really mean that they want an FP programming language that supports OOP.

What they want is a programming language that supports their current code base and allows them to add Functional Programming in small doses.

They want a compromise, much like C++ was a compromise for OOP. C++ offered classes to C programmers -- and was originally called "C with classes". It didn't mandate a pure OOP approach. Instead, it let programmers add OOP to an existing code base, and add OOP in a gradual manner.

The "FP plus OOP" approach fails because the existing code base is not OOP, but OOP with SP. A "FP plus OOP" language requires that all of the SP code blocks be re-written in FP. That can work for tiny programs and classroom exercises, but it doesn't work in the industry with large sets of code.

Yes, some companies have large, million-lines-of-code systems written in FP. But those systems were built in FP from the beginning.

Companies that have large, million-lines-of-code systems written in OOP (with procedural code too) don't want to stop everything and re-write that code. They want to add FP in small doses. They want the benefit of FP in a few key locations. Over time, they can expand FP to other parts of the system.

They don't want an FP language with OOP extensions. They want an "OOP and SP" language with FP extensions.

There is a language that may provide a path forward. Microsoft has its F# language, which is a Functional Programming language, but it also lives in .NET, which means one can combine F# modules with C# modules. If one has a system in C# and wants the advantages of Functional Programming, F# is worth investigating.

But beyond F#, I see no languages that offer a compromise between paradigms, and a way forward for managers and programmers. The current set of FP languages are too "pure" and don't allow the mixing of OOP, FP, and SP paradigms.

Perhaps we will see some new languages that bridge the worlds of Structured Programming, Object-oriented Programming, and Functional Programming.

Wednesday, September 4, 2019

Don't shoot me, I'm only the OO programming language!

There has been a lot of hate for object-oriented programming of late. I use the word "hate" with care, as others have described their emotions as such. After decades of success with object-oriented programming, now people are writing articles with titles like "I hate object-oriented programming".

Why such animosity towards object-oriented programming? And why now? I have some ideas.

First, we have the age of object-oriented programming (OOP) as the primary paradigm for programming. I put the acceptance of OOP somewhere after the introduction of Java (in 1995) and before Microsoft's C# and .NET initiative (in 1999), which makes OOP about 25 years old -- or one generation of programmers.

(I know that object-oriented programming was around much earlier than C# and Java, and I don't mean to imply that Java was the first object-oriented language. But Java was the first popular OOP language, the first OOP language that was widely accepted in the programming community.)

So it may be that the rejection of OOP is driven by generational forces. Object-oriented programming, for new programmers, has been around "forever" and is an old way of looking at code. OOP is not the shiny new thing; it is the dusty old thing.

Which leads to my second idea: What is the shiny new thing that replaces object-oriented programming? To answer that question, we have to answer another: what does OOP do for us?

Object-oriented programming, in brief, helps developers organize code. It is one of several techniques to organize code. Others include Structured Programming, subroutines, and functions.

Subroutines are possibly the oldest techniques to organize code. They date back to the days of assembly language, when code that was executed more than once was called with a "branch" or "call" or "jump subroutine" opcode. Instead of repeating code (and using precious memory), common code could be stored once and invoked as often as needed.

Functions date back to at least Fortran, consolidating common code that returns a value.

For two decades (from the mid 1950s to the mid-1970s), subroutines and functions were the only way to organize code. In the mid-1970s, the structured programming movement introduced an additional way to organize code, with IF/THEN/ELSE and WHILE statements (and an avoidance of GOTO). These techniques worked at a more granular level that subroutines and functions. Structured programming organized code "in the small" and subroutines and functions organized code "in the medium". Notice that we had no way (at the time) to organize code "in the large".

Techniques to organize code "in the large" did come. One attempt was dynamic-linked libraries (DLLs), introduced with Microsoft Windows but also used by earlier operating systems. Another was Microsoft's COM, which organized the DLLs. Neither were particularly effective at organizing code.

Object-oriented programming was effective at organizing code at a level higher than procedures and functions. And it has been successful for the past two-plus decades. OOP let programmers build large systems, sometimes with thousands of classes and millions of lines of code.

So what technique has arrived that displaces object-oriented programming? How has the computer world changed, that object-oriented programming would become despised?

I think it is cloud programming and web services, and specifically, microservices.

OOP lets us organize a large code base into classes (and namespaces which contain classes). The concept of a web service also lets us organize our code, in a level higher than procedures and functions. A web service can be a large thing, using OOP to organize its innards.

But a microservice is different from a large web service. A microservice is, by definition, small. A large system can be composed of multiple microservices, but each microservice must be a small component.

Microservices are small enough that they can be handled by a simple script (perhaps in Python or Ruby) that performs a few specific tasks and then exits. Small programs don't need classes and object-oriented programming. Object-oriented programming adds cost to simple programs with no corresponding benefit.

Programmers building microservices in languages such as Java or C# may feel that object-oriented programming is being forced upon them. Both Java and C# are object-oriented languages, and they mandate classes in your program. A simple "Hello, world!" program requires the definition of at least one class, with at least one static method.

Perhaps languages that are not object-oriented are better for microservices. Languages such as Python, Ruby, or even Perl. If performance is a concern, the compiled languages C and Go are available. (It might be that the recent interest in C is driven by the development of cloud applications and microservices for them.)

Object-oriented programming was (and still is) an effective way to manage code for large systems. With the advent of microservices, it is not the only way. Using object-oriented programming for microservices is overkill. OOP requires overhead that is not helpful for small programs; if your microservice is large enough to require OOP, then it isn't a microservice.

I think this is the reason for the recent animosity towards object-oriented programming. Programmers have figured out the OOP doesn't mix with microservices -- but they don't know why. They fell that something is wrong (which it is) but they don't have the ability to shake off the established programming practices and technologies (perhaps because they don't have the authority).

If you are working on a large system, and using microservices, give some thought to your programming language.

Tuesday, July 18, 2017

A sharp edge in Ruby

I like the Ruby programming language. I've been using it for several projects, including an interpreter for the BASIC language. (The interpreter for BASIC was an excuse to do something in Ruby and learn about the language.)

My experience with Ruby has been a good one. I find that the language lets me do what I need, and often very quickly. The included classes are well-designed and include the functions I need. From time to time I do have to add some obscure capability, but those are rare.

Yet Ruby has a sharp edge to it, an aspect that can cause trouble if you fail to pay attention.

That aspect is one of its chief features: flexibility.

Let me explain.

Ruby is an object-oriented language, which means the programmer can define classes. Each class has a name, some functions, and usually some private data. You can do quite a bit with class definitions, including class variables, class instance variables, and mix-ins to implement features in multiple classes.

You can even modify existing classes, simply by declaring the same class name and defining new functions. Ruby accepts a second definition of a class and merges it into the first definition, quietly and efficiently. And that's the sharp edge.

This "sharp edge" cut me when I wasn't expecting it. I was working on my BASIC interpreter, and had just finished a class called "Matrix", which implemented matrix operations within the language. My next enhancement was for array operations (a matrix being a two-dimensional structure and an array being a one-dimensional structure).

I defined a class called "Array" and defined some functions, including a "to_s" function. (The name "to_s" is the Ruby equivalent of "ToString()" or "to_string()" in other languages.)

And my code behaved oddly. Existing functions, having nothing to do with arrays or my Array class, broke.

Experienced Ruby programmers are probably chuckling at this description, knowing the problem.

Ruby has its own Array class, and my Array class was not a new class but a modification of the existing, built-in class named "Array". My program, in actuality, was quite different from my imagined idea. When I defined the function "to_s" in "my" Array class, I was actually overwriting the existing "to_s" function in the Ruby-supplied Array class. And that happened quietly and efficiently -- no warning, no error, no information message.

Part of this problem is my fault. I was not on my guard against such a problem. But part of the problem, I believe, is Ruby's -- specifically the design of Ruby. Letting one easily modify an existing class, with no warning, is dangerous. And I say this not simply due to my background with languages that use static checking.

My error aside, I can think of two situations in which this can be a problem. The first is when a new version of the Ruby language (and its system libraries) are released. Are there new classes defined in the libraries? Could the names of those classes duplicate any names I have used in my project? For example, will Ruby one day come with a class named "Matrix"? If it does, it will collide with my class named "Matrix". How will I know that there is a duplicate name?

The second situation is on a project with multiple developers. What happens if two developers create classes with the same name? Will they know? Or will they have to wait for something "weird" to happen?

Ruby has some mechanisms to prevent this problem. One can use namespaces within the Ruby language, to prevent such name conflicts. A simple grep of the code, looking for "class [A-Z][\w]" and then a sort will identify duplicate names. But these solutions require discipline and will -- they don't come "for free".

As I said earlier, this is a sharp edge to Ruby. Is it a defect? No, I think this is the expected behavior for the language. It's not a defect. But it is an aspect of the language, and one that may limit the practicality of Ruby on large applications.

I started this blog with the statement that I like Ruby. I still like Ruby. It has a sharp edge (like all useful tools) and I think that we should be aware of it.

Monday, January 16, 2017

Discipline in programming

Programming has changed over the years. We've created new languages and added features to existing languages. Old languages that many consider obsolete are still in use, and still changing. (COBOL and C++ are two examples.)

Looking at individual changes, it is difficult to see a general pattern. But stepping back and getting a broader view, we can see that the major changes have increased discipline and rigor.

The first major change was the use of high-level languages in place of assembly language. Using high-level languages provided some degree of portability across different hardware (one could, theoretically, run the same FORTRAN program on IBM, Honeywell, and Burroughs mainframes). It meant a distant relationship with the hardware and a reliance on the compiler writers.

The next change was structured programming. It changed our notions of flow control, using "while", "if/then/else", and "for" structures and discouraged the use of "goto".

Then we adopted relational databases, separate from the application program. It required using an API (later standardized as SQL) rather than accessing data directly, and it required thought and planning for the database.

Relational databases forced us to organize data stored on disk. Object-oriented programming forced us to organize data in memory. We needed object models and for very large projects, separate teams to manage the models.

Each of these changes added discipline to programming. The shift to compilers required reliable compilers and reliable vendors to support them. Structured programming applied rigor to the sequence of computation. Relational databases applied rigor to the organization of data stored outside of memory, that is, on disk. Object-oriented programming applied rigor to the organization of data stored in memory.

I should note that each of these changes was opposed. Each had naysayers, usually basing their arguments on performance. And to be fair, the initial implementation of each change did have lower performance than the old way. Yet each change has a group of advocates (I call them "the Pascal crowd" after the early devotees to that language) who pushed for the change. Eventually, the new methods were improved and accepted.

The overall trend is towards rigor and discipline. In other words, the Pascal crowd has consistently won the debates.

Which is why, when looking ahead, I think future changes will keep moving in the direction of rigor and discipline. There may be minor deviations from this path, with new languages introducing undisciplined concepts, but I suspect that they will languish. The successful languages will require more thought, more planning, and prevent more "dangerous" operations.

Functional programming is promising. It applies rigor to the state of our program. Functional programming languages use immutable objects, which once made cannot be changed. As the state of the program is the sum of the state of all variables, functional programming demands more thought given to the state of our system. That fits in with the overall trend.

So I expect that functional languages, like structured languages and object-oriented languages, will be gradually adopted and their style will be accepted as normal. And I expect more changes, all in the direction of improved rigor and discipline.

Thursday, July 21, 2016

Spaghetti in the Cloud

Will cloud computing eliminate spaghetti code? The question is a good one, and the answer is unclear.

First, let's understand the term "spaghetti code". It is a term that dates back to the 1970s according to Wikipedia and was probably an argument for structured programming techniques. Unstructured programming was harder to read and understand, and the term introduced an analogy of messy code.

Spaghetti code was bad. It was hard to understand. It was fragile, and small changes led to unexpected failures. Structured programming was, well, structured and therefore (theoretically) spaghetti programming could not occur under the discipline of structured programming.

But theory didn't work quite right, and even with the benefits of structured programming, we found that we had code that was difficult to maintain. (In other words, spaghetti code.)

After structured programming, object-oriented programming was the solution. Object-oriented programming, with its ability to group data and functions into classes, was going to solve the problems of spaghetti code.

Like structured programming before it, object-oriented programming didn't make all code easy to read and modify.

Which brings us to cloud computing. Will cloud computing suffer from "spaghetti code"? Will we have difficult to read and difficult to maintain systems in the cloud?

The obvious answer is "yes". Companies and individuals who transfer existing (difficult to read) systems into the cloud will have ... difficult-to-understand code in the cloud.

The more subtle answer is... "yes".

The problems of difficult-to-read code is not the programming style (unstructured, structured, or object-oriented) but in mutable state. "State" is the combination of values for all variables and changeable entities in a program. For a program with mutable state, these variables change over time. For one to read and understand the code, one must understand the current state, that is, the current value of all of those values. But to know the current value of those variables, one must understand all of the operations that led to the current state, and that list can be daunting.

The advocates of functional programming (another programming technique) doesn't allow for mutable variables. Variables are fixed and unchanging. Once created, they exist and retain their value forever.

With cloud computing, programs (and variables) do not hold state. Instead, state is stored in databases, and programs run "stateless". Programs are simpler too, with a cloud system using smaller programs linked together with databases and message queues.

But that doesn't prevent people from moving large, complicated programs into the cloud. It doesn't prevent people from writing large, complicated programs in the cloud. Some programs in the cloud will be small and easy to read. Others will be large and hard to understand.

So, will spaghetti code exist in the cloud? Yes. But perhaps not as much as in previous technologies.

Tuesday, January 19, 2016

Functional programming is waiting for microservices

Functional programming has been bubbling on the edge of mainstream development for years (perhaps decades). The advent of microservices may give functional programming its big break.

Why has it taken so long for the industry to adopt functional programming? I can think of a few reasons:
  • The effort to learn the techniques of functional programming
  • The effort to convert existing programs
  • The belief that current techniques are "good enough"
Individual developers (and teams of developers) have already learned the techniques of object-oriented programming. They are comfortable with them. They know the tools and understand the issues of object-oriented development. Changing to functional programming is a big change, with new techniques and new issues. That change can make people reluctant to switch to functional programming.

Existing systems are large and complex. Converting them to functional programming is a large effort, and one that must be done completely. The switch from procedural programming to object-oriented programming could be done gradually, especially if you were moving from C to C++. Functional programming has no gradual path.

Switching from object-oriented programming to functional programming incurs a cost, and the benefits are not so clear. The "return on investment" is not obvious.

These are all reasons to not switch from object-oriented programming to functional programming. (And they're pretty good reasons.)

Microservices change the equation.

Microservices are small, independent services. Changing an existing system (probably a large, monolithic system) to one that is composed of microservices is a large significant effort. It requires the decomposition of the monolith into smaller pieces.

If one is constructing small, independent services, either as part of a re-engineering effort or a new system, one can certainly consider functional programming -- if only for some of the microservices. Writing small functional programs is much easier than writing large systems in a functional language. (A statement that holds for object-oriented programming languages, and procedural languages, and assembly language.)

For microservices, a functional language may be better than an object-oriented one. Object-oriented languages are suited to large systems; classes and namespaces are designed for organizing the data and code of large systems. Microservices are, by design, small. It is possible that functional programming will be a better match for the small code of microservices.

If you are building microservices, or just contemplating microservices, think about functional programming.

Wednesday, May 6, 2015

Cloud apps may not need OOP

The rise of different programming styles follows the need for program sizes.

The earliest programs were small -- tiny by today's standards. Most would fit on a single printed page; the largest took a few pages.

Programs larger than a few pages quickly became a tangled mess. Structured Programming (SP) was a reaction to the need for larger programs. It was a technique to organize code and allow programmers to quickly learn an existing system. With SP we saw languages that used structured techniques: Pascal and C became popular, and Fortran and BASIC changed to use the structured constructs IF-THEN-ELSE and DO-WHILE.

Structured Programming was able to organize code up to a point, but it could not manage the large systems of the 1990s. Object-oriented programming (OOP) was a reaction to the need for programs larger than several hundred printed pages. With OOP we saw languages that used object-oriented techniques: Java and C# became popular, C mutated into C++, and Pascal mutated into ObjectPascal. These new languages (and new versions of old languages) used the object-oriented constructs of encapsulation, inheritance, and polymorphism.

Cloud computing brings changes to programming, but in a new way. Instead of larger programs, cloud computing allows for (and encourages) smaller programs. The need for large, well-organized programs has been replaced by a need for well-organized systems of small programs. In addition, the needs placed on the small programs are different from the needs of the old, pre-cloud programs: cloud programs must be fast, replicable, and substitutable. The core idea of cloud computing is a that a number of servers are ready to respond to requests and that any server (of a given class) can handle your request -- you don't need a specific server.

In this environment, object-oriented programming is less useful. It requires some overhead -- for the design or programs and at run time. Its strength is to organize code for large programs, but it offers little for small programs. I expect that people will move away from OOP languages for cloud systems, and towards languages than emphasize readability and reliability.

I don't expect a renaissance of Structured Programming. I don't expect anyone to move back to the older SP-inspired languages of Pascal and Fortran-77. Cloud computing may be the technology that pushes us to move to the "Functional Programming" style. Look for cloud-based applications to use functional languages such as Haskell and Erlang. (Maybe F#, for Microsoft shops.)

Monday, July 28, 2014

Improving code can cause an explosion of classes

Object-oriented programming took the world by storm in the 1990s. Those early days saw a lot of programmers learning new skills.

It took some time to truly learn object-oriented programming. The jump from structured programming (or procedural code) to object-oriented programming was not small. (And is still not small.)

Many early attempts at object-oriented programming were inelegant if not amateurish. They contained mistakes, but the errors are only visible in hindsight. Programmers who are inexperienced in a new technique make mistakes. (I'm one of them.)

Common problems were:

  • large classes with many purposes
  • long functions (procedural code wrapped in object clothing)
  • excessive inheritance
  • too little inheritance
  • weak encapsulation (little or no use of access control)
  • little or no composition

Legacy systems often contain these problems. More than three decades after their inception, systems contain original design flaws. The problems remain because they are difficult to correct and the return on the investment is unclear. I often argue that a better design reduces maintenance costs in the future, and the counter-argument is that the current development team knows the code and would gain little from an improved design.

When I can convince the system owners of the benefits of improved code (and I am becoming more convincing over time), we see a remarkable transformation in the code.

The most obvious change is in the number of classes. The revised system contains many more classes, often several times the original number. Yet while the number of classes increases, the total number of lines of code decreases. The construction of new classes allows for the consolidation of duplicate code, something that occurs often in legacy systems.

The new classes are usually small. Instead of the large, multipurpose classes of the earlier design, I move functions to small, single-purpose classes. Some classes are mere data containers, others hold one or two elements and provide a small number of functions on those elements. While small, these classes have a big effect on the readability of the code: they eliminate low-level operations from high-level and mid-level code, allowing the reader to focus on the higher level operations.

Small classes are much easier to test, and much easier to test with automated tools. Even C++ can use automated tests to verify the operation of classes. Automated tests relieve a burden from developers (and testers or "QA" folk) and allow them to direct their efforts to building and maintaining meaningful tests.

A large number of small classes provides an additional benefit: the ability to group classes into libraries. Large (or large-ish) early object-oriented systems tend to group all of the classes into a single package, usually called "the application". With a large number of classes, the system maintainers see groups of classes emerge (perhaps all of the database classes, or all of the elementary data classes). These groupings can be formalized with libraries. For very large projects, these libraries can be maintained by different teams. Libraries can also be shared across multiple projects, reducing the duplication of effort at a larger scale.

Modernizing legacy systems can lead to an "explosion of classes", and this can be a good thing. Smaller classes are easier to understand and maintain. They can be tested independently. They can be grouped into libraries. Do not fear such an increase in the number of classes in your code.

Wednesday, June 11, 2014

Learning to program, without objects

Programming is hard.

Object-oriented programming is really hard.

Plain (non-object-oriented) programming has the concepts of statements, sequences, loops, comparisons, boolean logic, variables, variable types (text and numeric), input, output, syntax, editing, and execution. That's a lot to comprehend.

Object-oriented programming has all of that, plus classes, encapsulation, access, inheritance, and polymorphism.

Somewhere in between the two is the concept of modules and multi-module programs, structured programming, subroutines, user-defined types (structs), and debugging.

For novices, the first steps of programming (plain, non-object-oriented programming) are daunting. Learning to program in BASIC was hard. (The challenge was in organizing data into small, discrete chunks and processes into small, discrete steps.)

I think that the days of an object-oriented programming language as the "first language to learn" are over. We will not be teaching C# or Java as the introduction to programming. (And certainly not C++.)

The introduction to programming will be with languages that are not necessarily object-oriented: Python or Ruby. Both are, technically, object-oriented programming languages, supporting classes, inheritance, and polymorphism. But you don't have to use those features.

C# and Java, in contrast, force one to learn about classes from the start. One cannot write a program without classes. Even the simple "Hello, world!" program in C# or Java requires a class to hold main() .

Python and Ruby can get by with a simple

print "Hello, world"

and be done with it.

Real object-oriented programs (ones that include a class hierarchy and inheritance and polymorphism) require a bunch of types (at least two, probably three) and operations complex enough to necessitate the need for so many types. The canonical examples of drawing shapes or simulating an ATM are complex enough to warrant object-oriented code.

A true object-oriented program has a minimum level of complexity.

When learning the art of programming, do we want to start with that level of complexity?

Let us divide programming into two semesters. The first semester can be devoted to plain programming. The second semester can introduce object-oriented programming. I think that the "basics" of plain programming are enough for a single semester. I also think that one must be comfortable with those basics before one starts with object-oriented programming.

Sunday, June 8, 2014

Untangle code with small classes

If you want to simplify code, build small classes.

I have written (for different systems) classes for things such as ZIP Codes, account numbers, weights, years, year-month combinations, and file names.

These are small, simple classes, usually equipped with a constructor, comparison operators, and a "to string" operator. Sometimes they have other operators. For example, the YearMonth class has next_month() and previous_month() functions.

Why create a class for something as simple? After all, a year can easily be represented by an int (or an unsigned int, if you prefer). A file name can be held in a string. Why have a separate class for them?

Small classes provide a number of benefits.

Check for validity The constructor can check for the validity of the contents. With the proper checks in place, you know that every instance of the class is a valid instance. With primitive types (such as a string to hold a ZIP Code), you are never sure.

Consolidate redundant code A class can hold the logic that is duplicated in the main code. The Year class can tell if a year is a leap year, instead of repeating if (year % 4 == 0) in the code. It is easier (and more readable) to have code say if (year.is_leap_year()).

Consistent operations Our Year class performs the proper calculation for leap years (not the simple one listed above). Using the Year class for all instances of a year means that the calculations for leap year are consistent (and correct).

Clear names for operations Our Year class has operations named next_year() and previous_year() which give clear meaning to the operations year + 1 and year - 1.

Limit operations Custom classes provide the operations you specify and no others. The standard library provides classes with lots of operations, some of which may be inappropriate for your needs.

Add operations Our YearMonth class has operations next_month() and previous_month(), operations which are not supplied in the standard library's Date class. (Yes, one can add a TimeSpan object, provided one gets the right number of days in the TimeSpan, but the code is more complex.) Also, our YearMonth class can calculate the quarter of the year, something we need for our computations.

Prevent accidental use An object of a specific class cannot be used accidentally. If passed to a function or class, the target must be ready to accept the class. Our Year class cannot be carelessly passed to another function. If we stored our years in ints, those ints could be passed to any function that expected an int.

These benefits simplify the main code. Custom classes for small data elements let you ensure that objects are complete and internally consistent. They let you consolidate logic into a single place. They let you tailor the operations to your needs. They prevent accidental use or assignment.

Simplifying the main code means that the main code becomes, well, simpler. By moving low-level operations to low-level classes, your "mainline" code focusses on higher-level concepts. You spend less of your time worrying about low-level things and more of your time thinking about high-level (that is, application-level) ideas.

If you want to simplify code, build small classes.

Tuesday, December 17, 2013

The transition from object-oriented to functional programming

I am convinced that we will move from object-oriented programming to functional programming. I am also convinced that the transition will be a difficult one, more difficult that the transition from structured programming to object-oriented programming.

The transition from structured programming to object-oriented programming was difficult. Object-oriented programming required a new view of programming, a new way of organizing data and code. For programmers who had learned the ways of structured programming, the shift to object-oriented programming meant learning new techniques.

That in itself is not enough to cause the transition to functional programming to be more difficult. Functional programming is, like object-oriented programming, a new view of programming and a new way of organizing data and code. Why would the transition to functional programming be more difficult?

I think the answer lies within the organization of programs. Structured programming, object-oriented programming, and functional programming all specify a number of rules for programs. For structured programming, functions and subroutines should have a single entry point and exit point and IF/THEN/ELSE blocks and FOR/NEXT loops should be used instead of GOTO statements.

Object-oriented programming groups data into classes and uses polymorphism to replace some conditional statements. But object-oriented programming was not totally incompatible with structured programming (or 'procedural programming'). Object-oriented programming allowed for a top level of new design with lower layers of the old design. Many early object-oriented programs had large chunks of procedural code (and some still do to this day). The thin layer of objects simply acted as a container for structured code.

Functional programming doesn't have this same degree of compatibility with object-oriented programming (or structured programming). Functional programming uses immutable objects; object-oriented programming is usually about mutable objects. Functional programming works with sets of data and leverages tail recursion efficiently, object-oriented programming uses the explicit loops and conditional statements of procedural programming.

The constructs of functional programming work poorly at containing object-oriented constructs. The "trick" of wrapping old code in a containing layer of new code may not work with functional programming and object-oriented programming. It may be better to build functional programming constructs inside of object oriented programming constructs, working from the "inside out" rather than from the "outside in" of the object-oriented transition.

One concept that has helped me transition is that of immutable objects. This is a notion that I have "imported" from functional programming into object-oriented programming. (And I must admit that the idea is not mine or not even new; Java's String objects are immutable and have been since its inception.)

The use of immutable objects has improved my object-oriented programs. It has moved me in the direction of functional programming -- a step in the transition.

I believe that we will transition from object-oriented programming to functional programming. I foresee a large effort to do so, and I foresee that some programs will remain object-oriented programs, just as some legacy programs remain procedural programs. I am uncertain of the time frame; it may be in the next five years or the next twenty. (The advantages of functional programming are compelling, so I'm tending to think sooner rather than later.)

Wednesday, November 20, 2013

We need a new UML

The Object Management Group has released a new version of UML. The web site for Dr. Dobb's asks the question: Do You Even Care? It's a proper question.

It's proper because UML, despite a spike of interest in the late 1990s, has failed to move into the mainstream of software development. While the Dr. Dobb's article claims ubiquity ("dozens of UML books published, thousands of articles and blogs posted, and thousands of training classes delivered"), UML is anything but ubiquitous. If anything, UML has been ignored in the latest trends of software: agile development techniques and functional programming. It is designed for large projects and large teams designing the system up front and implementing it according to detailed documents. It is designed for systems built with mutable objects, and functional programming avoids both objects and mutable state.

UML was built to help us design and build large complex systems. It was meant to abstract away details and let us focus on the structure, using a standard notation that could be recognized and understood by all practitioners. We still need those things -- but UML doesn't work for a lot of projects. We need a new UML, one that can work with smaller projects, agile projects, and functional programming languages.

Saturday, June 22, 2013

Functional programming has no loud-mouth advocate

I'm reading "The Best of Booch", a collection of essays written by Grady Booch in the 1990s.

In the 1990s, the dominant programming method was structured programming. Object-oriented programming was new and available in a few shops, but the primary programming style was structured. (Structured programming was the "better" form of programming introduced in the 1970s.)

We had learned how to use structured programming, how to write programs in it, and how to debug programs in it. We (as an industry) had programming languages: C, Visual Basic, and even structured methods for COBOL. Our compilers and IDEs supported those languages.

We had accepted structured programming, adopted it, and integrated it into our processes.

We had also learned that structured programming was not perfect. Our programs were hard to maintain. Our programs contained bugs. Our programs were difficult to analyze.

Object-oriented programming was a way forward, a way to organize our programs and reduce defects. Booch was an advocate, out in front of the crowd.

Now, these essays were also a way for Booch to hawk his business, which was consulting and training in UML and system design. Boosh was one of the early proponents of object-oriented programming, and one of the participants in the "notation wars" prior to UML. The agreement on UML was a recognition that there was more money to be made in supplying a uniform notation for system design than in fighting for one's own notation.

But despite the advertising motivation, the articles contain a strong set of content. One can feel the passion for object-oriented programming in Booch. These are legitimate articles on a new technology first, and convenient advertising for his business a distant second.

Fast-forward to the year 2013. We have accepted object-oriented programming as mainstream technology. People (and projects) have been using it for years -- no, decades. We have learned how to use object-oriented programming, how to write programs in it, and how to debug programs in it. We (as an industry) have programming languages: C++, Java, Python, and Ruby. Our compilers and IDEs support these languages.

We have accepted object-oriented programming, adopted it, and integrated it into our processes.

We have also learned that object-oriented programming is not perfect. Our programs are hard to maintain. Our programs contain bugs. Our programs are difficult to analyze.

In short, we have the same problems with object-oriented programs that we had with structured programs.

Now, functional programming is a way forward. It is a way to organize our programs and reduce defects. (I know; I have used it.)

But where are the proponents? Where is the Grady Booch of functional programming?

I have seen a few articles on functional programming. I have read a few books on the topic. Most articles and books have been very specific to a new language, usually Scala or Haskell. None have had the broader vision of Booch. None have described the basic benefits of functional programming, the changes to our teams and processes, or the implications for system architecture and design.

At least, none that I have read. Perhaps I have missed them, in my journeys in technical writings.

Perhaps there is no equivalent to Grady Booch for functional programming. Or perhaps one does not exist yet. Perhaps the time is too early, and the technology must develop a bit more. (I tend to think not, as functional programming languages are ready for use.)

Perhaps it is a matter of passion and business need. Booch wrote his articles because he felt strongly about the technology, and because he had a business case for them.

Perhaps it is a matter of distribution. The software business in 2013 is very different from the software business in 1997; the big change is the success of open source.

Open source projects emerge into the technology mainstream through channels other than books and magazine articles. They are transmitted from person to person via e-mail, USB drives, and Github. Eventually, magazine articles are written, and then books, but only after the technology is established and "running". The Linux, Apache, and Perl projects followed this path.

So maybe we don't need an obvious advocate for a new technology. Perhaps the next programming revolution will be quieter, with technology seeping slowly into organizations and not being forced. That might be a good thing, with smaller shocks to existing projects and longer times to learn and adopt a new programming language.

Sunday, April 28, 2013

C++ without source (cpp) files

A thought experiment: can we have C++ programs without source files (that it, without .cpp files)?

The typical C++ program consists of header files (.h) and source files (.cpp). The header files provide definitions for classes, and the source files provide the definition of the implementations.

Yet the C++ language allows one to define function implementation in the header files. We typically see this only for short functions. To wit:

random_file.h

class random_class
{
private:
    int foo_;
public:
    random_class( int foo ) : foo_(foo);
    int foo( void ) { return foo_ };
}

This code defines a small class that contains a single value and has no methods. The sole member variable is initialized in the constructor.

Here's my idea: Using the concepts of functional programming (namely immutable variables that are initialized in the constructor), one can define a class as a constructor and a bunch of read-only accessors.

If we keep class size to a minimum, we can define all classes in header files. The constructors are simple, and the accessor functions simply return calculated values. There is no need for long methods.

(Yes, we could define long functions in headers, but that seems to be cheating. We allow short functions in headers and exile long functions into .cpp files.)

Such a design is, I think, possible, although perhaps impractical. It may be similar to the chemists' "perfect gas", an abstraction that is nice to conceive but unseen in the real world.

Yet a "perfect gas" of a class (perhaps a "perfect class") may be possible for some classes in a program. Those perfect classes would be small, with few member variables and only accessor functions. Its values would be immutable. The member variables may be objects of smaller classes (perhaps perfect classes) with immutable values of their own.

This may be a way to improve code quality. My experience shows that immutable objects are much easier to code, to use, and to debug. If we build simple immutable classes, then we can code them in header files and we can discard the source files.

Coding without source files -- no there is an idea for the future.

Sunday, October 7, 2012

How I fix old code: I use the wisdom of those before me

I am often called in to a project to improve the code -- to make it more efficient, or more maintainable.  It seems that some programmers write code that is difficult to understand. (And luckily for me, a large number of them.)

I use the maxims provided by Kernighan and Plauger in their book "The Elements of Programming Style". Written in the 1970s, it contains wisdom that can be used by programmers today.

One of my favorites (possibly because so many programmers do not follow it) is "Each function should do one thing well."

Actually, Kernighan and Plauger wrote "Each module should do one thing well"; I am taking a (reasonable, in my mind) liberty to focus on functions. With today's object-oriented programming languages, the meaning of "module" is somewhat vague. Does it refer to the class (data, member functions, and all?) or does it refer to a separate compiled file (which may contain a single class, multiple classes, a portion of a class, or all three)? But such questions are distractions.

When I write code, I strive to make each function simple and easy to understand. I try to build functions of limited size. When I find that I have written a long function, I re-factor it into a set of smaller functions.

But these techniques are not used by all programmers. I have encountered no small number of programs which contain unreadable code. Some programs have large functions. Some programs have poor names of variables and functions. Some programs have complicated interactions between functions and data, otherwise known as "shared mutable state". It is these programs that I can improve.

Many times, I find that the earlier programmers have done a lot of the work for me, by organizing the code into reasonable chunks that just happen to be grouped into a single function. This makes it easy for me to create smaller functions, each doing one thing well.

I do more than reduce functions to maintainable sizes. I also move functions to their proper class. I find many functions have been placed in improper classes. Moving them to the right class makes the code simpler.

How does one know the proper class? I use this rule: When the function modifies data, the class that holds the data is the class that should hold the function. (In other words, only class functions can modify class data. Functions in cannot modify data in other classes.)

Functions that do not modify data but only read data belong to the class that holds the data.

If a function reads data from two classes, it is most likely doing too much and should be re-factored. If a function is changing data in two classes, it is definitely doing too much, and should definitely be re-factored. (These rules are for direct access of data. I have no problem with functions that invoke methods on multiple objects to retrieve or modify their data.)

These two simple rules ("each function should do one thing well" and "each function in its proper class"), give me guidance for most of my improvements. They have served me well.

I succeed because I simplify code. I simplify code by using not my own rules, but the maxims laid out by those who came before me.

Wednesday, September 19, 2012

How to improve spreadsheets

Spreadsheets are the sharks of the IT world. They evolved before the IBM PC (the first spreadsheet ran on an Apple II) and have, for the most part, remain unchanged. They have migrated from the 6502 processor to the 8080, the Z-80, the 8086, and today's set. They have added graphs and fonts and extravagant file formats. They have grown from 256 rows to a rather large number. But they remain engines of data and formulas, with the data in well-defined grids and formulas with well-defined characteristics. They are sharks, evolved to a level of efficiency that has yet to be surpassed.

Spreadsheets get a number of things right:

- The syntax is easy to learn and consistent
- The data types are limited: numeric values, string values, and formulas
- Feedback for changes is immediate (no compiling or test scripts)
- Cells can only read values from other cells; they cannot assign a value to another cell

Immediate feedback is important. Teams using dynamic languages are at risk of a slew of problems; they use automated tests to identify errors. Agile processes emphasize the frequent use of tests. Spreadsheets provide this feedback without the need for tests.

The last item is most important. Cells can assign values to themselves, but they cannot assign values to other cells. This means that spreadsheets have no shared mutable state, no update collisions, no race conditions. That gives them stability.

Yet spreadsheets get a number of things wrong:

- All data is global
- Organization of data is dependent on the composer's skills and discipline

Our current spreadsheets are like the C language: fast, powerful, and dangerous. In C, one can do just about anything with the underlying machine. The C language lets you convert data from one form to another, and point to just about anything. It is quite powerful, but you have to know what you are doing.

Spreadsheets are not that dangerous. They don't have pointers, and the only things you can reference are (type safe because they are all of one type) cells within the spreadsheet.

But spreadsheets have the element of "you have to know what you are doing". The global nature of the data allows for formulas to refer to any cell (initialized or not) with little or no warning about nonsensical operations. In this sense, spreadsheet programming (in formulas) is much like C.

At first, I thought that the concepts of structured programming would improve spreadsheets. This is a false lead. Structured programming organizes code into sequences, iterations, and alternate paths. It clarifies code, but the formulas in spreadsheets are not a Turing-complete programming language. Structured programming can offer little to spreadsheets.

Instead, I think the concept of data encapsulation (from object-oriented programming) may help us advance the spreadsheet.

Spreadsheet authors tend to organize data into ranges. They may provide names for these ranges or leave them unnamed, but they will cluster their data into areas. Subsections of the grid will be used for specific types of data (for example, contact names in one range, regional sales in another).

For small spreadsheets, the "everything on one grid" concept works. Larger spreadsheets can see data split across pages (or tabs, depending on your spreadsheet manufacturer).

The problem with the spreadsheet grid is that it is, up to a point, infinite. We can add data to it without concern for the organization or structure. This becomes a problem over time; after a number of updates and revisions the effort to keep data organized becomes large.

An advanced spreadsheet would recognize that data is not stored in grids but in ranges, and would provide ranges as the key building block. Current spreadsheets let you define ranges, but the ability to operate on ranges is limited. In my new species of spreadsheet, the range would be the organizational unit, and ranges would not be infinite, empty grids. (They could expand, but only as a result of conscious action.)

Ranges are closer to tables in a database. Just as one can define a table and provide data for that table, one could define a range and provide data for that range. Unlike databases, ranges can be easily extended horizontally (more columns), re-sequenced, formatted, and edited. Unlike grids, ranges can be separated or re-combined to build new applications. Ranges must provide for local addresses (within the range) and external addresses (data within other ranges). Formulas must be able to read values from the current range and also from other grids.

If we do it right, ranges will be able to live in the cloud, being called in when needed and stored when not. Ranges will also be members of one application (or multiple applications), serving data to whatever application needs it.

Any improved spreadsheet will have to retain the advantages of the current tools. The immediacy of spreadsheets is a big advantage, allowing users of all skill levels to become proficient in a short time. Changing from grid-based spreadsheets to range-based spreadsheets must allow for this immediacy. This is a function of the UI, something that must be designed carefully.

I think that this new form of spreadsheet it possible, and offers some advantages. Now all I need is some time to implement it.

Friday, August 24, 2012

How I fix old code

Over the years (and multiple projects) I have developed techniques for improving object-oriented code. My techniques work for me (and the code that has been presented to me). here is what I do:

Start at the bottom Not the base classes, but the bottom-most classes. The classes that are used by other parts of the code, and have no dependencies. These classes can stand alone.

Work your way up After fixing the bottom classes, move up one level. Fix those classes. Repeat. Working up from the bottom is the only way I have found to be effective. One can have an idea of the final result, a vision of the finished product, but only by fixing the problems at the bottom can one achieve any meaningful results.

Identify class dependencies To start at the bottom, one must know the class dependencies. Not the class hierarchy, but the dependencies between classes. (Which classes use which other classes at run-time.) I use some custom Perl scripts to parse code and create a list of dependencies. The scripts are not perfect but they give me a good-enough picture. The classes with no dependencies are the bottom classes. Often they are utility classes that perform low-level operations. They are the place to start.

Create unit tests Tests are your friends! Unit tests for the bottom (stand-alone) classes are generally easy to create and maintain. Tests for higher-level classes are a little trickier, but possible with immutable lower-level classes.

Make objects immutable The Java String class (and the C# String class) showed us a new way of programming. I ignored it for a long time (too long, in my opinion). Immutable objects are unchangeable, and do not have the "classic" object-oriented functions for setting properties. Instead, they are fixed to their original value. When you want to change a property, the immutable object techniques dictate that instead of modifying an object you create a new object.

I start by making the lowest-level classes immutable, and then working my way up the "chain" of class dependencies.

Make member variables private Create accessor functions when necessary. I prefer to create "get" accessors only, but sometime it is necessary to create "set" accessors. I find that it easier to track and identify access with functions than with member variables, but that may be an effect of Visual Studio. Once the accessors are in place, I forget about the "get" accessors and look to remove the "set" accessors"

Create new constructors Constructors are your friends. They take a set of data and build an object. Create the ones that make sense for your application.

Fix existing constructors to be complete Sometimes people use constructors to partially construct objects, relying on the code to call "set" accessors later. Immutable object programming has none of that nonsense: when you construct an object you must provide everything. If you cannot provide everything, then you are not allowed to construct the object! No soup (or object) for you!

When possible, make member functions static Static functions have no access to member variables, so one must pass in all "ingredient" variables. This makes it clear which variables must be defined to call the function. Not all member functions can be static; make the functions called by constructors static when possible. (Really, put the effort into this task.) Calls to static functions can be re-sequenced at will, since they cannot have side effects on the object.

Static functions can also be moved from one class to another, at will. Or at least easier than member functions. It's a good attribute when re-arranging code.

Reduce class size Someone (I don't remember where) claimed that the optimum class size was 70 lines of code. I tend to agree with this size. Bottom classes can easily be expressed in 70 lines. (if not, they are probably composites of multiple elementary classes.) Higher-level classes can often be represented in 70 lines or less, sometimes more. (But never more than 150 lines.)

Reducing class size usually means increasing the number of classes. You code size may shrink somewhat (my experience shows a reduction of 40 to 60 percent) but it does not reduce to zero. Smaller classes often means more classes. I find that a system with more, smaller classes is easier to understand than one with fewer, large classes.

Name your classes well Naming is one of the great challenges of programming. Pick names carefully, and change names when it makes sense. (If your version control system resists changes to class names, get a new version control system. It is the servant, not you!)

Talk with other developers Discuss changes with other developers. Good developers can provide useful feedback and ideas. (Poor developers will waste your time, though.)

Discuss code with non-developers Our goal is to create code that can be read by non-developers who are experts in the subject matter. We want them to read our code, absorb it, and provide feedback. We want them to say "yes, that seems right" (or even better, "oh, there is a problem here with this calculation"). To achieve that level of understanding, we need to strip away all of the programming overhead: temporary variables, memory allocation, and sequence/iteration gunk. With immutable object programming, meaningful names, and modern constructs (in C++, that means BOOST) we can create high-level routines that are readable by non-programmers.

(Note that we are not asking the non-programmers to write code, merely to read it. That is enough.)

These techniques work for me (and the folks on my projects). Your mileage may vary.

Monday, August 22, 2011

Immutable Object Programming

I've been working with "Immutable Object Programming" and becoming more impressed with it.

Immutable Object Programming is object-oriented programming with objects that, once created, do not change. It is a technique used in functional programming, and I borrowed it as a transition from traditional object-oriented programming to functional programming.

Immutable Object Programming (IOP) enforces a discipline on the programmer, much like structured programming enforced a discipline on programmers. With IOP, one must assemble all components of an object prior to its creation. The approach of traditional object-oriented programming allows for objects to change state, and this is not possible with IOP. With IOP, you do not want an object to change state. Instead, you want a new object, often an object of a different type. Thus, when you have new information, you construct a new object from the old, adding the information and creating a new object of a similar but different type. (For example, a Sale object and a payment are used to construct a CompletedSale object.)

IOP yields programs that have lots of classes and the logic is mostly linear. The majority of statements are assignment statements -- often creating an object, and the logic for iteration and decisions are contained within the constructor code.

As a programmer, I have a good feeling about the programs I write using IOP techniques. It is a feeling of certainty, a feeling that the code is correct. It is a good feeling.

I experienced this feeling once before, when I learned structured programming techniques. At the time, my programs were muddled and difficult to follow. With structured programming techniques, my programs became understandable.

I have not had that feeling since. I did not experience it with object-oriented programming; OOP was difficult to learn and not clarifying.

You can use immutable object programming immediately; it requires no new compiler or language. It requires a certain level of discipline, and a willingness to change. I use it with the C# language; it works with any modern language. (For this conversation, C++ is omitted from the set of modern languages.) I started with the bottom layer of our objects, the ones that are self-contained. Once the "elementary" objects were made immutable, I moved up a layer to the next set of objects. Within a few weeks I was at the highest level of objects in our code.

Sunday, December 19, 2010

Are you ready for another step?

The history of programming languages has introduced concepts over time. Most of these concepts add rigor to our programming most of these changes were accompanied by complaints and much gnashing of teeth from the "old school" programmers. The big changes were high-level languages, structured programming, and object-oriented programming.

The step to high level languages was perhaps the most traumatic, since it was the first. It saw the deprecation of assembly language and fine control of the program in exchange for the ability to write programs with little concern for memory layout and register assignment.

The step to structured programming was also controversial. The programming police took away our GOTO statement, that workhorse of flow control and limited us to sequential statements, if/then/else blocks, and while loops.

When we moved from structured programming to object-oriented programming, we had to learn a whole new "paradigm" (and how to spell and pronounce the word "paradigm"). We learned to organize our code into classes, to encapsulate our data, to build a class hierarchy, and to polymorphize our programs.

Funny thing about each of these steps: they built on the previous steps. Structured programming assumed the existence of high level languages. There was no (noticeable) movement for structured assembly language. Object-oriented programming assumed the tenets of structured programming. There was no GOTO in object-oriented programming languages, except for the C++ "goto" keyword which was offered up on the altar of backwards compatibility and only then with restrictions.

And now we are about to move from object-oriented programming to functional programming. Once again, the "old school" programmers will gnash their teeth and complain. (Of course, it will be a different bunch than the teeth-gnashers of the golden age of assembly language. The modern teeth-gnashers will be those who advocated for object-oriented programming two decades ago.) And once again we will move to the new thing and accept the new paradigm of programming.

Yet those shops which attempt to move up to the step of functional programming will find a challenge.

Here's the problem:

Many shops, and I suspect most shops, use only a small fraction of object-oriented programming concepts in their code. They have not learned object-oriented programming.

Big shops (and medium-size shops, and small shops) have adopted object-oriented languages (C++, Java, C#) but not adopted the mindset of object-oriented programming. Much of the code is procedural code, sliced into classes and methods. It is structured code, but not really object-oriented code.

Most programmers are writing not clean, object-oriented code, but FORTRAN. The old saying "I can write FORTRAN in any language" is true because the object-oriented languages allowed for the procedural constructs. (Mind you, the code is FORTRAN-77 and not FORTRAN IV or FORTRAN II, but FORTRAN and procedural it is.)

This model breaks when we move to functional languages. I suspect that one can write object-oriented code (and not pure functional code) in functional languages, but you cannot write procedural code in functional languages, just as you cannot write non-structured code in object-oriented languages. The syntax does not allow for it.

The shops that move to functional languages will find that their programmers have a very hard transition. They have been writing procedural code, and that technique will no longer work.

What is an IT shop to do? My recommendations:

First, develop better skills at object-oriented programming. This requires two levels of learning: one for individuals, and the second for the organization. Individuals must learn to use the full range of object-oriented programs. Organizations must learn to encourage object-oriented programming and must actively discourage the older, structured programming techniques.

Second, start developing skills in functional programming. If using a "plain" object-oriented programming language such as C++, build discipline in techniques of functional programming. My favorite is what I call "constructor-oriented" programming, in which you use immutable objects. All member variables are set in the constructor and do not allow methods to change any values. This exercise gives you experience with some of the notions in functional programming.

The transition to functional languages will occur, just as the transition to object-oriented languages occurred. The question is, do you want your shop to move to functional programming on your schedule, or on someone else's schedule? For if you make no plans and take no action, it will occur as the external world dictates.