Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Problems with Generic Reduce example #22

Open
wclodius2 opened this issue Nov 4, 2020 · 3 comments
Open

Problems with Generic Reduce example #22

wclodius2 opened this issue Nov 4, 2020 · 3 comments

Comments

@wclodius2
Copy link
Contributor

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
@tclune
Copy link
Member

tclune commented Nov 5, 2020

To be clear. That and the other examples that are currently in the repository are largely a capture of a prototyping discussion at the Tokyo meeting. (I'm not even sure at the moment which work I did on this particular example - I think I just varied some things to ask "what if" questions.)

The more full-fledged example is the block-matrix where I took it away as a homework exercise and iterated a couple of times with Magne.

In any event to be fully intelligible (aside from the fact that they don't even compile as written), there needs to be some writeup of the approach that Magne was having us follow. With insight one might be able to glean the method to his madness, but it probably would be much better with a nice exposition.

I do believe there were variants of this particular example that I or Damian did get to compile. The C preprocessor was used to enable some of the pseudo syntax. But again, we were rapidly doing different variations and it is not suprising that some things were lost by the time anything was captured in a repository. I am a bit surprised that the CONTAINS got lost though.

Cheers

@tclune
Copy link
Member

tclune commented Nov 5, 2020

PS - I encourage you to provide your improvements above as a PR.

@wclodius2
Copy link
Contributor Author

I have greatly modified my improvements, put each alternative in a separate file, added markdown documentation in the document reduce.md, and submitted a PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants