2017-12-19 5 views
0

こんにちは私はf#のサンプルをhttps://fsharpforfunandprofit.com/posts/elevated-world-3/から のC#でビルドしています。Language-Ext、C#でアプリケーションを使用するには?

私のコードは、ノート

public class CustomerId : NewType<CustomerId, int> { public CustomerId(int id) : base(id) { } } 

    public class EmailAddress : NewType<EmailAddress, string> { public EmailAddress(string email) : base(email) { } } 

    public class Customer : Record<Customer> 
    { 
     public readonly CustomerId Id; 
     public readonly EmailAddress Email; 
     public Customer(CustomerId id, EmailAddress email) 
     { 
      Id = id; 
      Email = email; 
     } 
    } 

    public static class CustomerConstructor 
    { 
     public static Result<CustomerId> CreateCustomerId(int id) 
     { 
      if (id > 0) return new Result<CustomerId>.Success(new CustomerId(id)); 
      else return new Result<CustomerId>.Error(new[] { "invalid id" }); 
     } 

     public static Result<EmailAddress> CreateCustomerEmail(string email) 
     { 
      if (string.IsNullOrEmpty(email)) return new Result<EmailAddress>.Error(new[] { "empty email" }); 
      else if (!email.Contains("@")) return new Result<EmailAddress>.Error(new[] { "invalid email" }); 
      else return new Result<EmailAddress>.Success(new EmailAddress(email)); 
     } 
    } 

    public abstract class Result<A> 
    { 
     public class Success : Result<A> 
     { 
      public readonly A Value; 

      public Success(A value) 
      { 
       Value = value; 
      } 
     } 

     public class Error : Result<A> 
     { 
      public readonly Arr<string> Errors; 

      public Error(IEnumerable<string> errors) 
      { 
       Errors = errors.ToArr(); 
      } 
     } 

    } 

public static class ResultModule 
{ 
    public static UnitTest1.Result<A> Return<A>(this UnitTest1.Result<A> self, A a) 
    { 
     return new UnitTest1.Result<A>.Success(a); 

    } 

    public static UnitTest1.Result<A> Return<A>(A a) 
    { 
     return new UnitTest1.Result<A>.Success(a); 

    } 

    public static UnitTest1.Result<B> Select<A, B>(this UnitTest1.Result<A> self, Func<A, B> map) 
     => Map<A, B>(self, map); 

    public static UnitTest1.Result<B> Map<A, B>(this UnitTest1.Result<A> self, Func<A, B> map) 
    { 
     if (self is UnitTest1.Result<A>.Success) 
     { 
      var sx = (UnitTest1.Result<A>.Success)self; 
      return new UnitTest1.Result<B>.Success(map(sx.Value)); 
     } 
     else 
     { 
      var er = (UnitTest1.Result<A>.Error)self; 
      return new UnitTest1.Result<B>.Error(er.Errors); 
     } 
    } 

    public static UnitTest1.Result<B> ApplyMine<A, B>(this UnitTest1.Result<A> self, UnitTest1.Result<Func<A, B>> apply) 
    { 
     if (apply is UnitTest1.Result<Func<A, B>>.Success && self is UnitTest1.Result<A>.Success) 
     { 
      var f = (UnitTest1.Result<Func<A, B>>.Success)apply; 
      var x = (UnitTest1.Result<A>.Success)self; 

      return new UnitTest1.Result<B>.Success(f.Value(x.Value)); 
     } 

     if (apply is UnitTest1.Result<Func<A, B>>.Error && self is UnitTest1.Result<A>.Success) 
     { 
      var f = (UnitTest1.Result<Func<A, B>>.Error)apply; 
      return new UnitTest1.Result<B>.Error(f.Errors); 
     } 

     if (apply is UnitTest1.Result<Func<A, B>>.Success && self is UnitTest1.Result<A>.Error) 
     { 
      var x = (UnitTest1.Result<A>.Error)self; 
      return new UnitTest1.Result<B>.Error(x.Errors); 
     } 

     if (apply is UnitTest1.Result<Func<A, B>>.Error && self is UnitTest1.Result<A>.Error) 
     { 
      var f = (UnitTest1.Result<Func<A, B>>.Error)apply; 
      var x = (UnitTest1.Result<A>.Error)self; 
      return new UnitTest1.Result<B>.Error(f.Errors.Concat(x.Errors)); 
     } 

     return default(UnitTest1.Result<B>);//fn should never hit here 
    } 

    public static UnitTest1.Result<B> Bind<A, B>(this UnitTest1.Result<A> self, Func<A, UnitTest1.Result<B>> bind) 
    { 
     if (self is UnitTest1.Result<A>.Success) 
     { 
      var sx = (UnitTest1.Result<A>.Success)self; 
      return bind(sx.Value); 
     } 
     else 
     { 
      var er = (UnitTest1.Result<A>.Error)self; 
      return new UnitTest1.Result<B>.Error(er.Errors); 
     } 
    } 

    public static UnitTest1.Result<C> SelectMany<A, B, C>(this UnitTest1.Result<A> self, Func<A, UnitTest1.Result<B>> bind, Func<A, B, C> project) 
    { 
     var bound = Bind<A, B>(self, bind); 
     if (bound is UnitTest1.Result<B>.Success) 
     { 
      var sxA = (UnitTest1.Result<A>.Success)self; 
      var sxB = (UnitTest1.Result<B>.Success)bound; 
      return new UnitTest1.Result<C>.Success(project(sxA.Value, sxB.Value)); 

     } 
     else 
     { 
      var er = (UnitTest1.Result<A>.Error)self; 
      return new UnitTest1.Result<C>.Error(er.Errors); 
     } 
    } 
} 

、以下の通りである:私のテスト上記のコードではUnitTest1は、名前空間が追加される(LanguageExtで結果の型があるとして)

以下の通りです
[TestMethod] 
public void TestApplicativeValidation() 
{ 
    var goodId = 1; 
    var badId = 0; 
    var goodEmail = "[email protected]"; 
    var badEmail = "example.com"; 

    Func<CustomerId, EmailAddress, Customer> createCustomer = (id, email) => new Customer(id, email); 
    var idResult = CustomerConstructor.CreateCustomerId(goodId); 
    var emailResult = CustomerConstructor.CreateCustomerEmail(goodEmail); 
    var createCustomer1 = ResultModule.Return(createCustomer); 

    //ResultModule.ApplyMine(idResult,) 




} 

[TestMethod] 
public void TestMonadaicValidation() 
{ 
    var goodId = 1; 
    var badId = 0; 
    var goodEmail = "[email protected]"; 
    var badEmail = "example.com"; 

    var goodCust = from id in CustomerConstructor.CreateCustomerId(goodId) 
        from email in CustomerConstructor.CreateCustomerEmail(goodEmail) 
        select new Customer(id, email); 

    var badCust = from id in CustomerConstructor.CreateCustomerId(badId) 
        from email in CustomerConstructor.CreateCustomerEmail(badEmail) 
        select new Customer(id, email); 

} 

Monadiacテストが実行され、そのすべてが見つかりましたが、リンクのようにアプリケーションシナリオを確認するためのテストを書くことができません。

let (<!>) = Result.map 
let (<*>) = Result.apply 

// applicative version 
let createCustomerResultA id email = 
    let idResult = createCustomerId id 
    let emailResult = createEmailAddress email 
    createCustomer <!> idResult <*> emailResult 
// int -> string -> Result<CustomerInfo> 

私はここでいくつかの洞察を述べることができますが、私たちは自動的にselect/selectを多用するlinq式を持っています。

+0

あなたに素晴らしい答えを与えるために、あなたがまだ質問していない場合は、あなたに一目惚れすると助けになるかもしれません。あなたが[mcve]を提供できるなら、それは役に立つかもしれません。 – Mat

+0

申し訳ありませんマット、その質問は、コードの作業を得るために欲求不満の時間後に投稿されました!問題は、TestApplicativeValidation()メソッドにどのコードを入れて動作させるかということです。あなたが見たように、私はMonadの法則を満たすResultクラスを作成しましたが、私はApplyMine関数の使い方が不明でした。 –

答えて

1

この例を実装するには、language-extでValidationタイプをお探しです。私はあなたのためにすべての仕事をするつもりはありませんが、one of the units testsを見てください。具体的には、Validationタイプの応用行動を使用しています。

language-extのコアタイプのほとんどは、アプリケーションの動作をサポートしていますthrough the apply function

+0

親愛なるLouthyさん、ポイントはちょうど私がアプリケーションのためにできることは何ですか?あなたのタイムリーな応答に感謝します。 –

+0

LINQは相互に依存しているため、アプリケーションの動作には使用できません。したがって、「xからyへ、yから... select x + y」は、「y」が「x」に依存し、「select」が「x」および「y」に依存することを意味する。出願人は本質的にすべて「並行」で計算する。すなわち、それらは独立した値である。したがって、LINQはファンクタとモナドの動作にのみ使用できます。 'Validation'では、タプルを使ってやや簡単に作業できます(上記のユニットテストリンクの158行目を見てください)。しかし、非Validation型の場合は、 'apply(op、left1、apply(op、left2、apply(op、right)))')を使用する必要があります。 – louthster

0

上記のApplyMine関数の正しい実装が見つかりました。以下は申請者のテストケースです。

[TestMethod] 
     public void TestApplicativeValidation() 
     { 
      var goodId = 1; 
      var badId = 0; 
      var goodEmail = "[email protected]"; 
      var badEmail = "example.com"; 

      Func<CustomerId, EmailAddress, Customer> createCustomer = (id, email) => new Customer(id, email); 

      /* 
      var idResult = CustomerConstructor.CreateCustomerId(goodId); 
      var emailResult = CustomerConstructor.CreateCustomerEmail(goodEmail); 
      var goodCustomer = idResult.Lift2(emailResult, createCustomer); 
      */ 

      var good = CustomerConstructor.CreateCustomerId(goodId).Lift2(CustomerConstructor.CreateCustomerEmail(goodEmail), createCustomer); 

      var bad22 = CustomerConstructor.CreateCustomerId(badId).Lift2(CustomerConstructor.CreateCustomerEmail(badEmail), createCustomer); 

      var bad1 = CustomerConstructor.CreateCustomerId(goodId).Lift2(CustomerConstructor.CreateCustomerEmail(badEmail), createCustomer); 

      var bad2 = CustomerConstructor.CreateCustomerId(badId).Lift2(CustomerConstructor.CreateCustomerEmail(goodEmail), createCustomer); 


     } 

拡張クラス/モジュールに追加されたLift2の実装です。 monadiacスタイルのためとして、我々はLINQ

var goodCust = from id in CustomerConstructor.CreateCustomerId(goodId) 
          from email in CustomerConstructor.CreateCustomerEmail(goodEmail) 
          select new Customer(id, email); 

を持って応用的なスタイルのプログラミングのための式はありませんCSHARPで

public static UnitTest1.Result<C> Lift2<A, B, C>(this UnitTest1.Result<A> self, UnitTest1.Result<B> other, Func<A, B, C> lift2) 
     { 
      Func<A, Func<B, C>> lifter = a => b => lift2(a, b); 

      var aBakedIn = self.ApplyMine(ResultModule.Return(lifter)); 
      return other.ApplyMine(aBakedIn); 
     } 

、それは2つのモナドと2パラメータ機能を明示的にlift2コールにより簡潔になります。言語extと同様にPreluteもそうです。私は、関数構造を使用するときには、「ドット表記法」をシャープにすることにしました。

この記事は脳波を覚えていたときに救助されました! http://adit.io/posts/2013-04-17-functors,_applicatives,_and_monads_in_pictures.html

+0

http://adit.io/posts/2013-04-17-functors、 _applicatives、_and_monads_in_pictures.html –

+0

実際の回答のみ* [回答を返信する]ボタンを使用してください。追加情報を追加するには、元の質問を変更する必要があります。 – Mat

関連する問題