Free Style Guide

Hi guys,

I’m offering a FREE Fortran Style Guide.

It’s chock full of stuff, so check it out.

Check it out:
https://everythingfunctional.mykajabi.com/pl/210756

3 Likes

The link isn’t accessible

So it isn’t. I’ve got it published as an open source project now.

The project is here: Brad Richardson / Fortran Style Guide · GitLab

And you can download a pdf copy from here: Releases · Brad Richardson / Fortran Style Guide · GitLab

2 Likes

I like most of this - it seems sensible and will definitely help improve code quality. My comments:

  1. there is no naming style for submodules

  2. there is no reference to block statements

  3. I was expecting a recommendation that constructs (if, do, select) should be labelled

  4. Whilst I never use it I think that forbidding the use of the legal language feature goto in a style guide is wrong. The Guide should limit proscription to gotos that transfer control backwards.

  5. Fortran is a very verbose language and requiring all variables to be declared on a separate line is unnecessarily onerous. I think that dummy variables (e.g. array indices) should be declarable on the same line.

  6. I was expecting a recommendation of implicit none

  7. I don’t agree that one should code around compiler bugs. If a warning is erroneous it should be noted and handled by the build system.

  8. I think that all procedure parameters should have their intent specified. For example marking an allocatable array as intent(out) makes it clear that it’ll be deallocated on entry.

Thanks.

3 Likes

Besides what @simong said, there has also been a lot of disagreement about naming conventions. I personally do not like indenting the bodies of functions / subroutines, and so on. I plan to implement the major styling guides as an automatic formatter for LFortran, so that people can easily reformat the code the way they like.

1 Like

Besides what @simong said, there has also been a lot of disagreement about naming conventions. I personally do not like indenting the bodies of functions / subroutines, and so on.

I agree, because unneeded indentation means that you will more continuation lines, which are less readable. I only indent constructs such as if blocks, do loops, and select case blocks.

I disagree with the OP about comments. It’s good to explain in English what a procedure does while you are coding it and it is fresh in your mind.

A book-length style guide is Modern Fortran: Style and Usage (2011), by Clerman and Spector.

1 Like

I understand that style guides are often contentious. I certainly didn’t mean to insist that everyone adopt this one and follow it dogmatically. Feel free to adopt what you like and avoid anything you strongly disagree with. I’m not trying to start any flame wars.

I will repeat one of my favorite quotes about comments (I wish I could remember who to attribute it to):

If the developer was unable to express themselves in code, what makes you so sure they will be able to properly express themselves in the comments

As I mentioned in the style guide, I do think comments that explain why are helpful, but the what and how should be apparent from the code (or at least that’s the goal).

I do appreciate @simong pointing out some things I missed and I will endeavor to add them (when I can get around to it).

I will respond to some points directly however.

  1. I rarely use them, but I would just go with *_s
  2. Again, I rarely use them. Do you have something you would recommend for them other than indent their contents?
  3. If you need to label your constructs, they may be nested too deep, but I could be convinced that they are useful “comments” (i.e. give some insight as to their purpose) in some cases
  4. Whilst goto statements specifically have not been marked obsolescent, some of its variants have, and experience in computing generally have concluded its use is generally problematic (and I’m really deferring to JPL and the automotive industries to make this claim)
  5. If you have sufficient variables declared that one per line becomes too verbose, it may be a sign that you should break your procedures into smaller, more focused ones, but I could be convinced that loop indices could go on one declaration
  6. I can’t believe I missed actually stating that. Maybe I just assumed everybody already new that :man_shrugging:
  7. If you have confirmed that it is legitimately a compiler bug (i.e. there is a bug report submitted to that compiler vendor about it), then I can agree with you here
  8. Agreed

And again, thank you all for the feedback.

1 Like

I have used block constructs but don’t have enough experience with them yet to make recommendations in a style guide. I mentioned them because they can be used to make variables more local which is the stated aim of section 5.2.

I agree regarding local variables, but I often declare each procedure argument on a separate line. When arguments are arrays and there are constraints on their shapes, I document those constraints. Those constraints should then be checked. For example, a subroutine that processes a matrix of stock prices can look like

subroutine foo(dates,symbols,prices)
type(date_mdy)   , intent(in) :: dates(:)    ! (ndays)
character (len=*), intent(in) :: symbols(:)  ! (nstocks)
real             , intent(in) :: prices(:,:) ! (ndays,nstocks)
integer                       :: ndays,nstocks
ndays = size(dates)
nstocks = size(symbols)
call assert_equal(ndays,size(stocks,1))
call assert_equal(nstocks,size(stocks,2))
...
end subroutine foo

(When arrays with related sizes are passed to many procedures, perhaps they should be stored in a derived type. I have done this.)

I find it more readable when INTENTs and :: are in the same columns, but this requires some manual fiddling when a new argument with a long name is added. I may write a script to align declarations as above.

The book by Clerman and Spector I previously mentioned was reviewed in Computing in Science and Engineering.

My style for this code at the moment is

subroutine foo(dates,symbols,prices)
    type(date_mdy), intent(in)    :: dates(:)        ! (ndays)
    character (len=*), intent(in) :: symbols(:)      ! (nstocks)
    real, intent(in)              :: prices(:,:)     ! (ndays,nstocks)
    integer                       :: ndays, nstocks
    ndays = size(dates)
    nstocks = size(symbols)
    call assert_equal(ndays, size(stocks,1))
    call assert_equal(nstocks, size(stocks,2))
    ...
end subroutine foo

but I’ll think about aligning the intents in the furure.

I usually use named arguments for optional arguments but not for required arguments (which typically are at the beginning of the argument list). For subroutines with many arguments, I often write a comment stating which arguments are results (changed by the subroutine). It would be convenient if you could write, as in Python,

d,e = foo(a,b,c)

instead of

call foo(a,b,c,d,e) ! results in d,e

although as a workaround you can write a function that returns an array or derived type.

I don’t use spaces around % because, for example, point%x and point%y are single entities, and if you were to write a simple parser for your code, it would be convenient if an entity did not have spaces within its name. In languages where . is the separator, I don’t think programmers typically use spaces around the dot.

2 Likes

I have always thought that there was one and only one indentation, given by the Almighty to Mr. Emacs, which I have always followed to a T, with the prayer “CTRL+ALT+Q”. I recently came into contact with a heretical person who informed me that I should indent with four spaces, instead of three, because python. But I did not listen to him, thus saving my soul.

6 Likes

:joy: :joy:

An inside joke with a team I had worked with went “indent with four spaces is unhealthy for you” … the rest of it is unprintable here!

1 Like

I am pretty fussy when it comes to alignment, because it helps me a lot parsing the code. If I do not have lines to guide the eye, I have to read each line, and it is like driving on a rocky path.

subroutine foo( dates, symbols, prices )
   type(date_mdy)  , intent(in) :: dates(:)    ! (ndays)
   character(len=*), intent(in) :: symbols(:)  ! (nstocks)
   real            , intent(in) :: prices(:,:) ! (ndays,nstocks)
   integer                      :: ndays,nstocks

   ndays   = size( dates )
   nstocks = size( symbols )
   call assert_equal( ndays  , size( stocks, 1 ) )
   call assert_equal( nstocks, size( stocks, 2 ) )
   ...
end subroutine foo

Sometime I like the tower, particularly when using open:

open(newunit = uid       , &
     file    = FileName  , &
     form    ="formatted", &
     status  ="unknown"  , &
     action  ="write"    , &
     iostat  = iostat    , &
     iomsg   = iomsg     )
call assert(iostat==0,iomsg)
...
close(uid)

or when reading command-line arguments using a separate routine,

call GetCommandLineArguments( &
   nIterations, &
   OutputDir, &
   EnergyMin, &
   EnergyMax, &
   nEnergies, &
   ...
   Verbose )

This style is convenient here, because the command-line parameters are subject to frequent changes at the beginning (number/name/order), and when there is one per line it is very easy to swap them.

3 Likes