Command design pattern in Fortran

I am playing around with object oriented Fortran features and I was interested in testing the command pattern, so I have put together an implementation based on the C# answer to this stackoverflow question.

My toy example is available on GitHub: GitHub - lucin81/command-pattern

While doing this I had a couple of observations/open questions which I wanted to share and happy to hear the opinion of people with more experience in Fortran, OOP or both.

  1. The Fortran version of the pattern that I came up with seems significantly longer and a bit verbose compared to the C# example or to other implementations I’ve seen around. Would there be a better fortran version? For example controlling the visibility attributes of the module, derived types, type members and type bound procedures is lengthy and repetitive, defining custom constructors, needing a wrapper type to store an array of objects that inherit from a base class, having to declare and implement the type bound procedures in two different places. Seems like all these things together contribute to having to type a lot longer.

  2. Not directly related to Fortran but my first impression was that I liked this pattern for a use case like a CLI like the one I am playing with and where there would be a few command types doing a lot of work. I can see this getting complex in presence of many different commands which might share part of the implementation and this could lead to repetitions in a naive implementation.

  3. I have been wondering what could be a more procedural way (or something that feels a bit more familiar to someone like me who doesn’t have so much experience with OOP in Fortran) to achieve similar results. Are there common procedural design patterns that allow separating execution of an operation from it’s definition like it is done here?

2 Likes

This is the kind of thing that makes for good practice and reference material. Thanks for sharing.

My critique after a quick perusal is that the factory type seems entirely superfluous. The only thing that even knows about it is the processor, so why could the factory logic not be internal to it?

Another critique, that is more due to the simplistic nature of the example that doesn’t really demonstrate the necessity of the pattern, is why once the desired command is determined can’t you just call a procedure directly? Of course there are more complex scenarios where the pattern is useful, but this example doesn’t encounter any of them. You’ve basically added unnecessary complexity into your solution just for the sake of following some pattern.

Of course this (I’m assuming) was meant more as an exercise than anything, so don’t take the critiques to harshly, but do be wary of times when a pattern may inadvertently introduce more complexity than it alleviates.

Unfortunately, it seems I’ve strayed from answering your actual questions, so I do apologize for that. Sadly, exploring a better solution (which is more what your questions were after) would take a bit more time than I have immediately available. Good luck and keep trying stuff.

I think yes, OOP in Fortran is more verbose than object-oriented languages, but at least Fortran can write object-oriented.
The essence of object-oriented is to transmit information among objects, and its mode of thinking is biased towards modeling, which is widely used in general programming.
Different from general programming, scientific calculations are often carried out by mathematical thinking. Functional programming seems to be the dominant and object-oriented as auxiliary. (see Comparison to Other Languages (fortran-lang.org))

Here (see farhanjk/FortranPatterns) is a more complete Fortran version of the design pattern code.

I’m also trying to practice Fortran’s 23 design patterns (including the command pattern) recently, according to Design Patterns (refactoring.guru). I’m not an object-oriented expert, just practice.
See zoziha/Fortran-Design-Patterns (github.com) and src/behavioral/command (github.com)

I think for any CLI the best-tested and most successful model is the shell. You can create shells that call other shells in parallel, asynchronously in the background, extend existing commands internally or by creating a wrapper that calls the existing commands in a novel way and so on. OOP in many ways is just trying to use a programming language to more efficiently do what shells have been doing for years.

So long before Fortran had recursion or OOP features or allocatable variables Fortran programs were written that parsed commands either from a READ or the command line and typically built a table. Since it was hard to allocate and deallocate usually a dictionary was maintained that used the subcommand name as a prefix and the keyword as the suffix and then stored a value, typically as a string in an old-fashioned form of a dictionary, often kept sorted for quick look-up with a binary search. Then, using the subcommand name a routine was called that then looked in the table for the values it needed. Sans looping and conditionals this very simple approach created basic shells with procedures treated much like built-in shell commands; and like a shell could call an external program as well. Things tended to get less portable with external commands that passed back information, as Fortran had no built-in method for doing that except for reading and writing scratch files. Socket connections were often used.

Updating that to today a byte code interpreter could be easily constructed with the post-f77 features now available in Fortran that would lend itself to creating a truly functional shell.

“Yes, Virginia – there really were Fortran shells” one might say. I had one myself that included being able to read and write self-describing vectors and tables that other programs could easily use, like an HDF5 precursor, had built-in graphics and allowed any keyword value on a command to be an expression and included function-based flow control commands by allowing any group of commands to be given a name which could be called directly as a new command or could be used as a name in IF and various looping expressions. Worked great. Still in use too. So that approach is possible using a procedural language (F77 did not give you a lot of choice about that), but it could definitely benefit from more modern features.

But the thing that holds up best to me is that nothing has come along better than a shell interface model for CLI in my experience, and I know from experience it can be done (with some difficulty) even with an old simple procedure language; but I would certainly recommend all the new features Fortran offers if doing that from scratch today. Just cracking a subcommand and options from the command line would work; but there are several resources on-line that discuss how to write interpreters that are amenable to being written in modern Fortran if someone is feeling ambitious. Since you use fpm note that it is written in Fortran and has a command line interface that sounds similiar to what you are describing.

PS:
I haven’t actually read it, but a quick search brought up Jumping Back and Forth · Crafting Interpreters which, although not Fortran-oriented looks really interesting,
and the LA code that lead to matlab was written in old-style Fortran and a derivative of that still exists in the fpm package M_matrix, although it really needs rewritten as it uses some techniques that were appropriate in F77 but are totally outdated today; but it is still inspiring and quite useful for inspecting data from a Fortran program when used as a subroutine.

One of the biggest reasons to use a shell-like interface that resembles commonly used shells is that users are often familar enough with the style that they can quickly start using the program. One of the main reasons to do an actual shell instead of cracking command lines is you can isolate yourself from shell expansion so you can use “magic” characters they way you want, so you can use () in math expressions and even use * for multiplication without having to quote and escape everything all the time.

1 Like

@ludnic ,

Re: your comment quoted above starting with “I have been wondering what could be a more procedural way …,” can you please explain how your OO-based approach differs from the following? I didn’t get too much time to look at your GitHub code.

   integer :: iArg
   character(len=256) :: Arg
   do iArg = 1, command_argument_count()
      call get_command_argument( iArg, Arg ) !<-- elided is error handling
      call execute_command_line( trim(Arg) ) !<-- elided is further command processing and error handling
   end do
end

C:\Temp>gfortran c.f90 -o c.exe

C:\Temp>c.exe “echo Hello World!”, “echo What’s up with all that OO?”
Hello World!,
What’s up with all that OO?

Thanks for your comments, very much appreciated!

Of course there are more complex scenarios where the pattern is useful, but this example doesn’t encounter any of them. You’ve basically added unnecessary complexity into your solution just for the sake of following some pattern .

Indeed I agree that mine is a perfect example of “cargo cult programming” (I didn’t know there was a definition for this) but my intent here is precisely to learn at which level of complexity (if any) this will become useful and justify the additional complexity as well as trying the Fortran implementation of a seemingly popular pattern. I will keep exploring it for a while to try and understand that.

My critique after a quick perusal is that the factory type seems entirely superfluous. The only thing that even knows about it is the processor, so why could the factory logic not be internal to it?

I think you are absolutely right. As above in this simple example it looks like lots of things used here are entirely superfluous. As I’m playing around and trying to understand better the pattern design, use and implementation, I’m thinking that the factory could be useful after all to register commands and construct the processor with and instance of the factory. Something like (not tested yet):

cf = command_factory_t(available_commands)
p = processor_t(cf)
call p%Process()

I’m thinking that this would allow a general way of composing workflows. One could then easily add or remove commands. And also create multiple processors that can work independently of each other. But again as you say the whole command_factory could be part of the processor probably and still allow exactly the same behaviour. So I’ll try it out and while I keep playing see if I miss that part.

Thanks for all your answers!

@urbanjost thanks for the explanation. It had never occurred to me the possibility of writing my own shell, so I looked it up and Tutorial - Write a Shell in C immediately came up. Which is an interesting reading. I’ll stick to the shell-like approach by now and will take a look at the fpm implementation.

Agree that the shell-like interface is the best model to handle a CLI, I am going to compare the parse/execute approach also as suggested by @FortranFan to make up my mind on whether the oop pattern is worth the effort and answer his question.

I’m not an OOP advocate, just a curious non-programmer, and I realise the CLI might not be the best example to demonstrate that specific pattern (it was just an interesting one to me).

@FortranFan the main advantage of that approach should be that the processor and the command logic are completely independent. The Execute procedure of a command does not necessarily need to be a shell command, even tho in my example it just prints a line the general case might be more complicated. However, in line with the discussion above you could swap call execute_command_line() with an execute procedure whose implementation depends on the value of Arg.