C#のジェネリクスで型クラスを真似る
id:Nagise さんのJavaジェネリクス記事を読みながら、C#ではどうかな、とあれこれ考えているわけです。
- 型に属する情報をジェネリックに扱う試み ... のC#版
- ジェネリクス型変数と型階層 ... のC#版
- 再帰的ジェネリクスの代入互換性 ... のC#版
- ジェネリクスの基礎とクラス設計への応用 ... のC#版
- Javaによる高階型変数の実装 ... のC#版(序)
- Javaによる高階型変数の実装 ... のC#版(本文)
そんな時に、ジェネリクスのカリー化 - プログラマーの脳みその話を振られて。
真の高階型引数、つまり、型パラメータを取る型パラメータは実現できないです。が、型クラス的なものであればエミュレートできるらしい。
もちろんCLRにもILにも型クラス的なものなんかないわけで、オダスキー教授が言うところの「貧者の型クラス(pdf)」を使う、つまり型に適合するメソッド群をインスタンスとして渡してやるしか方法はないわけですが(ちなみにScalaでは、そのインスタンスを暗黙的に生成したり渡したりできる仕掛けがある)。
というわけで、最初に書いたのがこっちなんだけど…
https://gist.github.com/matarillo/11392331
(追記)ん?型クラス(もどきインスタンス)、new()
制約をつけられるんじゃないの?
ということは?
public static class MonadEx { public static Value<TMonad, TResult> SelectMany<TMonad, TSource, TCollection, TResult>( this Value<TMonad, TSource> source, Func<TSource, Value<TMonad, TCollection>> collectionSelector, Func<TSource, TCollection, TResult> resultSelector) where TMonad : Monad<TMonad>, new() { var monad = new TMonad(); return monad.Bind(source, x=> monad.Bind(collectionSelector(x), y=> monad.Unit(resultSelector(x,y)))); } }
まさか?
internal class Program { private static void Main(string[] args) { var m = from x in Maybe.Instance.Unit(12) from y in Maybe.Instance.Unit(34) select x + y; var message = m.HasValue() ? m.Value().ToString() : "Nothing"; Console.WriteLine(message); } }
いけるじゃないか!!
(追記)new制約つけてnewしましたが、よーく考えると、Valueクラスの中に型クラスもどきインスタンスを持つようにするとか、あるいはタプルで型クラスインスタンスとValueクラスのインスタンスをまとめて扱うようにする方法もありますね。そしたら型クラスもどきインスタンスを使いたい側でnewしなくても、Valueと一緒に受け取れるはず。