自由モノイドプログラミング言語の作成(MonIter C#スタイル版 バージョン1.2)
このプログラミング言語はもともと自由モノイドを無限個の積に拡張したものをプログラミング言語の実装から定義しようとしたものなのですが、
- 有限個と無限個の積については定義できるのですがこれはもともと定義できているとも言えます。
- 無限個と無限個の積については
- 後の方は無視されると考えても良いし
- 定義はされるが後の方は実行されないと考えても良いし
- 同時に実行できるシステムであれば同時に実行されると考えても良い
ということで定義する方法は実装からは何とも言えません。
しかしこのシステムが他の用途にも使えそうなので続けていきます。C# の構文解析ができる「Roslyn」の「Syntax API」(構文解析の概要 (Roslyn API) - C# | Microsoft Learn)を使ってみます。このシステムでは今のところ不要なのですが、関数のパラメーターの型を指定したいとき、C# の構文を使って指定できるようにします。
フィボナッチ数列の例
以下のように C# の構文で書けるようにします。以下のように してMonIter.FreeMonoid<int> のかわりに mon と書けるようにします。
using mon = MonIter.FreeMonoid<int>;
引数で渡すバージョン
mon fib(int x, int y) => x & fib(y, x + y);
Zip を使うバージョン
mon fib(int x, int y) => x & y & zipsum(fib(x, y), tail(fib(x, y)));
このコードは C# では遅延評価が行われないので実行すると戻ってきません。この件について ChatGPT で調べてみました。
- C# で yield return はどこで使うことができますか → 戻り値の型が以下のものであるメソッド
- IEnumerable
- IEnumerable<T>
- IEnumerator
- IEnumerator<T>
- 戻り値の型を IEnumerable<T> の派生クラスにすることはできますか → できない
- LINQ の Concat を使った場合は遅延評価になりますか → 遅延評価される
- IEnumerable<T> を継承したクラスに Concat と同様のメソッドを定義することはできますか → 戻り値の型が IEnumerable<T> であるものは定義できる
- ConcatWith の戻り値の型を MyCollection<T> にすることはできますか → 戻り値の型を MyCollection<T> にすると遅延評価は行われない
- なぜ遅延評価は行われないのですか → yield return を使用したメソッドの戻り値は IEnumerable<T> または IEnumerator<T> に限定されるという制約があるため
- 遅延評価を行うために C# の仕様を変更するならば、どのような方法がありますか → yield return をカスタム型でも使用可能にすることで可能
ということで遅延評価ができても良さそうなのですが C# の仕様なのでできません。なぜこのような仕様なのかは理由はあると思うのですが理由はわかりません。
自由モノイド(無限版)のクラス
上記の例のコードが書けるように少し修正しました。
internal class FreeMonoid<T> : IEnumerable<T> { private IEnumerable<T> elements; public FreeMonoid(IEnumerable<T> enumobj) { elements = enumobj; } public IEnumerator<T> GetEnumerator() { return elements.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } public static FreeMonoid<T> Collect(IEnumerable<FreeMonoid<T>> ms) { IEnumerable<T> CollectEnum(IEnumerable<FreeMonoid<T>> ms) { foreach (FreeMonoid<T> m in ms) { foreach (T e in m) { yield return e; } } } return new FreeMonoid<T>(CollectEnum(ms)); } public static FreeMonoid<T> operator &(FreeMonoid<T> mon1, FreeMonoid<T> mon2) { return new FreeMonoid<T>(mon1.Concat(mon2)); } public static FreeMonoid<T> operator &(T n1, FreeMonoid<T> mon2) { return new FreeMonoid<T>(FreeMonoid<T>.Unit(n1).Concat(mon2)); } public static FreeMonoid<T> Unit(T element) { return new FreeMonoid<T>(new List<T> { element }); } public static FreeMonoid<T> Zero { get { return new FreeMonoid<T>(new List<T> { }); } } public FreeMonoid<T> ZipWith(FreeMonoid<T> mon2, Func<T, T, T> func) { return new FreeMonoid<T>(this.Zip(mon2, func)); } public FreeMonoid<T> Tail() { return new FreeMonoid<T>(this.Skip(1)); } public FreeMonoid<U> Map<U>(Func<T, U> func) { return new FreeMonoid<U>(this.Select(func)); } }