Skip to content

Problems with Generic Reduce example #22

Open
@wclodius2

Description

@wclodius2

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:

  1. 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.
  2. It is not clear how parameters are assigned at instantiation.
  3. 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.
  4. 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.
  5. 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 and product as if they were the function result in the same function.
  6. 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions