2017-12-19 9 views
0

안녕하세요, 저는 # # https://fsharpforfunandprofit.com/posts/elevated-world-3/ 에서 f # 샘플을 빌드 중입니다.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); 
     } 
    } 
} 

주, 다음과 같습니다 : 내 테스트 위의 코드에 대한

(LanguageExt의 결과 유형이있는 한) UnitTest1이 추가 된 네임 스페이스 다음과 같습니다

[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> 

어떤 통찰력을 줄 수 있습니까? 선택/선택을 자동으로 사용하는 linq식이 있습니다. 적용 스타일의 경우에는 무엇을해야합니까?

+0

아직 답을 찾지 못했다면 큰 도움이 될 것입니다. [mcve]를 제공 할 수 있다면 유용 할 수도 있습니다. – Mat

+0

죄송합니다 매트, 그 문제는 코드 작업을 얻으려면 좌절 후 게시되었습니다! 문제는 TestApplicativeValidation() 메소드에 어떤 코드를 넣어 작동시켜야하는지입니다. Monad의 법칙을 만족하는 Result 클래스를 만들었는데, ApplyMine 함수를 사용하는 방법이 확실하지 않았습니다. –

답변

1

예제를 구현하려면 Validation 유형을 language-ext로 찾으십시오. 모든 작업을 수행하지는 않겠지 만 유형의 응용 프로그램 동작을 사용하는 실제 예가있는 one of the units tests을 살펴볼 수 있습니다.

language-ext의 대부분의 코어 유형은 응용 프로그램 동작 through the apply function을 지원합니다.

+0

친애하는 Louthy, 요점은 (m ...에서) 사용하는 모나드의 linq 표현식과 같았습니다. 우리가 지원자를 위해 할 수있는 일이 있습니까? 적시 대응에 감사드립니다. –

+0

LINQ는 용어가 LINQ에서 서로 종속되어 있기 때문에 적용 가능한 동작에 사용할 수 없습니다. 그래서'x in ... from y in ... select x + y '는'y'가'x'에 의존하고'select'가'x'와'y'에 의존한다는 것을 의미합니다. 신청자는 본질적으로 모두 '평행하게'계산합니다. 즉, 독립 값입니다. 따라서 LINQ는 functor 및 모나드 동작에만 사용할 수 있습니다. 'Validation'을 위해 튜플을 사용하여 작업하기가 약간 더 쉬워졌습니다 (위의 유닛 테스트 링크의 158 행을보십시오).그러나 유효성 검사 유형이 아닌 경우 '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); 

이 곳

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); 
     } 

이 CSHARP에서 실용적 스타일의 프로그래밍에 대한 표현이 없다, 그것은보다 간결이 모나드와 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

실제 답변을 위해서는 * Post answer * 버튼을 사용하십시오. 추가 정보를 추가하려면 원래 질문을 수정해야합니다. – Mat