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

Allow to chain the % operator #317

Open
certik opened this issue Oct 24, 2023 · 3 comments
Open

Allow to chain the % operator #317

certik opened this issue Oct 24, 2023 · 3 comments

Comments

@certik
Copy link
Member

certik commented Oct 24, 2023

This was proposed in https://j3-fortran.org/doc/year/18/18-134.txt, but rejected.

What were the arguments against? Let's document it here.

This was recently re-requested in https://fortran-lang.discourse.group/t/whats-your-experience-with-using-for-accessing-object-members.

@klausler
Copy link

klausler commented Oct 24, 2023

Technically, % is not an operator. It's the punctuation mark between part-refs in a data-ref and the punctuation used in a procedure component reference or TBP procedure-designator.

In other languages whose functions (unlike Fortran's) can return references to objects, it is a natural and readable idiom to write something like object.set_position(x,y).set_target(tx,ty).set_speed(10).step().step();. Fortran can't return object references from functions, so being able to apply a procedure component reference or TBP reference to a function result still wouldn't enable this kind of usage.

Fortran differentiates between subroutines and functions. In "chained" usage as above, neither a subroutine call nor a function reference seems to apply -- the procedure components or TBPs being called are returning results, but the last result isn't being consumed by an expression. I suppose that a derived type with such an API could have a done() subroutine TBP to terminate the chain (or status() function).

Fortran can return a pointer from a function, and the result of a function returning an object pointer can be used as a variable in standard Fortran in many contexts (though not in all implementations yet). Such a variable could be used as the base object of a procedure component reference or TBP reference; this would be a small extension to the language. It would have the limitation that the original base object be a pointer or TARGET, and that could lead to lots of extra TARGET attributes being needed for objects in client code.

For functions returning non-pointer derived types, being able to apply a procedure component or TBP reference to their results would obviously be feasible, but that would be applicable only to "stateless" functional values due to the lack of object references.

References to data components of function results (whether procedure components or TBPs or not) would also be useful and natural -- it's tiresome to try to explain that z%re works but zf()%re does not. The rules for finalization might need to be changed to ensure that the data component can't be affected by finalization until it has been consumed.

@gronki
Copy link

gronki commented Mar 11, 2024

This is certainly one of the most needed fixes in Fortran. Quite unbelievable that it was rejected.

In other languages whose functions (unlike Fortran's) can return references to objects, it is a natural and readable idiom to write something like object.set_position(x,y).set_target(tx,ty).set_speed(10).step().step();. Fortran can't return object references from functions, so being able to apply a procedure component reference or TBP reference to a function result still wouldn't enable this kind of usage.

It would, just that it would generate copies of the object, and the last copy would need to be assigned to another variable. This would still be immensely practical for many cases. Clean code is typically not used in performance-critical sections but to build elegant interfaces, so producing copies in this case would not be a big issue. For example:

object = object % set() % launch()

Just like one would write

object = launch(set(object))

So original object would be modified by set, returning a modified temporary copy; that would be modified by launch, returning another copy; last, this would be assigned to object so another copy. That is okay though, because it enables building things, nice things. I feel like the only reason it is not in the language is that somebody did not think about it and now we have to deal with the consequences.

Of course, this only makes sense for functions. I do not see how it could work for subroutines.

@FortranFan
Copy link
Member

FortranFan commented Mar 12, 2024

For example:

object = object % set() % launch()

In addition to the simpler option of object = launch(set(object)) shown above, the language also supports another option with one level of indirection:

module m
   type :: t
      integer :: n = 0
   contains
      procedure :: set
      procedure :: launch
   end type
contains
   function set( this ) result(r)
      class(t), intent(in) :: this
      type(t) :: r
      r%n = this%n + 1
   end function 
   function launch( this ) result(r)
      class(t), intent(in) :: this
      type(t) :: r
      r%n = this%n + 41
   end function 
end module
   use m
   type(t) :: object
   associate ( o => object%set() )
      object =  o%launch()
   end associate
   print *, "object%n = ", object%n, "; expected is 42"
end
C:\temp>gfortran -ffree-form p.f -o p.exe

C:\temp>p.exe
 object%n =           42 ; expected is 42

So the issue then becomes one of convincing the implementors of the value of introducing facilities in the language that eases matters for the practitioners. Possibly the Community will get behind LFortran and learn to add such features by themselves, that can be a really progressive space given how @certik and co have built that up. But the other vendors are unlikely to be keen to support this.

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

4 participants