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

Improved control of type bound procedure accessibility #127

Open
nncarlson opened this issue Jan 3, 2020 · 10 comments
Open

Improved control of type bound procedure accessibility #127

nncarlson opened this issue Jan 3, 2020 · 10 comments
Labels
Clause 9 Standard Clause 9: Use of data objects

Comments

@nncarlson
Copy link

Here is a problem I regularly encounter: I have a class with a type bound procedure that (a) should not be publicly accessible (for invoking); but (b) I want to allow an extension of the class to override the procedure. In many cases the class is an abstract type and the procedure is deferred. This appears in the non-virtual interface (NVI) pattern, for example. Because of (a) I want to declare the procedure private, but due to (b) I have to let it be public (the two type declarations are in separate modules).

I wish there was another type bound procedure accessibility attribute between public and private with the effect that the procedure would not be accessible for invoking, but would be accessible when declaring an extension of the type.

@certik
Copy link
Member

certik commented Jan 3, 2020

@nncarlson is it the same thing as protected members in C++?

@nncarlson
Copy link
Author

@certik After visiting SO (my C++ is weak) I think it is the same in spirit but there are some differences due to Fortran's additional module scope. As it pertains to type bound procedures:

  • Fortran public == C++ public
  • Fortran private => inaccessible outside module, but still accessible and overridable within the module.

So perhaps what I'd propose is a new attribute protected for type bound procedure declarations with this effect:

  • Within the module scope: protected functionally the same as private (or public)
  • Outside of module scope: protected parent procedure would be accessible (and overloadable) within code defining/implementing a child type, but otherwise as private
  • protected is incompatible either non_overridable or private

Perhaps a protected attribute makes sense for data components of a derived type too.

@cmacmackin
Copy link

cmacmackin commented Jan 3, 2020 via email

@klausler
Copy link

klausler commented Jan 3, 2020

Be advised: the keyword PROTECTED is already used for something else (8.5.15).

Maybe it would suffice to define an exception to PRIVATE checking in this specific case of overriding a parent procedure in an extended derived type.

@nncarlson
Copy link
Author

nncarlson commented Jan 3, 2020

@cmacmackin, interestingly enough in fact you can (I was surprised), but it's not exactly overriding the procedure. The parent class doesn't see it as being overridden as the following example shows (but this behavior could be exactly what one wants -- just not in the NVI-type use cases I'm usually encountering).

But the one case this definitely doesn't work at all is with an abstract base class and deferred procedure, because deferred and private are incompatible attributes.

Here's the example:

module parent_type
  private
  type, public :: parent
  contains
    procedure, private :: private_sub
    procedure :: public_sub
  end type
contains
  subroutine private_sub(this)
    class(parent) :: this
    print *, 'parent:private_sub'
  end subroutine
  subroutine public_sub(this)
    class(parent) :: this
    call this%private_sub
  end subroutine
end module

module child_type
  use parent_type
  private
  type, extends(parent), public :: child
  contains
    procedure :: private_sub
  end type
contains
  subroutine private_sub(this)
    class(child) :: this
    print *, 'child:private_sub'
  end subroutine
end module

use parent_type
use child_type
type(parent) :: p
type(child) :: c
call p%public_sub  ! prints parent:private_sub
call c%public_sub  ! prints parent:private_sub
end

@nncarlson
Copy link
Author

@klausler I forgot that protected was already in use. But would that prevent it from being used in another context where it can't be currently used at all? That said, I don't find "protected" to be all that descriptive of what it implies for C++, I'm just using it as proxy by analogy with C++.

@nncarlson
Copy link
Author

@klausler

Maybe it would suffice to define an exception to PRIVATE checking in this specific case of overriding a parent procedure in an extended derived type.

Yes, that would too. And if one didn't want that exception the procedure could bePRIVATE, NON_OVERRIDABLE. That would recover the existing behavior outside of the module scope, though change things for the module scope.

@FortranFan
Copy link
Member

@nncarlson wrote:

Here is a problem I regularly encounter: I have a class with a type bound procedure that (a) should not be publicly accessible (for invoking); but (b) I want to allow an extension of the class to override the procedure. In many cases the class is an abstract type and the procedure is deferred. This appears in the non-virtual interface (NVI) pattern, for example. Because of (a) I want to declare the procedure private, but due to (b) I have to let it be public (the two type declarations are in separate modules).

I wish there was another type bound procedure accessibility attribute between public and private with the effect that the procedure would not be accessible for invoking, but would be accessible when declaring an extension of the type.

@nncarlson and all interested,

Please take a look at this discussion thread on essentially the same user need as here from Intel Fortran Forum several years ago: https://software.intel.com/en-us/forums/intel-fortran-compiler/topic/681705.

In this Intel Fortran forum thread, please look at the comments by IanH, a brilliant mind when it comes to programming languages as well as the Fortran standard: IanH makes some excellent points e.g., "The result of the interp results in a capability gap for authors when they want a binding that can be overriden but not invoked - which is what I guess was the intent of the code in Fortran Fan's example. It would be nice to see the language address this in the future." and who provides other use cases later in the thread involving designing of libraries using Fortran.

I'll check but my recollection is a similar need has been expressed on other forums (comp-fortran-90 mailing list or even J3 itself) re: derived type (or "classes") and type-bound procedures vis-a-vis the current accessibility statements with PUBLIC/PRIVATE that work at the MODULE level in Fortran.

So my thought was to consider the concept of EXTENSIBLE MODULEs as I had mentioned in the Intel forum link above which, if combined suitably with the concept of PACKAGEs and/or NAMESPACEs, can prove quite handy, I believe.

extend (!) the EXTENDS facility in the language to MODULEs as well and to introduce OVERRIDABLE keyword and see if this can address the "capability gap" mentioned by IanH above. For example, say the language allowed the following:

module, overridable :: b_m
   ! NOTICE the keyword above implying this module is EXTENSIBLE
   ! By default, modules are to be NON_OVERRIDABLE

   implicit none

   private

   type, abstract, public :: b_t
   contains
      private
      procedure(Ip), pass(this), deferred :: p
   end type b_t

   abstract interface
      subroutine Ip( this)
         import :: b_t
         class(b_t), intent(inout) :: this
      end subroutine Ip
   end interface

end module b_m
module, extends(b_m) e_m
   ! NOTICE extends keyword above

   implicit none

   private

   type, extends(b_t), public :: e_t
   contains
      procedure, pass(this) :: p => e_p
   end type e_t

contains

   subroutine e_p( this)
      class(e_t), intent(inout) :: this
   end subroutine e_p

end module e_m

Then can one effectively view a new module (e_m) as belonging to the same scope as base module (b_m) and thereby what's private to b_m can become accessible to the extended module. Developers of base module can mark it as either OVERRIDABLE (or a keyword of another name , say EXTENSIBLE) or NON_OVERRIDABLE (a keyword which already exists in the context of TBPs or a new one, say NON_EXTENSIBLE). The default will be such as to be consistent with existing code. I wonder if this can give flexibility to coders when it comes to extending derived types. My thought is to introduce functionality somewhat similar to "internal" (or protected or even friend) inheritance in other OO languages where "children" can access certain information from "parents" that others consumers cannot.

@zjibben
Copy link
Member

zjibben commented Jan 8, 2020

Is this the same as #16, and the proposal @aradi started in #31?

@aradi
Copy link
Contributor

aradi commented Jan 9, 2020

No, it is not. Issue #16 and PR #31 aim for allowing read-only access to variables in derived types (similarly, how you can do it for module variables with the protected attribute already). This issue here, as far as I understand, tries to fix the problem that a type-bound procedure is either public or private, but there is no way to allow special access for extending derived types. It aims to introduce something like the protected methods in C++. (Which would be also definitely a very desirable goal.)

@certik certik added the Clause 9 Standard Clause 9: Use of data objects label Apr 23, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Clause 9 Standard Clause 9: Use of data objects
Projects
None yet
Development

No branches or pull requests

7 participants