C#のジェネリクスでできないこと

この記事は[C# Advent Calendar 2015](http://qiita.com/advent-calendar/2015/csharp)の5日目の記事です。

C#は、静的型付けのプログラミング言語で、ジェネリクスを持っています。ジェネリクスによって汎用性と型安全性を備えたコードを書けるのですが、それでも「C#のジェネリクスだとうまく書けないな」と思うようなケースもたまにあります。そんな例を2つほど紹介します。そして、他のプログラミング言語ではどうなのか、そしてC#ではこうする／こうしている、という解決法も書いてみます。

# ジェネリックなコードで四則演算

C#ではジェネリックなコードで算術演算子を呼べません。

![image1.png](https://qiita-image-store.s3.amazonaws.com/0/2079/aafeda87-5988-2860-9002-7c27eb1afadb.png)

これは、演算子は静的メソッドとして型ごとに実装されているため、呼び出したい演算子が型パラメータ`T`に定義されていることを要請する型制約を書く方法がない（だけでなく、コンパイル結果のILにもそのような制約を持たせる方法がない）からです。

## Haskellでは

型クラスというものがあって、ある型の値に適用できる関数のシグネチャを定義しておくことができます。むりやりC#っぽく言うなら、「静的メンバーだけを定義した特殊なインターフェース型のようなもの」です。

算術演算については、`Num`という型クラスがあって、`+`や`-`や`*`といった演算子のシグネチャが定義されています。ジェネリックな関数を書くときに、引数の型が `Num` 型クラスに属するという制約をつけると、引数に対して `+`や`-`や`*`といった演算子を適用できます（逆もできて、引数に対して `+`や`-`や`*`といった演算子を適用するコードを書くと、引数の型を推論する時に`Num`型クラスの制約がつきます）。そしてたとえば Haskellの整数型 `Int` は`Num` 型クラスに属していて、`+`や`-`や`*`といった演算子が実際に定義されているので、そのようなジェネリックな関数の引数として整数を渡すことができる、という次第です。

## F#では

F#では、インライン関数という、コンパイル時にインライン展開される関数を定義できます。そして、ジェネリックなインライン関数を定義する場合は、「静的に解決される型パラメーター」というおもしろい型パラメーターを利用できます。

静的に解決される型パラメーターには、「ある静的メンバーを持っていること」とか「ある演算子を適用できること」といった、C#のジェネリクスでは書けないような型制約をつけることができます（逆もできて、インライン関数の引数に対して +や-や*といった演算子を適用するコードを書くと、引数の型を推論する時に演算子の制約がつきます）。そしてそのような型制約を付けた関数を呼び出すF#コードを書くと、コンパイルするときに制約を満たしているかどうかチェックされます。

コンパイル結果のILにはそのような制約を制約として埋め込むことはできませんので、静的に解決される型パラメータを持った関数をライブラリに含めておいてC#などの言語から呼び出そうとしたときには、呼び出し側のコンパイル時に制約がチェックされることはありません（演算子の適用など、一部の処理は動的に解決され実行されますが、それ以外の処理、たとえばメンバーの呼び出しなどは実行時に例外がスローされます）。

## C#ではどうするか

C#でどうするかは、`<`演算子や`>`演算子のことを考えるとわかりやすいです。値の大小を比較できるような型は`IComparable<T>`というインターフェースを実装していたりしますよね。

つまるところ、普通に四則演算をメソッドして宣言するインターフェースを定義して、それぞれの型がそのインターフェースを実装するしかないということです。

もしC#にそのようなインターフェースがあったら……という仮定のコードを書いてみます。

```csharp
public interface IField<T>
{
    public T Add(T that);
    public T Subtract(T that);
    public T Multiply(T that);
    public T Devide(T that);
}

public struct Int32 : IField<Int32>
{
    public Int32 Add(Int32 that) => this + that;
    public Int32 Subtract(Int32 that) => this - that;
    public Int32 Multiply(Int32 that) => this * that;
    public Int32 Devide(Int32 that) => this / that;
}

public class Program
{
    public static T Double<T>(T x) where T : IField<T>
    {
        return x.Add(x);
    }
}
```

まあ、そうなるよな……という感じですね。

ただしこのコードだと、~~`Double<T>(T x)`のようなメソッドは`IField<T>`のメソッドを呼び出しますので、`Int32`のような値型はボックス化されてしまいます。それを避けるには、~~[^footnote1] `Int32`型が`IField<Int32>`を実装していないと意味がありません。`Int32`のような組み込み型にはそれを望めませんから、やはり`<`演算子や`>`演算子のことを真似して

```csharp
public class Arithmetic<T>
{
    public static Arithmetic<T> Default { get { ... } }
    public virtual T Add(T x, T y) { ... }
    public virtual T Subtract(T x, T y) { ... }
    public virtual T Multiply(T x, T y) { ... }
    public virtual T Devide(T x, T y) { ... }
}
```

みたいな型を作って、

```csharp
public class Program
{
    public static T Double<T>(T x)
    {
        return Arithmetic<T>.Default.Add(x, x);
    }
}
```

みたいに呼び出す感じになるでしょうか。そして、`Arithmetic<T>.Default`の初回呼び出し時に内部でコード生成する、という。（追記：[13日目の記事](http://qiita.com/leetmikeal/items/1c4407d8e3dec39a3331)で、コード生成による対応例が紹介されました。）

[^footnote1]: 元の記述は誤りでした。訂正します。こういうジェネリックメソッドに値型を渡した場合、値型のメソッドが呼び出されるわけですが、インターフェース型にボックス化したあとでメソッドが呼び出されるのではなく、値型のメソッドが直接呼び出されます。値型のメソッドを直接呼び出すコードはJITの最適化がかなり効きますので、パフォーマンスの劣化はありません。

ちなみにこの`Arithmetic<T>.Default`は、事実上、「型クラスをオブジェクトのインスタンスで代用する」形になっています。Scalaでは、暗黙的な引数や暗黙的な型変換などによって、ある型に関連付けられたオブジェクトのインスタンスを自動的に得る仕組みがあるため、より型クラスっぽいコードを書くことができます。

# 型引数に別の型引数を渡す

非同期メソッドの戻り値として使う`Task<T>`ですが、`Task<Task<TResult>>`という入れ子になったTaskを`Task<TResult>`に変換してくれる、[`Unwrap`という拡張メソッド](https://msdn.microsoft.com/ja-jp/library/dd781129.aspx)があります。（※ちなみにこのメソッドは普通のインスタンスメソッドとして実装することはできなくて、拡張メソッドとして実装するしかありません。おもしろいですね。）

ところで最近は、C#言語仕様の拡張はオープンに議論されていますが、その中に[任意の`Task`風の型を非同期メソッドの戻り値にできるようにする提案](http://ufcpp.net/blog/2015/12/asyncawaitpost/)というのがあります。これがもし通れば、ユーザーが独自に定義した型、たとえば`FakeTask<T>`とかいうクラスを、非同期メソッドの戻り値にしてasync/awaitできるようになるわけです。

さて、そうなれば`FakeTask<T>`型にも`Unwrap`拡張メソッドを用意しておきたいですよね。

```csharp
public static class FakeTaskExtensions
{
    public static FakeTask<TResult> Unwrap<TResult>(this FakeTask<FakeTask<TResult>> fakeTask) { ... }
}
```

で、究極的には、`Task<T>`や`FakeTask<T>`という個別の型じゃなくて、非同期メソッドの戻り値にできる任意の型で `Unwrap` できることを表現したくなります。

## Haskellでは

さっき上で紹介した型クラスを使って書いてみます。

```hs
class Task t where
   unwrap :: t (t a) -> t a
```

こんな感じで、「`Task`型クラスに属する任意の型`t`は型引数を1つとるジェネリックな型であり、`unwrap`関数は入れ子になった `t (t a)`型を受け取って`t a`型を返す」みたいな宣言をさらっと書けます。

## C#ではどうするか

C#には型クラスはないので、インターフェースを定義する方向で考えてみます。

```csharp
public interface ITask<T>
{
    ITask<TResult> Unwrap<TResult>(ITask<ITask<TResult>> task);
}
```
（※もとの`Unwrap`が拡張メソッドだったことを思い出してください。`this`が使えるなら引数はいらないのだけど、それではうまく書けないから引数を受け取っています。でもこの記事で扱いたいのはそこじゃないんです。）

こうみればまあ書けなくもない感じですが、入れ子になっているのはインターフェース `ITask<T>`ですし、戻り値の型も`ITask<T>`です。こいつらを実装型にできないもんでしょうか。

```csharp
public interface ITask<T>
{
    // コンパイルエラー
    TTask<TResult> Unwrap<TTask<>, TResult>(TTask<TTask<TResult>> task)
        where TTask<T> : ITask<T>;
}
```

型引数として受け取った`TTask<>`を、もうひとつの型引数`TResult`と組みあわせて、
引数の型`TTask<TTask<TResult>>`と戻り値の型`TTask<TResult>`を作り出しています。**もちろんこれはコンパイルできません**。

こういうときC#でたまに見かける解決法に、**コンパイラが特別扱いする**というものがあります。今回の例でいうと、もともと目指していたことは「非同期メソッドの戻り値にできる任意の型で `Unwrap` できる」だったわけですが、それをコンパイラが保証する格好になります。

ちょっと何を言っているかぴんと来ない感じでしょうか？では、架空の`Unwrap`ではなく、実際の例を出してみます。LINQのクエリ構文が使える条件は、`Select`メソッドと`SelectMany`メソッドが定義されていることです。

これを、

```csharp
public interface ILinqable<T>
{
    ILinqable<TResult> Select<TResult>(Func<T, TResult> f);
    ILinqable<TResult> SelctMany<TResult>(Func<T, IMoge<TResult>> f);
}
```
みたいなインターフェースを実装していること、という条件にしてしまうと、LINQを使うと戻り値の型が必ず`ILinqable<T>`になってしまいますし、そこを実装型にしたいと思うと、さっきの例みたいに「型引数として受け取った`TLinqable<>`」みたいなものが登場せざるを得なくなります。

しかし、クエリ構文をコンパイルするときに、コンパイラが`Select`メソッドや`SelectMany`メソッドの呼び出しに機械的に置き換えるのであれば、引数や戻り値の型は自由にできるわけです。（.NET Framework標準では`IEnumerable<T>`とその派生インターフェースしかないですが、違う型でもクエリ構文を使うことができます。）