Arjen Markus (6 may 2009) While it is commonly said that Fortran (77/90/95) is not an object-oriented language and while it may be said that Fortran 2003 adds quite some support for object-orientation to the language, I would like to make some observations:
Many of these promises can be found in Fortran 90 already:
Fortran 90/95 does not allow inheritance, but the trend in OOP is towards aggregation instead of inheritance, so that should be no obstacle.
Furthermore, calling a method for an object like:
is syntactically different from:
call mymethod(obj, ....)
but not semantically.
Chris Brown (7 May, 2009)
For an interesting paper on Fortran from a OOP perspective see: Introduction to Object-Oriented Concepts using Fortran90 by V.K. Decyk, C.D. Norton and B.K. Szymanski
Jason Blevins (12 Jun 2009) You make some very good points Arjen—I agree completely. We need to be careful not to equate “OOP” with “OOP as C++ and Java have implemented it.” Much of OOP is about adopting a programming style or set of conventions, given a particular language syntax, that will achieve the right meaning and function. Whether or not the syntax is the same as C++ or Java is more about aesthetics (and I now realize that on the main page I may have focused too much on the syntax rather than semantics).
Ralph Johnson (26 July 2010) I want to agree with Arjen about there being many ways of defining OO languages, but I want to disagree about what is important. Inheritance is not essential for an OO language, what is important is “a way of organizing types of objects and extending old types to make new types”. Delegation and aggregation are alternatives to inheritance. However, if you look at how languages like Self are used, you will see that there is not really that much difference between inheritance and its alternatives.
On the other hand, polymorphism is essential for OO programming. In fact, OO languages require polymorphism by late-bound proc and so don’t need to be combined in any sophisticated way, or else theyedure calls. Polymorphism is the ability for a variable to refer to variables of many types, and there are many kinds of polymorphism. Some kinds can be resolved at compile time, such as is found in C++ templates. OO polymorphism can only be resolved at run time; when you call a procedure, it checks the classes of its arguments and selects the right procedure from a set of possibilities. In most OO languages, the procedure is selected based on the class of only one of its arguments. In these languages, procedures belong to (or are “members of”) a particular class. But in some languages, such as CLOS, procedures do not belong to particular classes, and the exact procedure to select can be based on checking the classes of more than one object.
The separation between interface and implementation is a key part of OO programming, but it is also a key part of programming with abstract data types. Some people seem to confuse the two, partly because there is a lot of similarity, and partly because people don’t understand the importance of polymorphism by late-binding of procedure calls. There is a good paper by William Cook from 2009 that explains the difference. Google “william cook abstract data type”. I tried to make a link to the paper but the spam filter didn’t like it.
Sean Santos (6 Jul 2012) I think that Ralph’s statement relates to one OOP feature that seems to me to be missing even in Fortran 200X, which is that there is no straightforward way to “compose” two types. Multiple inheritance (como C++) is the most stereotypical solution, but there are other options that are not based on inheritance.
In many practical cases, this is not a serious issue because the two types you want to combine are entirely unrelated and don’t really need to interact directly. (E.g. if I have a “label” type and a “point” type, and I want to create a “labeled point” type, I can create a type containing one label and one point and use these members to do the actual work, no inheritance required.) However, if you have two extensions to the same underlying base class, it is not so simple to create an object that uses both extensions.
Tangentially, I think it’s important to remember that syntax is key to how a language is actually used in practice. Fortran’s approach to types is both a blessing and a curse; there are some object-oriented (anti-)patterns that are very hard to optimize and that (I think) are much less likely to show up in Fortran than in a C-flavored language.
But when you talk about the difference between
call mymethod(obj, ....)
I think there are a few practical differences worth pointing out.
Firstly, the second style leads to longer “method” names, because even though you can use very generic names with a generic interface, it sometimes seems easier to disentangle the meaning of a line by giving procedures unique names (e.g. “obj_mymethod” or “modulename_mymethod” rather than just “mymethod”). Perhaps it may do more harm than good in the long run, but in the short term it helps you remember what or where the implementation is.
Secondly, there is a greater risk in the second case that unrelated code will be jumbled together in the actual implementation. Ideally, one would have an object and its methods bound together in a single module with nothing else. But I think this is a bit more likely to be violated if there’s no explicit association of mymethod to obj.
Thirdly, and this is probably the key to my other points, I think that Fortran is in some sense easier to use for many applications than other languages, while at the same time harder to write readable OO code in, simply because you often have to be more verbose to do it. So if you have a large project with a lot of contributors, with varying backgrounds, you have a lot more weird code coming in. And what I think it comes down to is what a “first iteration” of newly developed code often looks like:
particle_density = particle.avg_mass()/shape.volume();
layer.set_density(particle_density * shape.pack_factor(LOOSE_PACKING));
particle_density = part_avg_mass(particle)/shape_volume(shape)
call set_layer_density(layer,particle_density * get_shape_pack_factor(shape, loose_packing_idx))
Someone who finds the second block repetitious or arcane is probably more likely to stick with an imperative style in their own code, and introduce whatever implementation-specific assumptions they need to make it works:
volume = sphere_volume(dist_avg(particle%rad_dist, particle%rads))
particle_density = particle%mass/volume
layer%density = particle_density * sphere_loose_packing_factor
Of course, you can do better than either of these in Fortran, but I find that in practice, to someone who just wants to get something working that they understand, a familiar imperative style is more appealing than trying to clean up an OOP version. Which is not necessarily so bad, but a poor investment in the future of changing code.