1

Here's a simple Fortran95 code that GNU Fortran (gfortran) compiles very well:

    Module Module1
        Implicit None

        Type MyType
            Real(Kind=8) :: x, y
            Integer :: n
        End Type

        Contains

        Real(Kind=8) Function Calc(self)
            Type(MyType) :: self
            Calc = self%x**self%n + self%y**self%n
        End Function
    End Module
    
    Program Main
        Use Module1

        Implicit None

        Type(MyType) :: A
        
        A = MyType(3.0, 4.0, 2)

        Write(*,*) Calc(A)
    End Program Main

In this case, an object A of the derived type MyType is created and initialized inside Main. Calc() is called and its result printed. Now comes a slightly different code:

    Module Module1
        Implicit None

        Type MyType
            Real(Kind=8) :: x, y
            Integer :: n
        End Type

        Type(MyType) :: A

        Contains

        Real(Kind=8) Function Calc(self)
            Type(MyType) :: self
            Calc = self%x**self%n + self%y**self%n
        End Function
    End Module
    
    Program Main
        Use Module1

        Implicit None

        A = MyType(3.0, 4.0, 2)

        Write(*,*) Calc(A)
    End Program Main

Same number of lines, same result. But here A was assigned to the derived type MyType within Module1, and "travels" with it anytime this module is imported/used (Use) by any other module and therefore A can be used within this latter module (provided A had been previously initialized, of course). Works similarly to a global variable, but not quite; modules that don't USE Module1 cannot access A.

The question is: is there any conceptual problem with this programming "style"? One must, of course, be careful with whatever is done with A in other modules, but this applies to other variables as well.

I can see a few advantages in the use of these "travelling" objects, but I may be missing something.

Carlos Gouveia
  • 247
  • 2
  • 8
  • 1
    Please use tag [tag:fortran] for all Foryran questions. – Vladimir F Героям слава Apr 20 '22 at 21:43
  • 2
    Be aware that true OOP comes with Fortran 2003. Regarding the global variable - it is really matter of choice for the particular situation. Also, using `kind=8` is really ugly and not portable. See https://stackoverflow.com/questions/838310/fortran-90-kind-parameter If a named constant is too long to type, just drop the `kind=`. – Vladimir F Героям слава Apr 20 '22 at 21:52
  • `A` as a module variable isn't always accessible when its module is used: `use module1, only : ; end`. – francescalus Apr 20 '22 at 21:59
  • @VladimirFГероямслава I think gfortran is compliant with Fortran 2003. I used an old Fortran9x programming style. – Carlos Gouveia Apr 21 '22 at 00:14
  • Quite what advantages do you see? Please edit the question to tell us. Personally I can see no compelling reasons, and care much more about the issues global variables bring. – Ian Bush Apr 21 '22 at 06:24
  • @IanBush For example, to avoid overloading subroutine calls with several objects, as long as one's program grows complex -- the typical "advantage" of a "global" (sigh) variable. But, giving it a second thought, maybe this is not a good idea after all. This is the very point of my question: I see advantages, but are those advantages real? Aren't they bad programming habits or style instead? I'm afraid I haven't found an answer yet. – Carlos Gouveia Apr 22 '22 at 00:04

1 Answers1

2

In your first example, A is a local variable, but in your second example, A is a global variable. There are many reasons why global variables are often a bad idea, explained in depth in various places around Stack Overflow 1 2 3 and Stack Exchange 4.

To give a concrete example, consider the code:

module module2
  use module1
  implicit none
contains
  subroutine foo()
    A = MyType(1.0, 2.0, 3)
    write(*,*) calc(A)
  end subroutine
end module

module module3
  use module2
  implicit none
contains
  subroutine bar()
    A = MyType(2.0, 3.0, 3)
    call foo()
    write(*,*) calc(A)
  end subroutine
end module

program main
  use module3
  implicit none

  call bar()
end program main

Rather than printing

9.0
35.0

as might be expected, this instead prints

9.0
9.0

Since any change to A is necessarily a side effect, these kinds of problems are likely to be very hard to avoid. Note also that module3 does not explicitly use module1, but rather implicitly uses module1 via use module2.

veryreverie
  • 2,871
  • 2
  • 13
  • 26
  • In what way is the module variable `A` a _global_ variable? – francescalus Apr 20 '22 at 23:55
  • @veryreverie It's not a global variable in a strict sense, since the modules that do not use/import Module1 cannot see/access ```A```. I have long learnt that global variables are an awful idea, but this case is a bit different and I can see a few advantages. Obviously, one must be careful when using this concept, but care must be exercised everywhere when one is writing a code. – Carlos Gouveia Apr 21 '22 at 00:09
  • 1
    @francescalus to quote wikipedia, "a global variable is a variable with global scope, meaning that it is visible (hence accessible) throughout the program, unless shadowed. [...] In compiled languages, global variables are generally static variables, whose extent (lifetime) is the entire runtime of the program" this seems as good a definition as any. So `A` in the second example is global in that it is outside any and all local scopes, and its lifetime is that of the program. It has access restrictions, but I'd argue that doesn't stop it counting as a global variable. – veryreverie Apr 21 '22 at 07:16
  • @CarlosGouveia I tend to read "one must be careful" as "unit tests must be written", but problems involving global variables will often evade any unit tests written to catch them. – veryreverie Apr 21 '22 at 07:39
  • 1
    @veryreverie You might also consider linking to https://softwareengineering.stackexchange.com/questions/148108/why-is-global-state-so-evil – Ian Bush Apr 21 '22 at 08:01
  • I see your point and I'm happy with your answer. Thanks. Yet my example was very crude. It is indeed easy to mess up with variables having simple names like ```A``` or _x_ or _y_. In a real application those objects would be something like _circle_ or _square_ (just to come up with something), variable names with a more pungent persona, so to speak, and therefore harder to be mistaken or misused. But then again, care is always essential - no one needs to use OOP concepts to screw it all up. – Carlos Gouveia Apr 22 '22 at 01:06