From owner-sc22wg5+sc22wg5-dom8=www.open-std.org@open-std.org  Wed Sep 27 22:11:13 2017
Return-Path: <owner-sc22wg5+sc22wg5-dom8=www.open-std.org@open-std.org>
X-Original-To: sc22wg5-dom8
Delivered-To: sc22wg5-dom8@www.open-std.org
Received: by www.open-std.org (Postfix, from userid 521)
	id 3B1603588D5; Wed, 27 Sep 2017 22:11:13 +0200 (CEST)
Delivered-To: sc22wg5@open-std.org
Received: from mail.jpl.nasa.gov (mailhost.jpl.nasa.gov [128.149.139.109])
	(using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits))
	(No client certificate requested)
	by www.open-std.org (Postfix) with ESMTP id 2B561358283
	for <sc22wg5@open-std.org>; Wed, 27 Sep 2017 22:11:08 +0200 (CEST)
Received: from [137.79.7.57] (math.jpl.nasa.gov [137.79.7.57])
	by smtp.jpl.nasa.gov (Sentrion-MTA-4.3.1/Sentrion-MTA-4.3.1) with ESMTP id v8RKB5Kk015457
	(using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128 bits) verified NO)
	for <sc22wg5@open-std.org>; Wed, 27 Sep 2017 13:11:06 -0700
Subject: Generic programming examples
From: Van Snyder <Van.Snyder@jpl.nasa.gov>
Reply-To: Van.Snyder@jpl.nasa.gov
To: sc22wg5 <sc22wg5@open-std.org>
Content-Type: text/plain; charset="ISO-8859-1"
Organization: Yes
Date: Wed, 27 Sep 2017 13:11:05 -0700
Message-ID: <1506543065.32527.251.camel@math.jpl.nasa.gov>
Mime-Version: 1.0
X-Mailer: Evolution 2.32.3 (2.32.3-37.el6) 
Content-Transfer-Encoding: 7bit
X-Source-Sender: Van.Snyder@jpl.nasa.gov
X-AUTH: Authorized
Sender: owner-sc22wg5@open-std.org
Precedence: bulk

Consider a module for spline interpolation:

  module Splines
    type :: Coeffs ( RK )
      integer, kind :: RK
      real, allocatable :: A(:), B(:), C(:), D(:)
    contains
      procedure :: Eval_D, Eval_S, ...
      generic :: Eval => Eval_D, Eval_S
      ! And more procedures to compute A, B, C, D, etc.
    end type Coeffs
    integer, public, parameter :: DK = kind(0.0d0), SK => kind(0.0e0)
  contains
    subroutine Eval_D ( C, X, Y )
      integer, parameter :: RK = dk
      class(coeffs(rk)), intent(in) :: C
      real(rk), intent(in) :: X
      real(rk), intent(out) :: Y
      include "Eval_Body.f9h"
    end subroutine Eval_D

    subroutine Eval_S ( C, X, Y )
      integer, parameter :: RK = sk
      class(coeffs(rk)), intent(in) :: C
      real(rk), intent(in) :: X
      real(rk), intent(out) :: Y
      include "Eval_Body.f9h"
    end subroutine Eval_S
    ....
  end module Splines

Now I need single, double and Q-precision splines:

  use Splines, only: Coeffs, DK, SK
  integer, parameter :: QK = selected_real_kind ( 1 + precision(1.0d0) )
  type(coeffs(dk)) :: C_D
  type(coeffs(sk)) :: C_S
  type(coeffs(qk)) :: C_Q
  real(dk) :: Y_d
  real(sk) :: Y_S
  real(qk) :: Y_q
  call c_d%eval ( 1.5_dk, y_d )
  call c_s%eval ( 1.5_sk, y_s )
  call c_q%eval ( 1.5_qk, y_q )

Notice that Splines doesn't define a Q-precision Eval_* subroutine, and
Coeffs doesn't provide a generic binding for one.  So the code won't
even compile.  Generic resolution fails because there is no Q-precision
generic to match c_q%eval.  This is a problem if all you have for your
spline library is .o and .mod files, and the company that provided it no
longer exists to give (or sell) you source to upgrade to your
requirements.

This is what Richard Maine warned about twenty years ago -- when Ada
generic packages had already been in use for fourteen years, and had
been part of the Ada requirements since before FORTRAN 77.

Let's do it using a parameterized procedure without a parameterized
module (the syntax extensions should be obvious):

  module Splines
    type :: Coeffs ( RK )
      integer, kind :: RK
      real, allocatable :: A(:), B(:), C(:), D(:)
    contains
      procedure :: Eval_D, Eval_S, ...
      generic :: Eval => Eval_D, Eval_S
    end type Coeffs
    integer, public, parameter :: DK = kind(0.0d0), SK => kind(0.0e0)
    procedure :: Eval_D => Eval ( dk )
    procedure :: Eval_S => Eval ( sk )
  contains
    subroutine (rk) Eval ( C, X, Y )
      integer :: RK
      class(coeffs(rk)), intent(in) :: C
      real(rk), intent(in) :: X
      real(rk), intent(out) :: Y
      ... (don't need Include "Eval_Body.f9h")
    end subroutine Eval
  end module Splines

Now I need single, double and Q-precision splines:

Assume I got only .o and .mod files from a vendor, so I don't have
source to upgrade.  So first, I need to extend Coeffs and define a
Q-precision evaluator:

  module Splines_Q
    use Splines, only: Coeffs, Eval
    type, extends(coeffs) :: Coeffs_Q
    contains
      procedure :: Eval_Q
      generic :: Eval => Eval_Q
    end type Coeffs_Q
    integer, public, parameter :: QK = selected_real_kind ( 1 + precision(1.0d0) )
    procedure :: Eval_Q => Eval ( qk )
  end module Splines_Q

  use Splines_Q, only: Coeffs_Q, DK, SK, QK
  type(coeffs_q(dk)) :: C_D
  type(coeffs_q(sk)) :: C_S
  type(coeffs_q(qk)) :: C_Q
  real(dk) :: Y_d
  real(sk) :: Y_S
  real(qk) :: Y_q
  call c_d%eval ( 1.5_dk, y_d )
  call c_s%eval ( 1.5_sk, y_s )
  call c_q%eval ( 1.5_qk, y_q )

Now let's do it using a parameterized module:

  module Splines ( RK )
    integer, public, kind :: RK ! Module parameter
    type :: Coeffs ! Doesn't have a type parameter
      real(rk), allocatable :: A(:), B(:), C(:), D(:)
    contains
      procedure Eval
    end type Coeffs
  contains
    subroutine Eval ( C, X, Y )
      class(coeffs), intent(in) :: C
      real(rk), intent(in) :: X
      real(rk), intent(out) :: Y
      ... (don't need Include "Eval_Body.f9h")
    end subroutine Eval
  end module Splines

Now I need single, double and Q-precision splines:

  use Splines ( kind(1.0d0) ), only: Coeffs_D => Coeffs, DK => RK
  use Splines ( kind(1.0e0) ), only: Coeffs_S => Coeffs, SK => RK
  use Splines ( selected_real_kind ( 1 + precision(1.0d0) ), only: Coeffs_Q => Coeffs, &
    & QK => RK

  type(coeffs_d) :: C_D
  type(coeffs_s) :: C_S
  type(coeffs_q) :: C_Q
  real(dk) :: Y_d
  real(sk) :: Y_s
  real(qk) :: Y_q
  call c_d%eval ( 1.5_dk, y_d )
  call c_s%eval ( 1.5_rk, y_s )
  call c_q%eval ( 1.5_qk, y_q )

This works, even if you don't have source code for the module.  It's
easy to construct a type and consistent related type-bound procedures.
You can't do that nearly so easily using parameterized procedures
without parameterized modules.  If you have a parameterized EVAL
procedure but you don't have the source for the Splines module, you need
to extend the type to add a binding for the Q-precision Eval subroutine.
It's not impossible, but it's ugly and requires more labor expense for
development and maintenance.

To make things a little bit smoother, we should allow declarations
before USE statements, so long as they depend only upon things declared
earlier (an already-ubiquitous requirement in the Fortran standard):

  integer, parameter :: DK = kind(0.0d0)
  integer, parameter :: SK = kind(0.0e0)
  integer, parameter :: QK = selected_real_kind ( 1 + precision(1.0d0) )

  use Splines ( dk ), only: Coeffs_D => Coeffs
  use Splines ( sk ), only: Coeffs_S => Coeffs
  use Splines ( qk ), only: Coeffs_Q => Coeffs

This was all laid out in much greater detail in 04-383r1, with many more
use case examples.


