Description
In the scenarios directory @tclune has attempted to provide use cases and examples of how generics might work in Fortran. I have only examined in detail his Generic Reduce example. His reduce.F90 code has a number problems as an example:
- The code deviates unnecessarily from F2018 syntax, e.g., the procedures are not defined in a contains section of the module, there is no end module statement, and whole array functions are given the elemental attribute.
- It is not clear how parameters are assigned at instantiation.
- The definitions in the requirement section do not match up with the uses in the procedures, e.g., what he calls zero in the requirement is apparently called unit in the procedures, and what he calls .operator. in requirement is called .oper. in the procedures. Other definitions in requirement are apparently not used at all.
- He appears to want to use the example as an example of strong concepts (a generic definition that can be type checked before instantiation), but does not provide the equivalent of the interfaces needed for strong concepts.
- He is not consistent in his definition of the function results. He assigns a type to the function name, implying that it is the function result, but then treats
sum
andproduct
as if they were the function result in the same function. - The code is not robust in that size can easily overflow.
Below are four examples that attempt to fix the most obvious problems, though they may have problems of their own. In addition to fixing the above problems, they differ from @tclune's examples in that they: first, only define one reduction procedure while he attempts to define several; and second, use a function and not an operator as the operation to be reduced. The three examples differ in how the function is associated with the reduction procedure: the first makes it an explicit argument to the reduction procedure; the second makes it a type bound procedure; the third makes it an explicit parameter to the generic module; and the fourth implicitly imports the function from the scope of the instantiation.
module generic_reduce(T)
use, intrinsic:: iso_fortran_env, only: int64
implicit none
requirement
type :: T ! May need syntax to indicate assignment is defined
end type
end requirement
contains
function reduce(x, fun)
type(T) :: reduce
type(T), intent(in) :: x(:)
procedure(func) :: fun
abstract interface
function func(y,z)
type(T), intent(in) :: y, z
type(T) :: func
end function func
end interface
integer(int64) :: i
if ( size(x, kind=int64) == 0 ) &
error stop "In REDUCE, X must have at least one element."
reduce = x(1)
do i = 2, size(x,kind=int64)
reduce = fun(reduce, x(i))
end do
end function reduce
end module generic_reduce
or
module generic_reduce(T)
use, intrinsic:: iso_fortran_env, only: int64
implicit none
requirement
type :: T ! May need syntax to indicate assignment is defined
contains
procedure(func) :: fun
end type
abstract interface
function func(y,z)
nature(T), intent(in):: y ! nature(T) matches both type(T) and class(T)
type(T), intent(in) :: z
type(T) :: func
end function func
end interface
end requirement
contains
function reduce(x)
type(T) :: reduce
type(T), intent(in) :: x(:)
integer(int64) :: i
if ( size(x, kind=int64) == 0 ) &
error stop "In REDUCE, X must have at least one element."
reduce = x(1)
do i = 2, size(x,kind=int64)
reduce = reduce % fun(x(i))
end do
end function reduce
end module generic_reduce
or
module generic_reduce(T, fun)
use, intrinsic:: iso_fortran_env, only: int64
implicit none
requirement
type :: T ! May need syntax to indicate assignment is defined
end type
interface
function fun(y,z)
nature(T), intent(in):: y ! nature(T) matches both type(T) and class(T)
type(T), intent(in) :: z
type(T) :: fun
end function fun
end interface
end requirement
contains
function reduce(x)
type(T) :: reduce
type(T), intent(in) :: x(:)
integer(int64) :: i
if ( size(x, kind=int64) == 0 ) &
error stop "In REDUCE, X must have at least one element."
reduce = x(1)
do i = 2, size(x, kind=int64)
reduce = fun(reduce, x(i))
end do
end function reduce
end module generic_reduce
or
module generic_reduce(T)
use, intrinsic:: iso_fortran_env, only: int64
import:: fun ! indicates that fun comes from the scope of the instantiation
! and doesn't have to be an explicit generic parameter
implicit none
requirement
type :: T ! May need syntax to indicate assignment is defined
end type
interface
function fun(y,z)
nature(T), intent(in):: y ! nature(T) matches both type(T) and class(T)
type(T), intent(in) :: z
type(T) :: fun
end function fun
end interface
end requirement
contains
elemental function reduce(x)
type(T) :: reduce
type(T), intent(in) :: x(:)
integer(int64) :: i
if ( size(x, kind=int64) == 0 ) &
error stop "In REDUCE, X must have at least one element."
reduce = x(1)
do i = 2, size(x, kind=int64)
reduce = fun(reduce, x(i))
end do
end function reduce
end module generic_reduce