Naming convention for Fortran wrappers packages

Recently we had two discussions about how to name a (GitHub) repository that contains Fortran wrappers to a main package written typically in C++:

Many alternatives have been proposed. I encourage you to read both issues.

In the two threads we already gathered a lot of points which are important to consider, but those will be buried at the respective issue trackers once we mark them as resolved, so @awvwgk and I decided to move the discussion from the above linked issues to more visible place to also gather wider feedback.


My main motivation is that we should adopt a consistent naming scheme, as a community. I personally like the Julia’s approach to simply append .jl. And so I like package.f, and @awvwgk likes package-f.

Note that we started with symengine.f90, but as suggested by an outside contributor, symengine.f is more natural. Consistent with our slow move towards using just f instead of f90 as discussed elsewhere. As a maintainer of symengine in C++, I really like to just append something to each wrappers, it makes it easier to maintain. I think symengine.f and symengine-f would both work. As an fpm user, I would like to just add symengine to fpm.toml. However, in Conda, we have both symengine and python-symengine packages. So I don’t know what the best way is, and whether the package name must be equal to the repository name.

We can later open up a poll to perhaps vote, but for now I was hoping to just discuss this.

  • Should we adopt a naming scheme?
  • Which one?
2 Likes

I like the julia way: Internally (i.e. in fpm), one just uses the name symengine but for the outer world (e.g. on GitHub) the package would be called symengine.f or symengine-f.

1 Like

My main concern with using <name> for the project and <name>.f for repository, is that we will arrive at clashes.

Since Fortran shares its space in the installation prefix with C and C++ libraries we have to play nicely together with those. A Fortran wrapper for a lib<name>.so / lib<name>.a might end up in the same library directory as said library, I don’t see how this can work in practice. Even if we would put the libraries in different directories, this might cause issues for dynamic linking.

I don’t want to exclude the possibility of packaging a project or creating shared libraries by the choice of the project name.

2 Likes

The vast majority of Fortran code with the .f suffix uses fixed source form, so my default assumption is that foo.f is FORTRAN 77 (or earlier) code. Also, I use Windows CMD commands such as

dir /s *.f      # list fixed format source files
dir /s *.f90    # list free format source files
dir /s *.f*     # list fixed or free format source files

and would prefer that .f not be in a directory name. Thus I’d prefer foo-f to foo.f.

2 Likes

That is precisely the reason I used symengine.f90. But see the issues above, and the general trend to use .f for free form also.

2 Likes

I would vote for *-f. There is some precedent (i.e. toml-f), and . in a repository or directory name seems pretty rare. I also think that should be the name of the package, for reasons @awvwgk already mentioned, and because I find it a bit odd when a package’s name differs from the repository name.

2 Likes

Maybe blah-fortran? (wordier but more explicit).

Even in this day and age, I still encounter problems sometimes with ‘.’ in paths, so I would vote not to risk that.

.f90 was always a terrible extension, so I hope we don’t double down on that! That should die with implicit typing and implicit save. :slight_smile:

1 Like

Let’s separate the repository naming convention (whether -f or .f) and how the package is actually being used. Let’s also not worry about any potential current limitations of fpm.

Let’s start with how the package is actually being used.

Let’s look at Julia. In there, the packages are names such as Polynomials.jl (Manual · Polynomials.jl) and then used as:

Pkg.add("Polynomials")

In other words, inside the Julia environment, you do not append .jl, you just use Polynomials.

Now let’s look at fpm.toml of the fpm package:

[dependencies]
[dependencies.toml-f]
git = "https://github.com/toml-f/toml-f"
rev = "2f5eaba864ff630ba0c3791126a3f811b6e437f3"

Currently this is a temporary solution, since we do not yet have a package registry. So let’s look at Cargo to get an idea how this will look in the future, say, Cargo.toml of the bat package:

[dependencies]
atty = { version = "0.2.14", optional = true }
ansi_term = "^0.12.1"
ansi_colours = "^1.0"
console = "0.14.1"
...

As you can see, there is no .rs or -rs appended. So in the future, the fpm.toml will look like:

[dependencies]
toml-f = "0.14.1"

And I do not like appending -f there, which would be eventually for all packages.

I know @everythingfunctional mentioned that there is already a precedent, but this is actually not a good precedent.

This should look like just as this:

[dependencies]
toml = "0.14.1"

The same with symengine, it should just look like:

[dependencies]
symengine = "0.7.0"

and it just means the Fortran version of symengine, since we are in Fortran, so you can’t use any other version, for example it cannot be the Julia version of symengine (symengine.jl). The default for us is Fortran, so we should not use any of the following:

[dependencies]
symengine.f = "0.7.0"
symengine-f = "0.7.0"
symengine-fortran = "0.7.0"

However, there is one issue to resolve. How do we write fpm.toml for the symengine.f package itself? In there, it needs to depend on the C++ version of symengine. There are two ways forward:

  • Either we simply build the C++ package as part of symengine.f (by supplying a build script that fpm will support in the future). In that case, the Fortran package can and should just be called symengine, as there is no other package

  • Or we first create a symengine.cpp package, which contains the original C++ version (no wrappers). Then in symengine.f we depend on the symengine.cpp package.

If the second point is used, there can be some confusion. For example, if the Fortran package is just symengine, does it refer to symengine.f or symengine.cpp? Given that we are in Fortran, it would seem natural that if there are no -f or .f, it refers to the Fortran version, and we use extension like .cpp if the package does not contain wrappers and is just written in another language (to be used as a dependency). Note that symengine.cpp is just this GitHub url: https://github.com/symengine/symengine, it does not have a .cpp suffix (to further confuse things).


For pure Fortran original upstream code, say stdlib or fpm, I don’t think we need to append anything, we can just use stdlib. We can append stdlib.cpp for C++ wrappers to our Fortran stdlib.


So one way forward is to not use any suffix for upstream packages written in Fortran, and perhaps use a suffix just for wrappers, to make it clear that those are just wrappers. That leaves room to use no suffix for the original (C++) package. So symengine would mean the C++ package and symengine.f the Fortran wrappers. So toml-f should be renamed to just toml, because it is not a wrapper but an original package. The problem is that the module names cannot have .f or -f in them, breaking the current fpm convention for Fortran module names starting with the package name.


Note that I am talking about how the package is referred to in fpm.toml as well as how the modules are named, as fpm enforces to put the name of the package in the module name. (Further complicating the matter, since we can use neither -f or .f in the module name.)


Given all this, the cleanest so far to me seems:

  • in fpm.toml we use no suffix, and the Fortran module names start exactly with the package name. That restricts available characters in the package name to characters and underscore. So toml and symengine, and the Fortran module names start with toml_something and symengine_something, enforced by fpm.
  • If we need to build the original C++ package as an fpm package, we name it symengine.cpp in fpm (@awvwgk mentioned you can’t use . in toml, so perhaps symengine-cpp). That way it is clear which one it is. Then the Fortran symengine wrappers can simply depend on symengine.cpp in their fpm.toml.

Let’s first figure out whether we should use -f suffix or no suffix in fpm.toml. I vote for no suffix. Then we can go from there to design the rest.

We can use dots in TOML, but than we must use quoted keys instead of bare keys, otherwise we would declare implicit tables by the dotted key mechanism (TOML specs):

[dependencies]
symengine.f = "^0.7"  # (almost) equivalent to: symengine = {f = "^0.7"}
"symengine.f" = "^0.7"  # correct but doesn't look nice

Sorry for the OT post :wink:

Using toml-f as name for the TOML Fortran package instead of toml was a deliberate choice. There is a precedence in Python with the toml package, which is currently stuck at TOML 0.5.0 and not maintained anymore, but still widely in use. Going so far that the Python community was hesitant to adopt TOML 1.0.0 for their pyproject.toml for a time. Their resolution was to create a new TOML 1.0.0 compliant library from scratch (tomli), still most users working in Python will use toml instead and might get surprised over missing support for heterogenous arrays.

As a project maintainer we have the responsibility to make a choice here. I’m not going to claim the toml package name, and I would recommend future Fortran projects to not claim it as well. Maybe toml-f will be maintained by the Fortran community forever, but you never know.

This issue is of course different for wrapping an existing upstream project, the wrapper will be bound to the fate of said project. The precedence here is that languages that do not share the space with C and C++ in the installation prefix usually use the same name, because there is no reason not to. While external C bindings for C++ projects or C++ wrappers for C projects usually have different names. For me, Fortran belongs to the latter category.

I don’t like this suggestion at all. Building a project as part of another and vendoring it (and maybe its API as well) is usually discouraged and can lead to a lot of trouble later. Especially when packaging, the process of devendoring dependencies embedded in other projects is just annoying.

Of course we want to build our own ecosystem with fpm, but I don’t want to build it in a way that we are locking us out from existing ecosystems.