Parameterised Modules

Along the lines of generics/concepts in Fortran, I was thinking it would be useful to be able to parameterise a module based (at least) on a kind, e.g.,

module module_name(wp)
    integer, kind :: wp
    ...
    ...
end module_name

This would be like a highly restricted version of functors in ML languages and some other functional languages, and would provide a means of specifying kinds without having to create modules dedicated to specifying the working precision across a code base like mod_kinds or mod_kind_types or mod_types or all the helper modules that people usually create for this purpose.

3 Likes

Would the corresponding USE statement be

program main
use module_name(wp=kind(1.0d0))
end program main

Would you allow

program main
use module_name(wp=kind(1.0d0))
use module_name(wp=kind(1.0))
end program main

If so, if the module has function foo, which the main program accessed, which version of foo would be called?

What is the benefit of the proposed syntax over the current

module module_name
use kind_mod, only: wp
end module module_name

@vsnyder was a big proponent of parameterized modules, having written many proposals for it, e.g., https://j3-fortran.org/doc/year/05/05-107.pdf

I’m not sure what the prevailing opinion of parametrized modules is. It seems to solve a lot of the generic programming issues I personally encounter, but it is definitely a lot narrower than, say, the designs coming out of the generics subgroup.

To give my input on the question: 1st part yes. I like that use statement very much.

2nd question - multiple uses of a module in another scope: You should be allowed to use the module with as many unique kinds as desired, all within the same scope. The correct foo should be chosen at compile time based on the input/output arguments (and possible return type). If the compiler cannot make this determination at compile time, error. Under the hood, I imagine a use of the module would be compiling the entire thing for the given kind provided, and placing all subprograms in interface blocks. Therefore, if there is no way to determine the desired subprogram from arguments and return types, that should be considered a programmer error. Alternatively, allow some additional keyword (along the lines of pure, recursive, elemental, simple) to indicate that a subprogram in a module should only ever be included once, even if the module appears in multiple use statements in another scope.

Yes, the use statement will be as you wrote it.

For your second question, there re at least two possible solutions (both compile time-determined):

  1. Allow a new type called module and and create modules as required, e.g.,
program main
    module :: moda
    module :: modb 

    moda = use module_name(wp=kind(1.0d0)
    modb = use module_name(wp=kind(1.0)

    moda%foo()
    modb%foo()
end program main

β€˜OR’ If we allow simultaneous declaration and definition with no caveats:

program main
    module :: moda = use module_name(wp=kind(1.0d0)
    module :: modb = use module_name(wp=kind(1.0)

    moda%foo()
    modb%foo()
end program main

The syntax can always be tweaked but this would be the general idea.

  1. Exactly as @tyranids described. " The correct foo should be chosen at compile time based on the input/output arguments (and possible return type)". If it is a function or subroutine that is independent of the supplied wp, then the compiler should be able to distinguish this at compile time and call the foo function correctly.

As to your third question, with the current syntax, the wp is hard-coded into the module as it has to be a compile-time constant parameter and cannot be changed without resorting to a lot of complex gymnastics.

With the proposed syntax, it is still a compile-time constant but can be supplied outside the module or in other words, at the point of module use.

This is really useful. I wonder why this was not voted into the standard. Perhaps with some modifications or tweaks, it could be re-submitted for consideration.

As you pointed out, parameterised modules could solve a lot of generic programming issues. While it is not as broad as the generics/concepts work being carried out by the generics subgroup, it is complementary and in fact, independent too.

Parameterized modules were very seriously considered, and had several champions, but the Generics subgroup decided on a more expansive approach. I very much doubt there will be interest in a parallel effort.

At least initially this seems much clearer than other generic proposals. It seems like it would take a paragraph or so to explain and the syntax is succinct and simple. But then you quickly want type as well as kind, and probably LEN for character variables, and probably at least simple conditional inclusion, which starts to sound a lot like #define and #if/#elif/#else/#endif preprocessor directives if not quite as convoluted as some other generics proposals. If I were going to replace my generic input files only the ones that just do different integer or just different real kinds could be replaced with this; which would still be great; but I would need to at least be able to do a list of types to get most of them. And some currently require conditional code; often for when character or user-defined types are supported as well as basic numeric types/kinds. How would that be handled?

My original thought was to restrict this to just kind and nothing else. So, a module can only be parameterised on a kind.

Thanks @sblionel. It is good to know they were seriously considered.

This is not supposed to be a parallel effort or solution for generics although it helps and it should have no bearing on the work of the Generics subgroup. I do not see how one (negatively) affects or duplicates the other.

As a real-world example, most ML-type family of languages have functors as well as generics with no issue, and no, I am not advocating for turning Fortran into an ML language, just borrowing valuable and productive concepts to move the language forwards.