Fortran FOSSプログラマ向けスタイルガイドの概要

# 概要
FORD (FORtran Documenter)などを作成している，Fortran F/OSS Programmers Groupのリポジトリに，[Fortran FOSSプログラマ向けのスタイルガイドライン](https://github.com/Fortran-FOSS-Programmers/Best_Practices)があったので，概要をまとめましました．
併せて著者の考えを注釈として記載しました．

# 言語標準への準拠

言語標準に準拠することは最重要だが，実用性の観点から下記のように妥協する．

- 言語標準には可能な限り準拠せよ
- ただし，実用性の観点から拡張が必要な場合は除く
- その場合でも，複数のコンパイラによって広くサポートされている拡張を優先する

## 標準に準拠できない理由の例

### 並列計算を行う場合
HPC分野で並列計算用のハードウェアを利用するには，ライブラリなどを用いる必要がある．特化した特定の手段よりも，OpenMPやOpenACC等が好まれる[^exploit_parallel_architectures]．

[^exploit_parallel_architectures]: Fortran標準として，`do concurrent`や`co-array`が追加されている．`do concurrent`で`reduction`がしたいとの要望も出されているが，OpenMPの機能をFortran標準で置き換えるのは，まだまだ先になるだろう．<br>特化した特定の手段とは，恐らくOpenACCに関係しており，CUDA FortranよりもOpenACCを使えとの事だと考えられる．一方で，nvfortranでは，`do concurrent`を自動でGPUにオフロードする機能が追加されており，言語標準に沿った上で並列計算用ハードウェアを利用する手段が整備されつつある．

### サードパーティーライブラリを利用する場合
OSレベルの操作や実行時エラーの処理などは，非標準ライブラリを用いた混合言語プログラミングによって解決されることが多い．このような場合は，`iso_c_bindig`を利用して，可能な限り標準に準拠して開発することを検討せよ[^consume_third_party_library]．

[^consume_third_party_library]: これは，例えば`!DEC$ attributes`のようなコンパイラ依存のディレクティブではなく，Fortran 2003の`bind(name=c)`を使えということであろう．

### プリプロセッサディレクティブ
Fortranには標準にプリプロセッサディレクティブがないので，プリプロセッサディレクティブを使用すると，厳密には言語標準に準拠しなくなる[^use_of_preprocessor_directives]．

プリプロセッサディレクティブは一例であるが，コードを非標準にする可能性のある他の機能にも気をつけること．

[^use_of_preprocessor_directives]: Fortran向けのプリプロセッサとしては，近年では[fypp](https://github.com/aradi/fypp)が注目されている．原文では[COCO](http://www.daniellnagle.com/coco.html)というプリプロセッサが紹介されていた．

# 命名規則

## 自身の意見を盛り込んだ命名規則を採用し，それを一貫して使用する

## 小文字を利用する
Fortranは大文字小文字を区別しない言語であるため，構文チェックで大文字と小文字は区別できない．しかし，キャメルケースは可読性向上の一助となる可能性がある．

Fortranのキーワードとユーザが定義した名前を区別するために，Fortranキーワードを全て大文字で書く規約が採用されていたが，現在広く利用されているエディタの多くは，構文強調機能を備えているので，Fortranのキーワードを他の名前と区別できる．

- 例外としてグローバルに置かれたパラメータがある

### 小文字のみを用いる場合は，スネークケースを利用する
その場合，アンダースコアの使い方を統一せよ．

- `temp_wall`や`temp_gas`という変数名を定義したなら，`tempfloor`や`floor_temp`ではなく`temp_floor`とせよ．

## 説明的で，かつ意味を持った変数名を付ける
現在のFortran（90以降）は，63文字までの長い名前が許可されている．意味のある名前を付ける検討し，名前の短縮に力を入れすぎないようにせよ．

同じ概念の処理を異なるオブジェクトに対して実行する手続きを開発する場合，手続きに意味のある説明的な名前を与えることで可読性を改善できる．

```Fortran
subroutine compute_transpose_of_sparse_approximate_matrix(...)

subroutine compute_transpose_of_dense_approximate_matrix(...)

subroutine compute_transpose_of_sparse_exact_matrix(...)

```

### 変数名の短縮に労力を使わない
空気の音速の定義を例にすると，いくつかの分野で使われている`a`[^variable_name_sound_of_speed]は，この暗黙の慣習に詳しくない人にとっては不明瞭である．

[^variable_name_sound_of_speed]: いくつかの分野では`c`が使われることもある．

最悪なのは，簡潔にするために`sos`という名前を採用することである．この変数は，音速の値なのか遭難信号なのか区別できない．`speed_of_sound`のような名前が明快で長さも適切である．

もし，複数の物質における音速が必要なら，`speed_of_sound(AIR), speed_of_sound(WATER)`のように，定数配列を設けて物質名をインデックスとして利用することを検討せよ[^index_for_material]．

[^index_for_material]: `AIR`や`WATER`という名前から，それらがインデックスであることを想像するのは困難である．列挙型を利用することを想定していると思われるが，その場合は`MaterialIndex_AIR, MaterialIndex_WATER`など接頭辞を用いた方がよりよいと考えている．あるいは，独自に`material_index`型を定義し，その成分として`air, water`を設けた上で，`material_index`型定数を用いた方がよいだろう．

#### 簡潔な方が可読性向上に繋がる場合は，名前の長さを制限してもよい
音速の変数が，気体を表す構成要素である場合を仮定する[^ratio_of_heat_capacities]．

```Fortran
type :: gas
  real :: gamma = 0.0
  real :: pressure = 0.0
  real :: speed_of_sound = 0.0
end type gas
```

`associate`構文を利用すると，変数名は簡単に短縮できる．

```Fortran
function density(low_pressure_gas)
  type(gas), intent(in) :: low_pressure_gas
  real                  :: density

  associate(gamma => low_pressure_gas%gamma,       &
            pressure => low_pressure_gas%pressure, &
            speed_of_sound => low_pressure_gas%speed_of_sound)
    density = gamma * pressure / (speed_of_sound**2)
  end associate
  return
end function density
```

[^ratio_of_heat_capacities]: 著者は，`gamma`ではなく`ratio_of_heat_capacities`あるいは`heat_capacity_ratio`を使うべきであると考えている．`associate`構文で一時的かつ局所的に名前を短縮するのであれば，プログラム全体に矛盾がない限り，`gamma`, `p`, `a`を使ってもよいと考えている．関数`gamma`を利用している場合は，命名規則の検討が必要である．

### 一文字の変数名は，配列インデックスやループカウンタとして利用する場合のみ許可
1文字変数`l`は，数字（リテラル）の`1`と区別が難しいので避けるべきであるが，エディタの構文強調機能があれば区別できる．`O`と`0`についても同様．

### マジックナンバーを利用せず，パラメータを利用する[^avoid_magic_numbers]

[^avoid_magic_numbers]: 例えば定数の`1`を直接書かない代わりに，定数`integer, parameter :: one = 1`を利用しても，何も解決していない．その`1`が何を指しているのかを説明する変数名にせよ．

### 論理変数には，それが伝えるべき状態を反映した名前を付ける
- `lib_init`よりも`lib_is_initialized`
- `obj_parent`よりも`obj_has_parent`

## エンティティ[^entity]を明確化する
モジュール名，派生型名，手続き名，変数名を明確化するための規則を採用する．

下記の例のように，モジュール名と派生型名（同一スコープ内の異なるエンティティ）に同じ名前を付けたい場合がある．しかし，これは許可されていないので，モジュール名と派生型両方に意味のある名前を選択する必要がある．

```Fortran
module shape_sphere
  type, public :: shape_sphere ! 許可されない
  end type shape_sphere
end module shape_sphere
```

変数名を付ける場合も，同様の曖昧さが発生する事がある．

```Fortran
type(shape_sphere) :: shape_sphere ! 許可されない
```

[^entity]: 現実世界におけるモノや概念のうち，計算機で取り扱うためにモデル化され，かつモデルが持つ情報によって一意に特定できる対象を意味する．ここでは，モジュール名，派生型名，手続き名，変数名を区別しろと言っている．

### 接頭辞，接尾辞を利用する
モジュールに接尾辞`_m`，派生型に接尾辞`_t`を利用する．

```Fortran
module shape_sphere_m
  type, public :: shape_sphere_t
  end type shape_sphere_t
end module shape_sphere_m
```

ライブラリやパッケージを構成する全てのモジュールに，共通の接頭辞（例えば`pkg_`）を付ける．

```Fortran
module pkg_shape_sphere
  type, public :: shape_sphere
  end type shape_sphere
end module pkg_shape_sphere
```

### 異なるスタイルを利用する
キャメルケース[^CamelCase]とスネークケースなど異なるスタイルを用いる．ただし，この方法では一つの単語からなるモジュール名（`Car`）と派生型名（`car`）は区別できない．

```Fortran
module shape_sphere ! モジュール名はスネークケース
  type, public :: ShapeSphere ! 派生型はキャメルケース
  end type ShapeSphere
end module shape_sphere
```

[^CamelCase]: 原文はキャメルケースだが，1文字目も大文字にするのはパスカルケースのはず．

### サブモジュールを利用する
派生型の型束縛手続きのAPI定義とその実装を分けるために`submodule`を利用すると，結果としてモジュール名と派生型名が区別できる．

```Fortran
module speaker_interface
  type :: speaker
  contains
    procedure, nopass :: speak
  end type speaker

  interface
    module subroutine speak
    end subroutine speak
  end interface
end module

submodule (speaker_interface) speaker_implementation
contains
  subroutine speak
    print "(A)", "Hello, world!"
  end subroutine speak
end submodule speaker_implementation
```

ここで，`_interface`はAPI定義の識別に使う接尾辞であり，接尾辞`_implementation`を付けたサブモジュールでの実装と区別される．派生型名は接頭辞や接尾辞がなくても十分に識別できる．

## 手続きを明確化する

### 型束縛手続きに派生型名を入れる

```Fortran
type :: object
  contains
    procedure :: stuff_1 => perform_stuff_1_on_object
    procedure :: stuff_2 => perform_stuff_2_on_object
    ...
end type object
```

### キャメルケース[^CamelCase]とスネークケースを利用して手続きを識別する

```Fortran
type :: gas
  real :: speed_of_sound ! 成分はスネークケース
  contains
    procedure :: ProjectOnCharacteristics ! 型束縛手続きはキャメルケース
end type gas
```

### 手続き名称には動作に関する動詞を含める
`subroutine`には動詞形を利用し，`function`には名詞形を利用するなど，`subroutine`と`function`の識別に動詞の有無が利用できる．

```Fortran
! 動詞形
subroutine get_useful_thing
end subroutine get_useful_thing
! 名詞形
function useful_thing
end function useful_thing
```
## ソースファイル名は，その中で定義されている主なエンティティの名前を反映する

# 常に`implicit none`文を利用する
暗黙の型宣言は，コードの開発を加速できるが，コードの理解やデバッグを困難にする．

`program`や`module`，`submodule`の最初には`implicit none`を書く．外部手続き（`program`，`module`，`submodule`内で定義されていない手続き）は，最初に`implicit none`を書く．

多くのコンパイラは，暗黙の型宣言を無効化する専用のオプションを提供しているので，その利用も検討する．

# 複雑よりも単純な方がよい
単純さは，可読性と保守性が高いコードを作成する鍵である．

オブジェクト指向プログラミングは，より一般的で複雑なタスクに対して保守性と再利用性を向上できる[^oop_is_tailored]だろうが，単純なタスクに対しては複雑なだけである．

[^oop_is_tailored]: オブジェクト指向プログラミングを利用すれば自動的に保守性と再利用性が向上するのではなく，そのように設計しなければならないという制約である．

## 可能な限り`goto`文の利用は避ける
ほとんどの場合，`goto`文は，構造的に安全な構文によって置き換えることができる．可読性と保守性を大幅に改善しながら性能劣化もない．しかし，非常に特殊で有用な場合は例外を認めるべきである．

`goto`はそれ自体が悪ではなく，カオス的なコードを開発するようプログラマを駆り立てる可能性があることが問題である．