クラス定義の引数名にx使うのは避けよう

問題が解決したので、記事のタイトルを変更。
「stackoverflowの原因がわからない」→「クラス定義の引数名にx使うのは避けよう」

##環境
dotnetcore:3.1.102
F# 4.7

##事象
ジェネリック型を確かめてみようと思って試しに実装してみたんだけど
なぜかstackoverflowになった。

```fsharp:ジェネリック型のテスト

type Gen<'a>(x: 'a) =
    override x.ToString() = sprintf "%A" x

Gen<int>(5) |> printfn "%A"

// => Stack overflow.
```

じゃあジェネリック型じゃなかったらどうなの？と思ってやってみたけど
やっぱりstackoverflow。

```fsharp:intで試す
type Gen(x: int) =
    override x.ToString() = sprintf "%A" x

Gen(5) |> printfn "%A"
```

##対応
自己識別子をxから別のに変える。

```fsharp

type Gen(x: int) =
    override this.ToString() = sprintf "%A" x

Gen(5) |> printfn "%A"
```


##原因
F#では自己識別子は何でもよいのだが、慣習的にxがつけられているケースが多い。
たまたま変数名をxにして、自己識別子もxとしてしまったがためになかなかミスに気づけなかった。

プログラマとしては、xはコンストラクタで定義してるGenのint型メンバーのxだと思ってる。

コンパイラとしては、xは自己識別子なので、末尾のxもクラスオブジェクトそのものだと解釈する。

```fsharp

type Gen(x: int) =
           //↓自己識別子なのでGen型       ↓自己識別子と同じxなのでGen型
    override x.ToString() = sprintf "%A" x
```

sprintfやprintfnはフォーマットとして"%A"が指定されると、toString()を呼び出そうとする。
その結果、以下の無限ループが起きて、stackoverflowになったと思われる。

1. x(Gen型)のtoString()を呼び出す
2. toString()でsprintf %A xが指定されたのでx(Gen型)のtoString()を呼び出す
3. 2の無限ループ



暗黙的コンストラクタに頼ってたから気づきにくかったけど
明示的にコンストラクタ書いたらどうなるんだ？と思って書いてみたら、ハッと気づいた。

```fsharp:明示的に書いてみる

type Gen =
    val x:int

    new(a) = {
        x = a
    }

    // ionideがsprintfに渡しているxをintじゃなくてGen型と認識している    
    // もしかしてコンパイラが自己識別子のxと勘違いしてる？
    override x.ToString() = sprintf "%A" x

Gen(5) |> printfn "%A"

// もちろんstack overflow.
```

```fsharp:自己識別子を変えてみる

type Gen =
    val x:int

    new(a) = {
        x = a
    }

    // これでxがGen型ではなくintとして解釈される
    override this.ToString() = sprintf "%A" this.x

Gen(5) |> printfn "%A"

// 5が出力される

```

別にsprintfとかprintfnの実装のせいではなかったw

```fsharp:ジェネリック版
type Gen<'a>(x: 'a) =
    override this.ToString() = sprintf "%A" x

Gen<int>(5) |> printfn "%A"

// 5が出力される
```

###教訓
クラスメソッド(コンストラクタ含む)の引数でx使うのは避けよう。
※もちろん自己識別子がx派でなければ避けなくてもいい

##以下は古い記事(記録として残しておく)

##仮説
たぶんsprintfかprintfnのどっちかの処理で無限再帰に
陥っているのではないかと思われるが原因はよくわからんまま。

ためしにToStringをオーバーライドせんかったらどうなるか？を見ると、こいつはエラーにならんかった。

組み合わせの問題？

```fsharp:これはエラーにならない
type Gen<'a>(x: 'a) =
    member x.print = printfn "%A" x
    // override x.ToString() = sprintf "%A" x

Gen<int>(5) |> sprintf "%A" |> printfn "%A"
```

##調査ログ
思いつく範囲でやってみたけど、頓挫。
原因をどうつきとめたらいいかわからないorz

MSDN見てみたけど、ほしい情報は特に書いてなかった。

[printfn](https://msdn.microsoft.com/visualfsharpdocs/conceptual/printf.printfn%5b%27t%5d-function-%5bfsharp%5d)

[sprintf](https://msdn.microsoft.com/visualfsharpdocs/conceptual/printf.sprintf%5b%27t%5d-function-%5bfsharp%5d)

[Fsharp.Core.printf.fs](https://github.com/fsharp/fsharp/blob/master/src/fsharp/FSharp.Core/printf.fs)かなとあたりをつけてコード追っかけてみたけど、今のF#力ではよくわからなかったｗ

最後のformatterの定義はどこにいるんやろ？
Githubの検索窓で検索かけても見つけられんかった。

```fsharp:おいかけた跡
[<CompiledName("PrintFormatLine")>]
let printfn format = fprintfn (!outWriter) format

[<CompiledName("PrintFormatLineToTextWriter")>]
let fprintfn (textWriter: TextWriter) format  = kfprintf (fun _ -> textWriter.WriteLine()) textWriter format

[<CompiledName("PrintFormatToTextWriterThen")>]
let kfprintf continuation textWriter format =
    doPrintf format (fun _ -> 
        TextWriterPrintfEnv(continuation, textWriter) :> PrintfEnv<_, _, _>
    )

type TextWriterPrintfEnv<'Result>(k, tw : IO.TextWriter) =
    inherit PrintfEnv<IO.TextWriter, unit, 'Result>(tw)
    override __.Finish() : 'Result = k()
    override __.Write(s : string) = tw.Write s
    override __.WriteT(()) = ()

let inline doPrintf fmt f = 
    let formatter, n = Cache<_, _, _, _>.Get fmt
    let env() = f(n)
    // こいつどこにおるんや…
    formatter env

```