FortranのGenerics (template)の現状

# 概要
Fortranのtemplateは，[先の記事](https://qiita.com/implicit_none/items/fae73e09f91930b91034)から結構変化していて，私の要望に近づいています．

# FortranのGenericsの現状
[先の記事](https://qiita.com/implicit_none/items/fae73e09f91930b91034)で，FortranのGenerics（主にtemplate）について紹介し，自身の要望を述べました．

その後，Genericsを策定するチームのメンバーがtemplateの現状について説明している[動画](https://www.youtube.com/watch?v=dOkeiAQGmIw)を紹介していただいたので，現状を改めて紹介まします．

## axpy
動画で説明されていたaxpyの例を紹介します．

axpyは，$$y = ax + y$$を計算する手続の総称です．二つのベクトル`x, y`を受け取り，`x`をスカラ倍（factorが`a`）した結果を`y`に加算します．Fortranでは，簡単に
```Fortran
subroutine axpy(a, x, y)
  real, intent(in) :: a
  real, intent(in) :: x(:)
  real, intent(inout) :: y(:)

  y = y + a*x
end subroutine
```
と書けます．これは単精度浮動小数点数に対するaxpyなので，single-precisionのsを付けてsaxpyと呼ばれます．単精度以外にも，倍精度浮動小数点数に対するaxpy（daxpy）もあります．
現在検討中のtemplateでは，saxpyとdaxpyはまとめて次のように書ける（ようになる）そうです．
```Fortran
template axpy_tmpl(k)
  private
  public :: axpy
  integer, constant :: k
  interface axpy
    procedure axpy_
  end interface
constants
  subroutine axpy_(a, x, y)
    real(k), intent(in) :: a
    real(k), intent(in) :: x(:)
    real(k), intent(inout) :: y(:)

    y = y + a*x
  end subroutine
end template
```
テンプレートをインスタンス化し，呼び出すには下記のようにします．
```Fortran
integer, parameter :: sp = kind(1.0), dp = kind(1.d0)
instantiate axpy_tmpl(sp)
instantiate axpy_tmpl(dp)
real(sp) :: a, x(10), y(10)
real(dp) :: da, dx(10), dy(10)

call axpy(a, x, y)
call axpy(da, dx, dy)
```

型の種別を表す定数`integer, constant :: k`は，parameterized derived typeであった`integer, kind`の拡張と見なせるでしょう．これは実数型の種別を切り替えるだけですが，`requirement`の要求が不要であること，インスタンス化した際に同じ名前が利用でき，引数の型（種別）で呼び出される手続が切り替えられていることは，非常に素晴らしいことだと思います．（`interface`宣言なしでそれが可能ならなお素晴らしいのですが…）

次に，任意の型を引数に取れるように発展させます．任意の型を引数に取り，axpyを計算するには，加算と乗算を行う演算子が定義されていなければなりません．任意の型に対応するには，先の記事で紹介した通りに`requirement`が必要なようです．また，演算を行う手続のインタフェースも必要です．

型パラメータ`T`は，これまでの`type T; end type`（空のユーザ定義派生型定義）から，`type, deferred :: T`という宣言に変わっています．`deferred`は，現状の仕様では遅延宣言とよばれるオブジェクト指向プログラミングの機能で，C++の`virtual`（仮想関数の定義）に対応しています．それを変数に拡張しようとしているのでしょう．これはよい拡張だと思います．

```Fortran
requirement bin_op(T, op)
  type, deferred :: T
  elemental function op(a, b)
    type(T), intent(in) :: a, b
    type(T) :: op
  end function
end requirement
```

関数テンプレートでは，任意の型に対する加算と乗算の抽象インタフェース（のようなものを）`requires`で記述します．

```Fortran
template axpy_tmpl(T, plus, times)
  private
  public :: axpy
  requires bin_op(T, plus)
  requires bin_op(T, times)
  interface axpy
    procedure axpy_
  end interface
constants
  subroutine axpy_(a, x, y)
    type(T), intent(in) :: a
    type(T), intent(in) :: x(:)
    type(T), intent(inout) :: y(:)

    y = plus(y, times(a, x))
  end subroutine
end template
```

これで，加算や演算が定義されていない型`T`が与えられたときの問題は排除できます．先の記事では，既に演算子が定義されている組込型に対しても，いちいち加算や乗算を書かなければならないなら総称名でよいと書きました．現状では，インスタンス化の際にオペレータは必要になるものの，既定のオペレータを渡すことでその問題を回避しているようです．
```Fortran
integer, parameter :: sp = kind(1.0), dp = kind(1.d0)
instantiate axpy_tmpl(real(sp), operator(+), operator(*))
instantiate axpy_tmpl(real(dp), operator(+), operator(*))
instantiate axpy_tmpl(integer, operator(+), operator(*))

real(sp) :: a, x(10), y(10)
real(dp) :: da, dx(10), dy(10)
integer :: ia, ix(10), iy(10)

call axpy(a, x, y)
call axpy(da, dx, dy)
call axpy(ia, ix, iy)
```

異なる型同士の演算については，上記の例を素直に拡張して，
```Fortran
requirement bin_op(T, U, V, op)
  type, deferred :: T, U, V
  elemental function op(a, b)
    type(T), intent(in) :: a
    type(U), intent(in) :: b
    type(V) :: op
  end function
end requirement

template axpy_tmpl(T, U, V, plus, times)
  private
  public :: axpy
  requires bin_op(T, U, V, plus)
  requires bin_op(T, U, V, times)
  interface axpy
    procedure axpy_
  end interface
constants
  subroutine axpy_(a, x, y)
    type(T), intent(in) :: a
    type(U), intent(in) :: x(:)
    type(V), intent(inout) :: y(:)

    y = plus(y, times(a, x))
  end subroutine
end template
```
とし，インスタンス化の際に適切な型を当てはめます．
```Fortran
integer, parameter :: sp = kind(1.0), dp = kind(1.d0)
instantiate axpy_tmpl(real(sp), integer, real(dp), operator(+), operator(*))

real(sp) :: a
real(dp) :: dy(10)
integer :: ix(10)

call axpy(a, ix, dy)
```
異なる型間の加算や乗算は定義していませんが，組込型であれば，Fortranに備わっている機能（暗黙の型変換）で実行できるということでしょう．

# 例に対する私の感想
先の記事と比較すると，私が希望していた内容に近くなってきていると思います．急にどうした…？

しかし，相変わらず記述量が多いのは気になります．`requirement`と`requires`，関数テンプレート内の`interface`等々．組込型の種別を変えるだけの例では`requirement`が必要ではなかったのですから，既定の組込型と演算に限って`requirement`が不要とかはできないものでしょうか．`type, intrinsic, deferred :: T`とかで識別自体はできる気がします．`character`や`logical`が入ってきたときのことを考えると，完璧ではないでしょうが…
`interface`は，インスタンス化した関数テンプレートに同じ名前を使うために必要なのだと推察します．`interface`を書くか別名を与えるかになるのでしょうか？

# まとめ
かなり良くなっていました．Fortranは記述量が多いため，なるべく記述を簡略化できる方向で改善していってもらえるとうれしいと思います．あとは，スカラ変数と配列，あるいは配列次元をどうテンプレート化できるようになるのか気になります．