Go1.18でジェネリクスが追加されたので、5年ぶりにGolangを覚えなおし、GenericListとマルチキャスト可能Channelの実装をした

# はじめに
Go1.18が2022/3/15にリリースされました

以前Goの開発陣はジェネリクスは不要とか言ってたような気がするが

1.17でもお試しが出来たと思いますが
今回正式に導入されたので、5年ぶりにGoのお勉強です

ジェネリクス以外に5年間のUpdateを調べましたがツールやライブラリは変わったものの
言語じたいはたいした変化はなかったので
ジェネリクスだけです

# ジェネリクスとは
C++でいうテンプレートです
D言語、Java、C#、Haskell・・・
今では様々な言語に導入されている概念です
引数の型が異なる関数やメソッド、メンバ、クラスに対して、一つの実装で複数の型に対応出来ます

具体的には、例えばint型の入るリストクラス IntListを作ったとします
float型のリストも必要となったのでコピペして FloatListクラスを作成
string型のリストも欲しいから StringListクラスを作成
自作したMyClassを保存する MyClassListを作成・・・
メンテできませんね。。

ジェネリクス例
List<T>  というクラスを作成し、メソッドを実装する
List<int> hoge;  と変数を定義した時に、int型のListクラスが生成される
同じく List<float>  List<string>  List<MyClass> ...と任意の型のListクラスを自動生成出来る

## 言語による実装の違い
- C++、Rust等
コンパイル時にそれぞれの型のコードを自動生成する
型が明確に定まるため実行速度は最も速い
コンパイル時間が長くなり、実行ファイルが大きくなる

- Java等
コンパイル時は型をチェックするが、ILレベルではObject型である（後方互換性）
List<int>もList<MyClass>もILでは List<Object>と同じため、実行ファイルはあまり増えない
常にキャストが行われるため、実行速度は遅い
また、全て同じObject型なので、リフレクションで型を取れない

- C#等
ILでGenericsに対応した命令セットが追加されている
別の型として扱われるため、キャストも発生せずリフレクションでもちゃんと型が取れる
ILでうまく吸収するため実行ファイルもあまり増えない


Goの実装がどうなっているか調査不足ですが、コンパイル言語なのでC++実装と近いと予想しています


## おまけ、動的型付け言語
型によるコードの自動拡張なので、型がない言語では基本的にGenericsは使われない

静的型付け言語でも、ジェネリクスを使わず、JavaでいうObject型のような
何でも入る基底オブジェクトにすれば、ジェネリクスを使わずに全ての型に対応できる
ただし、型を制限できないので使い勝手は異なる


# Goのジェネリクス
## 文法
文法は他の言語と似ているが、最も異なるのが多くの言語では *<>* で表記されるが、 *[]* で表記する事
配列と同じ記号なので、配列オブジェクトをテンプレート引数にしたりすると、読みにくい（なんで<>じゃないの？）

それ以外は他の言語とだいたい同じで、非常に素直
また、型制約も可能で、interfaceに使用可能な型を記述する
interfaceにはunion構文が追加され、型制約にかぎり複数の型のunionが使える
~をつかいunderlying typeも制約に追加出来る
制約に関して、言語ライブラリによく使うものがまとめられている(Ordered等)

など・・
詳細は他の人の記事をみるか、リクエストあれば作ります・・

```go
type Number interface { 
    ~int | ~int32 | ~int64 | ~float32 | ~float64
}

func Add[T Number] (a, b T) T{
    return a+b
}


result := Add[int](1, 2)    // [int]で型を明示しているが、型推論されるので省略可能

```

# そんなわけでジェネリックなListを作った
リポジトリは一番下においときます。。。

といっても、言語ライブラリの container/listをジェネリクスにしただけで
練習にもなってませんが
Goのジェネリクスが簡単だよ！ 今までのコードを簡単にジェネリクス対応できるよ！
っていう事で

変更点
具体的には、ListとElementという structがあるので、それらをany制約（つまり制約なし）のジェネリクスにします

```go
// これを
type Element struct {
type List struct {

// こうするだけ
type Element[T any] struct {
type List[T any] struct {

```

そして、ListやElement型を使っていたものをすべて List[T]、Element[T]に置換する

```go
// こういうのを片っ端から
	next, prev *Element
	list *List
func (e *Element) Next() *Element {


// [T]つけるだけじゃ
	next, prev *Element[T]
	list *List[T]
func (e *Element[T]) Next() *Element[T] {
```

そして今回はコンテナの中身、ValueメンバもT型になります
またNewという公開関数も[T any]になる
その他 anyが出てくる部分はTに置き変えるべきな場合が多いのでそれらをすべて置き換える

```go
// このへんのanyは
	Value any
func (l *List) insertValue(v any, at *Element) *Element {
func New() *List { return new(List[T]).Init() }

// Tになるんじゃ！
	Value T
func (l *List[T]) insertValue(v T, at *Element) *Element[T] {
func New[T any]() *List[T] { return new(List[T]).Init() }

```

これでcontainer/listはジェネリクス対応になりました


試す

```go
	l := list.New[int]()
	l.PushBack(1)
	l2 := l.PushBack(2)
	l.PushBack(3)
	l.Remove(l2)

	for e := l.Front(); e != nil; e = e.Next() {
		println(e.Value)
	}

// 結果
1
3 
```

もちろんfloatやstringでも正しく機能する


# マルチキャストチャンネルの作成
上記のジェネリクス対応Listを使って何か遊ぼうと思って、いいものが思いつかなかったので
以前からGoで不満を持っていた、複数チャンネルへのマルチキャストを実装しようと思う

Goroutineは基本的にはchannelによるメッセージパッシング方式を好む
もちろんグローバルでフラグをもち共有メモリ・・も実装可能であるが
channelを使う方が良いだろう

例えば、複数のコネクションにたいしGoroutineを作成し、親の終了時に子を終了させるなら
終了用チャンネルを作り、Closeして子がそれをselectで受ければ実装できるが
チャンネルをCloseしたくない場合
例えば複数人とのチャットサーバを作り、すべてのコネクションにメッセージを送る場合等
1つのchannelにメッセージを送ると、ブロードキャストしてくれるようなもの
欲しい時がある

goでは知る限り、上記を満たすものがないため作った

## モチベーション
https://github.com/SierraSoftworks/multicast

こちらの実装がたいへん参考になったが、少し不満点があった
上記の実装は非常にシンプルで良い実装だが
シンプルなリンクリストを作成し、親チャンネルからメッセージが来たら
リンクリストをたどり、結果登録した全てのListenerに対して親からきたメッセージを送信している

しかし不満点および疑問点があった

不満点は、任意のListenerを削除できない点
双方向リンクリストにすれば削除も可能と思われるが、それならListを内包する方が良くないか？と思った

また疑問点は、ListenerごとにGoroutineを作っているが
このGoroutineは
メッセージを受け取った場合 リンクリストがあれば、次の子にメッセージを伝播し、自分のチャンネルにメッセージを投げる
以下、リンクリストをたどり、上記Goroutineが走る
つまり、親がメッセージを受け取った場合
第一子にメッセージを投げる、第一子は第二子にメッセージを投げ、自分のメッセージを処理する
・・・
と、子の数をnとすると、 2n+1個のチャンネルを作り、それらに伝播し、最終的にn個のチャンネルに伝播される
無駄が多くね？？？

あとは、チャンネルに送るメッセージがジェネリクスではないので、すべてinterfacfe{}である。
これをジェネリクスにして型安全にしたい（もちろん anyにする事もできる）

## 設計方針
上記のジェネリックのListを使い、Listenerを追加削除が自由に行えるようにする
また、子への伝播用のchannelを生成せず、親がListを使い全ての子にたいしてメッセージを送るようにする
つまりchannelの伝播を 2n+1からn+1に減らし、Goroutineもnから1に減らす


## 型の定義


```go
type Listener[T any] struct {
	C chan T
	E *list.Element[*Listener[T]]
}

func (s *Listener[T]) Close() {
	close(s.C)
}

type Channel[T any] struct {
	C chan<- T
	c chan T
	l *list.List[*Listener[T]]
	m sync.Mutex
}
```

リンクリストは不要。かわりに l *list.List[*Listener[T]] と、Listenerのリストを保持する。
ただしGo言語ではデストラクタが存在しないため・・・
このListの中身を自由にRemoveされると、Closeされず困った事になる・・・辛い・・・

ので現在は、ChannelのPublicメソッドとしてAddとRemoveを用意している・・・辛い・・・

	C chan<- T
	c chan T

ここは、参考の実装からそのままいただいた。非常に素晴らしい実装だと思う
Cとcのメンバ変数は、後のNewで説明するが、同じチャンネルであるが
公開用のCは、入力専用であるが、内部メンバのcは子に伝播させるように出力用になっている
非常にスマートだ！

そしてNew

```go

func New[T any]() *Channel[T] {
	c := make(chan T)
	l := list.New[*Listener[T]]()

	go func() {
		for v := range c {
			if l != nil {
				for e := l.Front(); e != nil; e = e.Next() {
					e.Value.C <- v
				}
			}
		}

		if l != nil {
			for e := l.Front(); e != nil; e = e.Next() {
				e.Value.Close()
			}
		}
	}()

	return &Channel[T]{
		C: c,
		c: c,
		l: l,
	}
}
```

ほぼすべてを物語っている
先ほど話したように、Cとcには同じチャンネル（ブロードキャストするためのインタフェース）を指定しているが
送信用、受信用と使い分けている。すばらしい

そしてListenerを保存するためのListを作成
受信をし、リストから子に伝播させるためのGoroutineを起動

後始末として、ListenerのチャンネルをCloseしている（デストラクターがない・・・辛い・・・



その他のメソッド

```go

func (c *Channel[T]) Add() *Listener[T] {
	c.m.Lock()
	defer c.m.Unlock()

	l := &Listener[T]{C: make(chan T, 0)}
	e := c.l.PushBack(l)
	l.E = e

	return l
}

func (c *Channel[T]) Remove(l *Listener[T]) {
	c.m.Lock()
	defer c.m.Unlock()

	c.l.Remove(l.E)
	l.Close()
}

func (c *Channel[T]) Close() {
	c.m.Lock()
	defer c.m.Unlock()

	close(c.c)
}
```

ListenerをAddしたりRemoveしたりするその他メソッドだ
コンストラクタやデストラクタさえあれば、Listをそのまま出せるかもしれないが
Goにはコンストラクタもデストラクタも存在しないので・・・辛い・・・
こうやって、インタフェースを別途作らないといけないのか・・

もしかしたらListクラスをダックタイピングで派生させれば出来るのかもしれない
そのあたりは、私のGoの知見の低さゆえ・・今はこの実装で勘弁してください

## 使い方

実際にはWaitGroupを使ったり、selectで終了をまったり、必要な作業はありますが
大雑把に下記のイメージで

```go

	c := multichannel.New[interface{}]()

	for i := 0; i < 3; i++ {
		go func(c *multichannel.Channel[interface{}]) {
            c.Add()
            ....
        }(c)
	}

	c.C <- "1"
	time.Sleep(time.Second)

	c.C <- "2"
	c.Remove(ll)
	time.Sleep(time.Second)

	c.C <- "3"
	time.Sleep(time.Second)
	c.C <- "4"

	c.Close()

```

このへんは、詳しくは 下記のmain.goを見て下さい（リファクタリングしてないけど）

むしろWorkingGroupの使い方の方がややこしい（こっちもライブラリ作ろうかなあ・・・）

# 参考コードのリポジトリ

https://github.com/YukiMiyatake/HereWeGo/tree/Qiita1

モジュール化して、go getで使おうとしたけど上手くモジュールとして動かない・・
githubでのモジュール化について勉強しなきゃ・・