將 Haskell 翻譯為 Rust, C# (上)標準庫

這次承接上篇,是關於 C# 的。 Rust 的 algebraic isomorphism 也寫好了,歡迎大家嘗試。

進度

  • algebraic isomorphism 已支持 Rust, C#.
  • isomorphism 已支持 C#

前情提要

  • 講了 Rust Java Dart

前文曾經說過,

某些有類 型 擦 除的語言要儘可能避免類型擦除,除非推導不了了。

但是 C# 沒有類型擦除,所以你必須把類型全都寫出來( Java 的有時候就不用,因為被轉換為 Object 類型了)

先 bb 兩句

一個 Rust 的坑

Rust 有個問題,它目前還沒有穩定支持的 FnBox 。也就是說,不能在返回 capture 了 closure 的 closure。 也就是說,上面給的這個例子, Rust 是做不到寫出等價代碼的(用 FnBox 可以,但是這個東西只有 Nightly 的編譯器支持)。

對此,我的臨時解決方案是:

  1. 參數使用(反正就是傳入的東西) Box<Fn(_) -> _> 類型,返回值使用(反正就是傳出的東西) Box<FnOnce(_) -> _> 類型。這樣就可以安全的返回了(這其實是個關於 GC 的問題,編譯器擔心在返回 closure 的時候 closure 裡面 capture 的變數會被銷毀,那麼這樣的調用就是不安全的,而當 closure capture 了 closure 後這個關係就更複雜了),後果是返回的這個 Box<FnOnce(_) -> _> 不能調用
  2. test case 裡面對返回的 closure 進行 type check 即可,不直接調用,後面會講這個

庫的實現(接上)

C#

C# 不得不說是一門好語言,它的語言層面上的缺點我覺得是比 Java 少的(換言之,這是一門比 Java 更優秀的語言), 而且 Java 有的那種 Lambda 字面量在語法設計上的優點 C# 也具備。這裡給出一個完整的對比:

Java:

return iso(ab -> a -> b -> ab.apply(a, b));

Kotlin:

return iso { ab -> { a -> { ab(a, it) } } }

Dart:

return iso((ab) => (a) => (b) => ab(a, b));

C#:

return Iso(ab => a => b => ab(a, b));

Rust:

// 很抱歉,寫不出來。

而 C# 有一個讓我很難受的地方是,標準的大括弧寫法是這樣的:

public static string CSharpSucks(){return "Yes you"re right, I"m sorry, I"m so sorry";}

這樣就憑空多了很多很多行代碼,非常不利於閱讀。我只是能適應這樣的寫法。畢竟不好看歸不好看, 寫任何語言都應該聽取官方的意見,這樣才會寫出最地道的代碼。

你可能會說,C# 有一個語法糖:

public static string CSharpSucks() => "Yes you"re right, I"m sorry, I"m so sorry";

很遺憾, CodeWars 不支持這個版本的 C# 。

而且, C# 也沒有匿名內部類。

Either

public abstract class Either<L, R>{ public abstract T Match<T>(Func<L, T> lt, Func<R, T> rt); public abstract override bool Equals(object obj); public static Either<L, R> Left(L l) { return new _Left<L, R>(l); } public static Either<L, R> Right(R r) { return new _Right<L, R>(r); } public class _Left<L, R> : Either<L, R> { private readonly L l; public _Left(L l) { this.l = l; } public override T Match<T>(Func<L, T> lt, Func<R, T> rt) { return lt(l); } public override bool Equals(object rhs) { return rhs is Either<L, R> && ((Either<L, R>) rhs).Match(arg => l.Equals(arg), rr => false); } } public class _Right<L, R> : Either<L, R> { private readonly R r; public _Right(R r) { this.r = r; } public override T Match<T>(Func<L, T> lt, Func<R, T> rt) { return rt(r); } public override bool Equals(object rhs) { return rhs is Either<L, R> && ((Either<L, R>) rhs).Match(ll => false, arg => r.Equals(arg)); } }}

Maybe

public abstract class Optional<T>{ public static Optional<T> From(T obj) { return new Some<T>(obj); } public static Optional<T> Empty() { return new None<T>(); } public abstract Optional<R> Map<R>(Func<T, R> f); public abstract Optional<R> FlatMap<R>(Func<T, Optional<R>> f); public abstract T Get(); public abstract T OrElseGet(Func<T> f); public abstract bool IsPresent(); public abstract override bool Equals(object obj); public class Some<T> : Optional<T> { private readonly T _obj; public Some(T obj) { _obj = obj; } public override Optional<R> Map<R>(Func<T, R> f) { return new Some<R>(f(_obj)); } public override Optional<R> FlatMap<R>(Func<T, Optional<R>> f) { return f(_obj); } public override bool Equals(object other) { return other is Some<T> && Equals(_obj, ((Some<T>) other)._obj); } public override T Get() { return _obj; } public override T OrElseGet(Func<T> f) { return _obj; } public override bool IsPresent() { return true; } } public class None<T> : Optional<T> { public override Optional<R> Map<R>(Func<T, R> f) { return new None<R>(); } public override Optional<R> FlatMap<R>(Func<T, Optional<R>> f) { return new None<R>(); } /// don"t change this public override T Get() { throw new Exception("cannot get from null"); } /// <summary> /// don"t change this /// </summary> /// <param name="obj">other object</param> /// <returns>is equaled</returns> public override bool Equals(object obj) { return null != obj && obj.GetType().Name.Equals("None`1"); } public override T OrElseGet(Func<T> f) { return f(); } public override bool IsPresent() { return false; } }}

Tuple

標準庫。

Unit

public class Unit{ private Unit() { } public static readonly Unit INSTANCE = new Unit();}

Void

public abstract class Void{ public abstract A Absurd<A>();}

標準庫裡面那個還是要不得(而且也不是 Bottom type)。

最後推薦一篇我認為寫的很好的關於 Haskell 的博客,是娘泉聚聚的,給我帶來了很大的幫助。

你學到了什麼

  • C# 沒有匿名內部類
  • Rust 暫時不支持 Box<FnOnce(_) -> _>
  • C# 沒有匿名內部類
  • C# 沒有類型擦除,所以你必須把類型全都寫出來

為什麼我要把 C# 單獨提出來說

因為<del>太長了</del>這個是最後完成的。

推薦閱讀:

Haskell有多少跟State/Reference有關的東西?
Rust相較於Haskell有何優勢?

TAG:C# | Rust编程语言 | Haskell |