# What is benefit of PASS in type-bound procedures?

**URL:** <https://fortran-lang.discourse.group/t/what-is-benefit-of-pass-in-type-bound-procedures/9294>\
**Category:** Help\
**Created:** [March 4, 2025, 3:21pm UTC](https://fortran-lang.discourse.group/t/what-is-benefit-of-pass-in-type-bound-procedures/9294 "2025-03-04T15:21:10Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![ivanpribec](https://yyz2.discourse-cdn.com/free1/user_avatar/fortran-lang.discourse.group/ivanpribec/32/3290_2.png) [@ivanpribec](https://fortran-lang.discourse.group/u/ivanpribec)\
**Post date:** [March 4, 2025, 3:21pm UTC](https://fortran-lang.discourse.group/t/what-is-benefit-of-pass-in-type-bound-procedures/9294/1 "2025-03-04T15:21:10Z")

</div>

In Fortran we can control the passed-object dummy argument of type-bound procedures:

```auto
module engine_mod

type :: engine_type
contains
   procedure(ignite_fun), pass(engine) :: ignite
end type

contains
   subroutine ignite(params,engine)
      real :: params(3)
      class(engine_type) :: engine
      print *, "Igniting engine with params: ", params
   end subroutine
end module

```

This is different from languages like C++ which uses an implicit object parameter, the so-called [`this` pointer](https://en.cppreference.com/w/cpp/language/this), or from Python [method objects](https://docs.python.org/3/tutorial/classes.html#method-objects) where it appears as the first argument of the method and is usually named `self` (this is not required).

As explained in the Fortran interpretation document (J3/24-007, sec. 7.5.4.5, pg. 96),

> If a procedure is bound to several types as a type-bound procedure, different dummy arguments might be the  
> passed-object dummy argument in different contexts.

This means if we have a procedure using multiple different derived type classes, those procedures can be bound to multiple methods.

Is there any special use case of this feature? Is it just for the convenience of the caller? What would be a good situation to use it?

---

<div class="post-metadata">

**Author:** ![Arjen](https://avatars.discourse-cdn.com/v4/letter/a/b9bd4f/32.png) [@Arjen](https://fortran-lang.discourse.group/u/Arjen)\
**Post date:** [March 4, 2025, 3:34pm UTC](https://fortran-lang.discourse.group/t/what-is-benefit-of-pass-in-type-bound-procedures/9294/2 "2025-03-04T15:34:47Z")

</div>

You can also use it to keep the interface the same, when a type does not appear as the first argument. For instance, a routine that was written before the standard offered type-bound procedures.

---

<div class="post-metadata">

**Author:** ![ivanpribec](https://yyz2.discourse-cdn.com/free1/user_avatar/fortran-lang.discourse.group/ivanpribec/32/3290_2.png) [@ivanpribec](https://fortran-lang.discourse.group/u/ivanpribec)\
**Post date:** [March 4, 2025, 3:47pm UTC](https://fortran-lang.discourse.group/t/what-is-benefit-of-pass-in-type-bound-procedures/9294/3 "2025-03-04T15:47:22Z")

</div>

Isn’t that a property the `NOPASS` gives you?

The `PASS` only comes into play when you invoke a method through the `obj%tbp()` mechanism.

---

<div class="post-metadata">

**Author:** ![jwmwalrus](https://yyz2.discourse-cdn.com/free1/user_avatar/fortran-lang.discourse.group/jwmwalrus/32/4483_2.png) [@jwmwalrus](https://fortran-lang.discourse.group/u/jwmwalrus)\
**Post date:** [March 4, 2025, 4:47pm UTC](https://fortran-lang.discourse.group/t/what-is-benefit-of-pass-in-type-bound-procedures/9294/4 "2025-03-04T16:47:43Z")

</div>

Since there are no reserved keywords in Fortran, a `this` (or `self` or `me`, etc.) couldn’t be considered, so the passed-object had to be explicit —besides Python, Go comes to mind as another example of explicit passed-object (where it’s called “receiver”).

The `pass` attribute may useful for overloading or defining operators:

```fortran
type :: t
contains
    generic :: operator(+) => t_plus_t, t_plus_i, i_plus_t
    procedure :: t_plus_t, t_plus_i
    procedure, pass(rhs) :: i_plus_t
end type

```

In C++ you would have to use a `friend` function for the `i_plus_t` case.

Also, there might be a debate of whether `inout`|`out` arguments should go at the beginning (command order) or at the end (processing order), so the `pass` attribute helps with your preferences.

---

<div class="post-metadata">

**Author:** ![jwmwalrus](https://yyz2.discourse-cdn.com/free1/user_avatar/fortran-lang.discourse.group/jwmwalrus/32/4483_2.png) [@jwmwalrus](https://fortran-lang.discourse.group/u/jwmwalrus)\
**Post date:** [March 4, 2025, 4:56pm UTC](https://fortran-lang.discourse.group/t/what-is-benefit-of-pass-in-type-bound-procedures/9294/5 "2025-03-04T16:56:40Z")

</div>

The case of reusing the procedure in multiple derived types seems less useful, so it might just be a side-effect of the way OOP was introduced into the language?

```fortran
module mod1
    type :: t1
    contains
        generic :: operator(//) => t1_concat_t2
        procedure :: t1_concat_t2
    end type

    type :: t2
    contains
        generic :: operator(//) => t1_concat_t2
        procedure, pass(rhs) :: t1_concat_t2
    end type

contains
    pure function t1_concat_t2(lhs, rhs) result(str)
        character(:), allocatable :: str
        class(t1), intent(in) :: lhs
        class(t2), intent(in) :: rhs
        ...
    end function
end module
```

---

<div class="post-metadata">

**Author:** ![ivanpribec](https://yyz2.discourse-cdn.com/free1/user_avatar/fortran-lang.discourse.group/ivanpribec/32/3290_2.png) [@ivanpribec](https://fortran-lang.discourse.group/u/ivanpribec)\
**Post date:** [March 4, 2025, 6:33pm UTC](https://fortran-lang.discourse.group/t/what-is-benefit-of-pass-in-type-bound-procedures/9294/6 "2025-03-04T18:33:06Z")

</div>

Typically I don’t use operator overloading a lot, and if possible I limit the use to non-polymorphic types. But I can see why that is needed and it makes sense franly.

As you remark, the case of reusing a procedure in multiple derived types is a bit strange.  
What irks me a bit about this feature is the strong coupling it introduces.

Here is a potential use-case I’ve found for providing default settings, thematically related to the threads:

- [Generic or Kind-Agnostic linear system solvers](https://fortran-lang.discourse.group/t/generic-or-kind-agnostic-linear-system-solvers/7574)
- [stdlib\_LinAlgOperators: Linear Algebra Operators](https://fortran-lang.discourse.group/t/stdlib-linalgoperators-linear-algebra-operators/9286)
- [Modern Fortran interface for NLopt - #19 by ivanpribec](https://fortran-lang.discourse.group/t/modern-fortran-interface-for-nlopt/2685/19)

The example works with gfortran, ifort, ifx, flang, nvfortran and nagfor.

```fortran
! linear_operators_demo.f90 --
! Example of using passed-object dummy arguments as a means
! of implementing a default fallback method.
!
! This produces tight coupling of the problem class (linop) and the
! solver class (krylov_method).
!
module linear_operators

implicit none
private

public :: dp, linop, matvec, preconditioner
public :: krylov_method, bicgstab
public :: linsolve

integer, parameter :: dp = kind(1.0d0)

! A linear operator of the form y = op(x)
type, abstract :: linop
    integer :: n = 0
contains
   procedure(apply_sub), deferred :: apply
end type

abstract interface
    subroutine apply_sub(op,x,y)
        import linop, dp
        class(linop), intent(in) :: op
        real(dp), intent(in) :: x(op%n)
        real(dp), intent(out) :: y(op%n)
    end subroutine
end interface

! Applies the y = A x operation, where A is a dense matrix
type, extends(linop) :: matvec
    ! Dense matrix, for the sake of this experiment
    real(dp), allocatable :: A(:,:)
contains
    procedure :: apply => apply_matvec

! N.b.: we could also make this method non_overridable
    procedure, pass(A) :: solve => solve_using_method
end type

! Applies the P = M^{-1} operation, by default this is just M = I
type, extends(linop) :: preconditioner
contains
    procedure :: apply => default_pc
end type

! Applies the A^{-1} operation, using a Krylov process
type, abstract :: krylov_method
contains
    procedure(solve_sub), deferred, pass(method) :: apply
end type

abstract interface
    subroutine solve_sub(A,b,x,P,method,info)
        import matvec, preconditioner, krylov_method, dp
        class(matvec), intent(in) :: A
        real(dp), intent(in) :: b(:)
        real(dp), intent(inout) :: x(:)
        class(preconditioner), intent(in), optional :: P
        class(krylov_method), intent(inout), optional :: method
        integer, intent(out) :: info
    end subroutine
end interface

! One particular Krylov method
type, extends(krylov_method) :: bicgstab
contains
    procedure, pass(method) :: apply => apply_bicgstab
end type

! Add to generic overload set, for solving different problems
interface linsolve
    module procedure :: solve_using_method
end interface

contains

    ! Dense matrix-vector product
    subroutine apply_matvec(op,x,y)
        class(matvec), intent(in) :: op
        real(dp), intent(in) :: x(op%n)
        real(dp), intent(out) :: y(op%n)
        y = matmul(op%A,x)
    end subroutine

    ! y = I x
    subroutine default_pc(op,x,y)
        class(preconditioner), intent(in) :: op
        real(dp), intent(in) :: x(op%n)
        real(dp), intent(out) :: y(op%n)
        y = x
    end subroutine

    subroutine apply_bicgstab(A,b,x,P,method,info)
        class(matvec), intent(in) :: A
        real(dp), intent(in) :: b(:)
        real(dp), intent(inout) :: x(:)
        class(preconditioner), intent(in), optional :: P
        class(bicgstab), intent(inout), optional :: method
        integer, intent(out) :: info

        ! this should always works in theory
        if (.not. present(method)) error stop "bicgstab method is not present."

        info = 0
        print *, "Calling fake bicgstab routine"

    end subroutine

    ! generic solve procedure with fallback to a default method
    subroutine solve_using_method(A,b,x,P,method,info)
        class(matvec), intent(in) :: A
        real(dp), intent(in) :: b(:)
        real(dp), intent(inout) :: x(:)
        class(preconditioner), intent(in), optional :: P
        class(krylov_method), intent(inout), optional :: method
        integer, intent(out) :: info
        if (present(method)) then
            call method%apply(A,b,x,P,info)
        else
            info = 0
            print *, "Calling fallback method"
        end if
    end subroutine

end module

program linear_operator_demo

    use linear_operators
    implicit none

    type(preconditioner) :: eye
    type(matvec) :: A
    class(krylov_method), allocatable :: method

    integer, parameter :: n = 3
    real(dp) :: x(n), b(n)
    integer :: info

    allocate( bicgstab :: method )

    call method%apply(A,b,x,info=info)
    call A%solve(b,x,method=method,info=info)
    call A%solve(b,x,info=info)

    call linsolve(A,b,x,P=eye,method=method,info=info)

end program

```

```txt
$ gfortran -Wall linear_operators_demo.f90 
$ ./a.out
 Calling fake bicgstab routine
 Calling fake bicgstab routine
 Calling fallback method
 Calling fake bicgstab routine

```

---

<div class="post-metadata">

**Author:** ![jwmwalrus](https://yyz2.discourse-cdn.com/free1/user_avatar/fortran-lang.discourse.group/jwmwalrus/32/4483_2.png) [@jwmwalrus](https://fortran-lang.discourse.group/u/jwmwalrus)\
**Post date:** [March 4, 2025, 7:41pm UTC](https://fortran-lang.discourse.group/t/what-is-benefit-of-pass-in-type-bound-procedures/9294/7 "2025-03-04T19:41:22Z")

</div>

> [@ivanpribec](#):
>
> Typically I don’t use operator overloading a lot, and if possible I limit the use to non-polymorphic types

Polymotphism is not that bad —the real problem is unnecessary inheritance.

Your example is interesting —although, the the passed-object being `optional` looks weird.

---

<div class="post-metadata">

**Author:** ![ivanpribec](https://yyz2.discourse-cdn.com/free1/user_avatar/fortran-lang.discourse.group/ivanpribec/32/3290_2.png) [@ivanpribec](https://fortran-lang.discourse.group/u/ivanpribec)\
**Post date:** [March 4, 2025, 8:06pm UTC](https://fortran-lang.discourse.group/t/what-is-benefit-of-pass-in-type-bound-procedures/9294/8 "2025-03-04T20:06:06Z")

</div>

> [@jwmwalrus](#):
>
> the the passed-object being `optional` looks weird.

I thought so too; but then I thought about C and C++ - when you have a pointer argument, there is always a possibility it could be null.

I just created a smaller example, and it works consistently across compilers ([Compiler Explorer](https://godbolt.org/z/echEGdEG8)) in the non-polymorphic case:

```fortran
module test_mod
    implicit none
    type :: foo
    contains
        procedure, pass :: say_hello
        procedure, nopass :: say_goodbye
    end type
contains
    subroutine say_hello(f)
        class(foo), optional :: f
        if (present(f)) then
            print *, "hello from f"
        else
            print *, "hello from null()"
        end if
    end subroutine
    subroutine say_goodbye
        print *, "goodbye"
    end subroutine
end module

program test
    use test_mod
    type(foo), allocatable :: f
    call f%say_hello
    allocate(f)
    call f%say_hello
    call f%say_goodbye
end program

```

In the polymorphic case, the majority of compilers produce crashing executables:

```auto
program test
    use test_mod
! N.b. v-- class here
    class(foo), allocatable :: f
    call f%say_hello ! <-- breaks on ifx, ifort, flang, nvfortran, and nagfor
    allocate(f)
    call f%say_hello
    call f%say_goodbye
end program

```

I guess the crash has to do with the fact that before allocation, the dynamic type of `f` isn’t yet established, meaning the virtual method table of the `f` instance isn’t populated either (the procedured could be overriden by a child type). If I add the `non_overridable` attribute to `say_hello`, then flang doesn’t crash which makes sense to me.

The rules for type-bound procedure overriding stipulate:

 ![image](https://global.discourse-cdn.com/free1/uploads/fortran_lang/original/2X/3/30ba84553466d5f0846ee87e15e018341992fbcb.png)
