Feedback for fpm based on teaching my son

I started teaching my oldest 8 years old son programming. So we naturally used Fortran. Here is some feedback that I noticed while doing that. We did three sessions so far.

First one a few months ago in Python on Linux, Gedit as an editor, using python a.py in a terminal to run. It works, but not having the compilation step actually kind of hurts, it’s good to have the compiler check the syntax and semantics and provide good feedback. The Python runtime errors are not always the most helpful for a complete beginner. But it does the job.

Second one on Linux, using LFortran, VSCode with the built-in terminal, where we always recompiled and ran by hand. I had to turn off my Vim bindings for VSCode so that he can actually use it.

The third session we did on a macOS, I decided to use fpm, which doesn’t work with LFortran yet, so I installed GFortran using Conda, had to activate the environment. We used the default editor (TextEdit) on macOS when you type open some_file.f90 in a terminal. It was auto-correcting to upper case at the beginning of the “sentence”, so this was not a good experience. Also we had to save every time before switching to the terminal to fpm run. (I like to setup my Neovim in a terminal and VSCode to automatically save the file when losing focus, so I just switch to the terminal and it gets saved.)

Feedback.

Setup

I spent about 20 minutes installing Conda, first I installed conda install fpm gfortran into an environment, and linked them into $HOME/bin which I put into $PATH. fpm works, but gfortran failed to link when invoked that way… So I removed the $HOME/bin and installed fpm and gfortran into the base Conda environment and loaded it by default when a new terminal starts. That works.

Suggestion: document on fortran-lang.org how to get started on each platform using Conda in 5 minutes. Conda is nice because it does not require root access and installs locally, in a non-invasive manner. And the usage from the user perspective is almost identical on all platforms. (#280)

Editor

Suggestion It would be nice to settle on the same editor on all platforms, probably VSCode and document how to autosave, as that is really nice and helpful, as my son forgets to save before recompiling.

fpm

fpm works great, but here are a few suggestions:

  • Hide the compilation commands, unless requested with fpm -v, and instead just show the file being compiled and a progress bar. In colors. Nice and clean. Could even show the Fortran module dependency graph and highlight in color where in the dependency graph we are.
  • When an error happens, it shows a stacktrace, but on macOS it just shows hashes, no lines. fpm should be compiled with -g and ensure it shows a nice stacktrace on macOS. The same when an error happens in the compiled program.

fpm is a real life saver — everything just works and it hides the details. Previously I didn’t want to go into explaining CMake, so we compiled using lfortran a.f90 && ./a.out, but it’s just not as nice and it does not really scale well once we use more than one module.

Fortran

I didn’t even bother trying to explain why implicit none has to be in every file (we only used one file, the main program so far). The good news is that my son didn’t seem to be bothered by this “noise”.

Suggestion: perhaps make implicit none by default using a compiler option in fpm, so that users don’t have to type it? I think that should work, as if fpm exports cmake files, it would include the option, and if somebody uses the file in another project by copying it, it would still work because all variables would be declared.

Otherwise Fortran was very easy to teach, we used exponentiation, +, *, do loop, print * and read (*,*). Integer variables. The compiler helps — my son tried to assign to a loop variable and GFortran correctly did not allow it.

Compilers

LFortran works fine for this, but the error messages are not the best yet. But we will fix it.

GFortran has decent error messages, but it has some stuff that should be improved, for example when a variable is not declared, it says:

$ gfortran expr2.f90
expr2.f90:6:15:

    6 | x = (2+3)*5 + z
      |               1
Error: Symbol ‘z’ at (1) has no IMPLICIT type

I usually don’t read the messages when developing, I know right away that it was not declared. But with my son I actually read it. This is completely cryptic – I know what it means (the variable is not declared, and there is no implicit type). But good luck explaining this. I don’t want an “implicit type”. I want this to be declared. Suggestion: It should say: Variable or symbol z at (1) is not declared.

Summary

Everything works and we will continue. I explained that there is a source code, the compiler checks the code and produces a machine code. We looked at the executable using Vim, I explained that these are machine instructions that the CPU executes, we found the string that we printed in the machine code, etc. I explained that fpm run just runs this compiled executable. He got all that at this high level, it’s not complicated.

There is no problem using the terminal, he got that really quickly. We used the following commands: cd, ls, cat, fpm new and fpm run.

Is anyone else teaching their kids? I’d be interested in hearing your experience.

10 Likes

Thanks for sharing. No experience with teaching kids yet. But is there a way to set implicit none via compiler options? The Fortran default behavior is likely not going to change given the massive body of existing legacy code, but default compiler options would be a good alternative if all compilers have it. GFortran installation via Homebrew is also quite straightforward on macOS.

I personally just made the decision to not use “implicit none”. Here is a non-comprehensive list of flags (for the compilers I use).
gfortran: -fimplicit-none
ifort: -implicitnone
nvfortran: -Mdclchk

I also made the decision to not worry about line length, which you can disable with:
gfortran: -ffree-line-length-none

And one other little thing I’ll add that I use to fix the terminal width is this, I spent a long time being frustrated before finally finding out what was going on with the differences between the compilers’ printing behavior:

subroutine stdout_init()
   integer :: stdout_recl
   inquire(unit=6, recl=stdout_recl)
   if(stdout_recl < 4096) then
      if(stdout_recl .ne. 0) then
         close(unit=6)
      endif
      open(unit=6, recl=4096)
   endif
endsubroutine

But I also have to use this with it:

#define stdout if(this_image()==1)write(6,*)
3 Likes

Very useful; thanks for sharing. NVIDIA’s flag looks like an unnatural choice, though. Also, interesting workaournd for compiler printing differences.

1 Like

I feel like there are some more things along these lines that the fpm authors could think about or perhaps LFortran could just make them easier. For example, I have this for using single or multiple images.

#ifndef PARALLEL
public this_image
public num_images
public co_sum
public co_broadcast

interface this_image
   module procedure single_this_image
endinterface

interface num_images
   module procedure single_num_images
endinterface

interface co_sum
   module procedure single_co_sum_real_0d
   module procedure single_co_sum_real_1d
   module procedure single_co_sum_real_2d
   module procedure single_co_sum_real_3d
   module procedure single_co_sum_real_4d
endinterface

interface co_broadcast
   module procedure single_co_broadcast_real_0d
   module procedure single_co_broadcast_real_1d
   module procedure single_co_broadcast_real_2d
   module procedure single_co_broadcast_real_3d
   module procedure single_co_broadcast_real_4d
   module procedure single_co_broadcast_logical
endinterface
#endif

And unfortunately, you have to do something like this:

#ifdef PARALLEL
subroutine single_sync_all
  !! Subroutine that allows intrinsic sync all to be used in the single image case
   sync all
endsubroutine
#else
pure_def subroutine single_sync_all
   !! Subroutine that allows intrinsic sync all to be used in the single image case
endsubroutine
#endif

And then I have this as well for using pure subroutines:

pure_def subroutine error_stop(msg)
   character(len=*), intent(in) :: msg
#ifdef DEBUG
   print *, 'Error: '//msg
   stop
#else
   if(len(msg) == 0) then; endif
#endif
endsubroutine

The point of all that is just that I like to be able to turn on and off multiple images as well as pure for printing.

I know at least Intel ifort can generate optimized serial binary from Coarray-enabled Fortran code. I think the flag is -coarray=single. That would likely obviate the need for some of the workarounds you have in your comment in the above. That said, I personally prefer to separate serial vs. parallel implementation by preprocessor directives, so the default build would always generate serial code, without the need for such compiler flags as -coarray=single.

I’ve been trying to handle gfortran, caf, ifort, and nvfortran. I understand what you mean, I’ve been just using this_image, co_sum, and co_broadcast to implement some algorithms that just average values on images, it’s incredibly easy and works but maybe that is not useful generally. If all of the compilers had -coarray=single it would be great. And then for the other situation, there would need to be some sort of flag that removes stop and stdout from pure subroutines.

After looking back at what is essentially a random utilities file of code, I see that I also have code for creating and printing arrays. I’m sorry if it’s off topic, I’m just listing what I consider to be “noise” that is easily dealt with but could probably have been dealt with by a tool, creating and printing arrays would be in a library. It really boils down to just implementing some of the most basic Numpy functionality which I’m betting almost everyone has done.

The idea to make implicit none default through a compiler flag is part of a more comprehensive proposal made some time ago to set some modern settings in fpm. (Yes, disclaimer, I am the proponent :grin: .)

If you agree, please consider supporting that proposal, commenting and adding your views in the relevant issue. With the nice GSOC work on fpm profiles this could be the right moment to implement them.

2 Likes

So would people prefer to create their own profiles, or have a new flag or profile perhaps called “best”, or have something like a compiler name ending in a plus trigger turning on “best practices” or …? Making some of these options the default would hinder quick migration of existing code; but perhaps they would be the defaults with a switch to turn them off? Deciding on “best practices” is somewhat arbitrary; but I would like to see files ending in .f treated as free format; a default implicit none and no line length, which are all available via switches on most compilers that are at least f2003. Some people might want automatic interfaces on where available; …

1 Like

I would find it appealing if there were an fpm package easily donated to that had example codes for different features; part tutorials and part “Rosetta-code-like”. If if were organized into categories like “beginner”, “parallel programming”, “functional”, “object oriented” so a new user could find an appropriate starting point that might keep it better focused. So far it is much easier for someone to add something to the Fortran Wiki or Rosetta-code or some of the other sites with small examples. It should be as easy to donate to as those.

2 Likes

I agree and that is what I have in mind, but until the work on fpm profiles and compiler flags reaches some slightly more advanced state, it’s difficult to be more specific, I think.

I don’t think that would be the case — for example enforcing implicit none would only break codes that use implicit typing and do not use implicit double precision ..., which is just a very minority of codes, and in those you just put in a simple option (one line) into fpm.toml to turn the default implicit none feature off.

I agree. We don’t need to wait for GSoC. @epagone, why don’t you go ahead and implement the implicit none flag? Send a PR to fpm and tag me in it. Let’s take it from there. I’ll help you if you get stuck. I don’t like waiting for others to do the work, as then it never gets done. :slight_smile: We are meeting tomorrow with @lkedward, @kubajj, see here: Handling compiler arguments in fpm project - Blog by Jakub Jelinek - #20 by lkedward, why don’t you join us @epagone? We’ll brainstorm how to implement it, and then you can implement it, and @kubajj can then integrate it into the bigger picture. But from the user point of view, we don’t care how it is implemented, as long as it is done one way or another. That way we get things moving, which is the most important part, to make steady progress.

3 Likes

In addition to gfortran for a Mac, you might want to try ifort from the oneAPI distro from Intel. It’s free to use and is easier to install.

Regarding IMPLICIT NONE, the Cray compiler also has an option to treat the code “as if” IMPLICIT NONE had been inserted in all the program units. The option is -eI (upper case i). With the man page entry

Treat all variables as if an IMPLICIT NONE statement had been specified. Do not override any IMPLICIT statements or explicit type statements. All variables must be typed.

2 Likes

I don’t want to speak for him, but I think @urbanjost was referring to using .f for free form (that is part of the “package” of modern flags that I was proposing) in that particular part of his message. However, as mentioned, with a simple mechanism to revert these “modern” flags (e.g. a switch) it is possible to, at least, alleviate the problem.

If this is the way I came across with my messages, I am sorry. I think that it is quite obvious from my previous messages, but let me re-iterate that this is absolutely not my intention. Simply, I believe that setting the default flags for fpm sits on top of the more generic problem of handling compiler flags and profiles that need to be sorted out first. If this is not the case, it is probably due to my poor understanding of the fpm implementation.

It is true that I might not have the time or skills (or combination of the two) to implement what I am proposing but this is yet to be seen and (obviously) I do not expect anyone to do the work just because I am throwing an idea in.

On a more pragmatic note, I am happy to join the call tomorrow and we’ll take it from there. Let’s see if I can support @kubajj in some way.

2 Likes

I also can’t think of any reason not to use the preprocessor, I haven’t had any problems with the modern PC compilers having used it for a while.
gfortran: -cpp
ifort: -fpp
nvfortran: -cpp

1 Like

I think the only reason I don’t use .f and use .f90 is that the VSCode tools had this setup.

Good points @implicitall .

I guess the problems here are

  1. there are cases when the pre-processor is not needed (and setting it on by default might be too much)
  2. there is more than one pre-processor available and we might struggle to select the “chosen one” and set it by default.

This issue has been identified and discussed here. It can be solved amending the configuration files of the most popular editors and (I think) it would help making them available together with fpm. For sure this could create some temporary confusion but I am personally not overly concerned.

I agree, but if you want to encourage consistency in the file extensions, then turning on the preprocessor would be better than using .F or .F90. As far as a default, the basic flags I listed seem fine to do everything I’ve needed.

1 Like

Thinking about it, it can work: if the pre-preprocessor is not used, nothing bad will happen (I guess? I haven’t used it much). If another pre-processor is desired, the default flags can be switched off. Appending the issue on GitHub.

Very nice feedback, thank you for writing it up.

I opened the issue in #280.

I agree this would be an improvement, I opened #502.

Definitely, we have an issue for this in #359.

1 Like