Fortranで(a==1 && a==2 && a==3)を真にする

# 概要
一部で話題になっているStackOverFlowの記事，[Can (a ==1 && a== 2 && a==3) ever evaluate to true?](https://stackoverflow.com/questions/48270127/can-a-1-a-2-a-3-ever-evaluate-to-trueJavaScript)をFortranでもやってみました．比較演算子のオーバーロードを使うので，aの値に影響されて常に真になるわけではありませんが，Fortranのオブジェクト指向プログラミング機能の紹介にもなると思い，投稿しました．

# 背景
2018年になって早々に，StackOverFlowの[記事](https://stackoverflow.com/questions/48270127/can-a-1-a-2-a-3-ever-evaluate-to-trueJavaScript)が注目を集めていました．内容は，
> JavaScriptで(a==1 && a==2 && a==3)を常に真にすることは可能か？と面接で質問されたのだが，そんなコードは書いたことがない．だが興味はある．

という内容でした．様々な回答が付いており，みんな色々と考えるもんだと感心しました．
実際のところ，このようなコードは動作が直感に反するので書くべきではないのでしょうが，当該プログラミング言語やその関連の知識，あるいは技術レベルを確認することはできると思います．良問かどうかは別ですが．

この程度なら，Fortranでもできそうですし，Fortranのオブジェクト指向プログラミングの紹介にもよいかと思ったのでやってみました．Fortranのオブジェクト指向プログラミングに関しては，拙著[^1]をご覧ください．
[^1]: 暗黙の型宣言編著，Fortranによる実践オブジェクト指向プログラミング，2017.（好評につき増刷中）

# 実装
実装にはIntel Parallel Studio XE 2017 Update 4 Composer Edition for Fortranを使いました．他のFortranコンパイラでは再現できない可能性がありますので，注意してください．

Fortranでオブジェクト指向プログラミングができることは全世界の人が知っている事実ではありますが，正直なところあまり複雑な事はできません．そこで，新しく型を作り，比較演算子をオーバーロードすることにしました．

## 派生型StrangeInt
取り扱う数値は整数なのに，1にも2にも3にも等しいということで奇妙な整数型という名前を付けました．派生型を定義する前に，モジュールを作ります．

```Fortran
module class_StrangeInt
    implicit none
    private
end module class_StrangeInt
```
暗黙の型宣言を無効にするために`implicit none`を書き，モジュール内全体を`private`とします．その後，派生型StrangeIntを定義していきます．

### 派生型の定義

```Fortran
    type,public :: StrangeInt
        integer :: value

        contains

        procedure,private,pass :: Equals
        generic :: operator (==) => Equals
    end type StrangeInt
```
最低限必要そうなのは

- 整数型変数
- 比較演算子をオーバーロードする関数

です．それぞれ

- integer型変数value
- 型束縛手続Equals

として設けました．`generic :: operator (==) => Equals`は，関係演算子`==`をオーバーロードし，StrangeInt型変数に対して比較演算子が作用すると，型束縛手続`Equals`が呼び出されることを意味しています．

### 型束縛手続Equalsの実装
型束縛手続を実装します．派生型の定義と手続の実装を区切るために`contains`が必要です．

```Fortran
    type,public :: StrangeInt
        !省略
    end type StrangeInt

    contains

    function Equals(this,criterion) result(isEqual)
        implicit none
        class(StrangeInt)  :: this
        integer,intent(in) :: criterion

        logical :: isEqual

        isEqual = (this%value == criterion) !右辺との比較結果をlogical型で返す
        this%value = this%value + 1 !派生型成分に1を足す
    end function Equals
```

仮引数`this`は当該実体仮引数と呼ばれ，C++やJavaのthis，Pythonのselfに相当します．criterionは`==`の右辺値が渡されます．それらを比較して真ならlogical型の`True`，偽なら`False`を戻り値用の変数isEqualに代入します．最後に成分valueを+1することで，`(a==1 .and. a==2 .and. a==3)`と連続で呼ばれたときに値が1,2,3と変化していくことを狙いました．

## 修正
一見良さそうですが，これはコンパイルが通りません．演算子をオーバーロードする手続では，当該実体仮引数（this，すなわちオブジェクト）は明示的に`intent(in)`を指定しなければならないからです．どういうことかというと，オブジェクトを使って演算（演算・代入・比較含む）をしたときに，演算に使ったオブジェクトが変更されることを防止するために，読込専用である事を強制しています．まさに，本記事の`(a==1 .and. a==2 .and. a==3)`のような挙動を防止するためです．

Fortranのオブジェクト指向プログラミングの規格が一応まともであることが確認できましたが，これでは本記事の目的を達成できません．しかし，Fortranのオブジェクト指向機能には抜け穴があって，この読込専用の強制を回避することができます．

### 値を更新する手続の追加
手続`Equals`中でオブジェクトの成分を更新するのは上記の理由によりできません．そこで，値に1を足す型束縛手続を新しく作り，`Equals`の中から呼ぶようにします．

```Fortran
    type,public :: StrangeInt
        !省略
        procedure,private,pass :: update
    end type StrangeInt

    contains

    function Equals(this,criterion) result(isEqual)
        !省略
        call this%update()
    end function Equals
    
    subroutine update(this)
        implicit none
        class(StrangeInt) :: this

        this%value = this%value + 1
    end subroutine update
```

このとき，新しく作った手続`update`の仮引数を`class(StrangeInt),intent(inout)`とするとコンパイルエラーが出ます．`Equals`の中で読込専用の変数`this`を，`update`の中で読み書き可能として扱っていることをコンパイラが見抜きます．ところが，`intent`を指定しないとあっさりとコンパイルが通ってしまいます[^2]．
[^2]: 他のFortranコンパイラではコンパイルが通らないかもしれません．

派生型StrangeIntは最終的に次のようになりました．

```Fortran:class_StrangeInt.f90
module class_StrangeInt
    implicit none
    private
    
    type,public :: StrangeInt
        integer :: value

        contains

        procedure,private,pass :: Equals
        procedure,private,pass :: update
        generic :: operator (==) => Equals
    end type StrangeInt

    contains

    function Equals(this,criterion) result(isEqual)
        implicit none
        class(StrangeInt),intent(in) :: this
        integer,intent(in) :: criterion

        logical :: isEqual

        isEqual = (this%value == criterion)
        call this%update()
    end function Equals
    
    subroutine update(this)
        implicit none
        class(StrangeInt) :: this

        this%value = this%value + 1
    end subroutine update

end module class_StrangeInt
```

# 動作テスト
実装したStrangeIntが想定通りに動作するかテストします．メインルーチンの中でモジュールをuseし，StrangeIntを使ってみます．繰り返しますが，暗黙の型宣言は必ず無効にします．

```Fortran
program main
    use class_StrangeInt
    implicit none

    type(StrangeInt) :: a
    
    a = StrangeInt(1)
    print *,a%value
    
    if(a==1)then
        print *,"TRUE"
        print *,a%value
    end if
end program main
```
`type(StrangeInt) :: a`でStrangeInt型の変数`a`を宣言し，続く`a = StrangeInt(1)`ではFortranのbuilt-inコンストラクタ[^3]で派生型成分`value`に1を代入しています．
[^3]: 値の代入のみ可能で，いわゆるコンストラクタのように任意の動作を記述することはできません．
実行結果は

```console
          1
TRUE
          2
```
となり，`a==1`の比較の後で派生型成分`value`の値が1増えています．想定通りに動作しているようなので，本記事の狙いである`(a==1 .and. a==2 .and. a==3)`が真になるかを試してみました．

```Fortran:main.f90
program main
    use class_StrangeInt
    implicit none

    type(StrangeInt) :: a
    
    a = StrangeInt(1)
    if(a==1 .and. a==2 .and. a==3)then
        print *,"TRUE"
    end if
end program main
```

結果は

```console
 TRUE
```

と表示され，`(a==1 .and. a==2 .and. a==3)`を真にすることができました．

# まとめ
`(a==1 .and. a==2 .and. a==3)`が真になるようなプログラムをFortranで実装しました．Fortranのオブジェクト指向プログラミングの機能を使い，比較演算子`==`をオーバーロードしました．実装の過程で，Fortranのオブジェクト指向プログラミングの規格が一応まともであることが確認できました．
一方で，コンパイラが原因なのか規格に穴があるのかわかりませんが，規格で禁止している動作をさせることができました．今後，コンパイラのバージョンアップでこのような動作ができなくなる可能性がありますし，そもそもIntel Fortranコンパイラ以外ではできない可能性もありますが，旬のネタを利用してFortranのオブジェクト指向プログラミングがそこそこまともである事[^4]は紹介できたと思います．
[^4]: まともでない側面のインパクトの方が大きいような気もしますが･･･