2014-12-10 5 views
2

나는 C#에서 Maybe 모나드의 장난감 구현을 가지고 있고 관련 SelectMany 확장 메서드를 구현하여 Linq와 함께 작동합니다. IEnumerable과 IMaybe를 하나의 Linq 문에 섞으려고 할 때 문제가 발생했습니다. 내 프로그램이 파일을 읽을 URI 항목의 숫자로 컨텐츠를 구문 분석을 시도하고, 각 항목에 대한 URI에서 콘텐츠를 다운로드 단일 Linq 표현식에서 모나드 구문을 혼합하는 방법은 무엇입니까?

public interface IMaybe<T> 
{ 
    bool HasValue { get; } 
    T Value { get; } 
} 

public static class Maybe 
{ 
    class SomeImpl<T>: IMaybe<T> // obvious implementation snipped for brevity 
    class NoneImpl<T>: IMaybe<T> // obvious implementation snipped for brevity 

    // methods to construct the Maybe monad 
    public static Wrap<T> Some<T>(T value); 
    public static Wrap<T> Some<T>(T? value) where T: struct; 
    public static IMaybe<T> None<T>(); 

    public static IMaybe<B> SelectMany<A, B>(this IMaybe<A> a, Func<A, IMaybe<B>> mapFn) 
    { 
     if (a.HasValue) 
      return mapFn(a.Value); 
     else 
      return None<B>(); 
    } 

    public static IMaybe<C> SelectMany<A, B, C>(
     this IMaybe<A> a, Func<A, IMaybe<B>> mapFn, Func<A, B, C> selector) 
    { 
     if (a.HasValue) 
     { 
      var b = mapFn(a.Value); 
      if (b.HasValue) 
       return Some(selector(a.Value, b.Value)); 
      else 
       return None<C>(); 
     } 
     else 
      return None<C>(); 
    } 
} 

처럼

어쩌면 모나드 보인다. 이러한 작업이 정확히 어떻게 구현되는지는 관련이 없습니다. 내가 가지고있는 문제는 Linq 문에서 이러한 작업을 연결하는 데 있습니다. 나는.

static IMaybe<string> ReadFile(string path); 
    static IMaybe<KeyValuePair<string, Uri>[]> ParseEntries(string input); 
    static IMaybe<string> Download(Uri location); 

    static void Main(string[] args) 
    { 
     var result = // IEnumerable<IMaybe<anonymous type of {Key, Content}>> 
      from fileContent in ReadFile(args[0]) 
      from entries  in ParseEntries(fileContent) 
      from entry  in entries     // this line won't compile 
      from download in Download(entry.Value) 
      select new { Key = entry.Key, Content = download }; 

     // rest of program snipped off for brevity 
    } 

오류는 IMaybe와 IEnumerable 모나드를 혼합하는 것에 대해 불평합니다. 정확한 표현에서 :

오류 1 유형의 표현 'System.Collections.Generic.KeyValuePair은 []'소스 유형 'MonadicSharp.IMaybe'와 쿼리 식의 절에서 다음에서 허용되지 않습니다. 'SelectMany'호출에서 형식 유추가 실패했습니다. C : \ Dev \ Local \ MonadicSharp \ MonadicSharp \ Program.cs 142 31 MonadicSharp

어떻게이 문제를 해결할 수 있습니까?

+0

아마도 여기서는 내 지식의 한계를 맞고 있습니다. 나는 모나드 변압기와 하스켈에 관해 읽으 러 돌아갈 것이다. 나는 단서를 얻을 때이 질문에 대해 더 언급 할 것이다. – Ben

+0

약간의 힌트 -'IMaybe '을 피하고 간단히 'Maybe '의 구체적인 유형을 지정하면됩니다. 많은 것들이'IEnumerable '을 구현할 수 있지만,'IMaybe '의 여러 구현에 대한 필요성을 생각할 수 없다. 너는 할수 있니? – Enigmativity

답변

0

약간의 연구 끝에 나는 하나의 LINQ 문에 모나드를 섞는 것이 불가능하다고 결론을 내 렸습니다. 그래서 나는 그것을 두 문장으로 나누기로 결정했습니다.

public interface IMaybe<out T>{ ... } 

다음, 나는에 IMaybe 모나드를 변환하는 일부 도우미 방법이 필요합니다

우선, 내가 IMaybe 인터페이스 선언에 약간의 변화가 공분산을 사용할 수 있도록해야합니다 이것은 어떻게 작동 IEnumerable을 모나드 :

public static IEnumerable<IMaybe<T>> UnfoldAll<T>(
    this IMaybe<IEnumerable<T>> source) 
{ 
    if (source.HasValue) 
     return Enumerable.Range(0, 1).Select(i => Maybe.None<T>()); 
    else 
     return source.Value.Select(value => Maybe.Some(value)); 
} 

그리고 마지막으로, 나는 두 개의 문으로 원래 LINQ 문을 깰 것이다

static void Main(string[] args) 
{ 
    var path = args[0]; 
    var theEntries = 
     from fileContent in ReadFile(path) 
     from entries in ParseEntries(fileContent) 
     select entries; 

    var theContents = 
     from entry in theEntries.UnfoldAll() 
     where entry.HasValue 
     select Download(entry.Value.Value); 

    foreach (var content in theContents) 
    { 
     //... 
    } 
} 
(LINQ 식을 중첩 너무 작동)

첫 번째 LINQ 문은 IMaybe 모나드에서 작동하고 두 번째 LINQ 문은 IEnumerable에서 작동합니다.

0

entriesIMaybe<T> 유형이고 IEnumerable<T>이 아니기 때문에 문제가 있다고 생각합니다.
는이 같은 시도 유무 : 물론

from entry  in entries.Value 

이것은 모나드의 목적이 무엇인지되지 않습니다, 그러나 이것은 첫 번째 단계되어야한다.

+0

항목은 KeyValuePair [] 유형이므로이 항목은 컴파일되지 않습니다. 클로저는 SelectMany 확장 메서드에 의해 도입되었으며 이미 "언 래핑"되어 있음을 기억하십시오. 진짜 문제는 그렇게하는 의미가 잘 정의되어 있지 않기 때문에 모나드를 섞을 일반적인 방법이 없다는 것입니다. – Ben

1

문제는 ParseEntries의 서명에있는 것으로 보입니다.

그것은 현재 : 아마도

static IMaybe<KeyValuePair<string, Uri>[]> ParseEntries(string input); 

가되어야 하는가?

static IMaybe<KeyValuePair<string, Uri>>[] ParseEntries(string input); 

어쩌면 어쩌면 배열 일 수도 있습니다.

+0

이것은 내 솔루션에 매우 가깝습니다. ParseEntries의 기본 의미는 입력이 특정 문법을 따르는 경우 입력 배열을 반환하거나 그렇지 않으면 아무 것도 반환하지 않으므로 메서드의 서명이 그대로 유지됩니다. 보시다시피 IMaybe를 IMaybe []으로 변환하는 또 다른 방법을 도입하기로 결정했습니다. 이것은 작업 []을 작업 으로 변환하는 Task.WhenAll의 역순과 유사합니다. 제안 해 주셔서 감사합니다. – Ben

관련 문제