Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/259.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/linq/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何在单个Linq表达式中混合一元结构?_C#_Linq_Functional Programming_Monads - Fatal编程技术网

C# 如何在单个Linq表达式中混合一元结构?

C# 如何在单个Linq表达式中混合一元结构?,c#,linq,functional-programming,monads,C#,Linq,Functional Programming,Monads,我有一个C中Maybe monad的玩具实现,并实现了与Linq相关的许多扩展方法。当我试图在一个Linq语句中混合使用IEnumerable和IMaybe时,我偶然发现了一个问题 也许monad看起来像 public interface IMaybe<T> { bool HasValue { get; } T Value { get; } } public static class Maybe { class SomeImpl<T>: IMay

我有一个C中Maybe monad的玩具实现,并实现了与Linq相关的许多扩展方法。当我试图在一个Linq语句中混合使用IEnumerable和IMaybe时,我偶然发现了一个问题

也许monad看起来像

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>();
    }
}
我的程序尝试读取一个文件,将内容解析为多个URI条目,并为每个条目从URI下载内容。这些操作的具体实现方式无关紧要。我的问题在于将这些操作链接到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源类型为“MonadicSharp.IMaybe”的查询表达式的后续from子句中不允许使用类型为“System.Collections.Generic.KeyValuePair[]”的表达式。调用“SelectMany”时类型推断失败。C:\Dev\Local\MonadicSharp\MonadicSharp\Program.cs 142 31 MonadicSharp


我该如何解决这个问题?

我认为问题在于条目的类型是IMaybe,而不是IEnumerable。 你有没有试过这样的方法:

from entry       in entries.Value

当然,这不是Monad的目的,但这应该是第一步。

在我看来,问题在于ParseEntries的签名

目前是:

static IMaybe<KeyValuePair<string, Uri>[]> ParseEntries(string input);
也许应该是这样

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

因此,与其说是maybe of数组,不如说是maybe数组。

经过一些研究,我得出结论,在一个LINQ语句中混合单子是不可能的,所以我决定将它分成两个语句。这就是它的工作原理:

首先,我需要对IMaybe接口声明稍作修改,以使用协方差:

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

如您所见,第一条LINQ语句适用于IMaybe monad,第二条适用于IEnumerable。

好吧,也许我在这里只是达到了知识的极限。我要回去读关于monad transformer和Haskell的书。当我得到一个线索时,我会进一步评论这个问题。只是一个小小的提示-我会通过避免IMaybe来简化,并且只会有一个具体类型的Maybe。很多东西都可以实现IEnumerable,这是有道理的,但我想不出需要多个IMaybe实现。可以吗?条目的类型为KeyValuePair[],因此无法编译。请记住,闭包是由SelectMany扩展方法引入的,并且已经展开。真正的问题是没有通用的方法来混合单子,因为这样做的语义没有很好的定义。这与我的解决方案非常接近。ParseEntries的基本语义是,如果输入符合特定语法,则返回一个条目数组,否则不返回任何内容,因此方法的签名仍然有效。如您所见,我决定引入另一种方法将IMaybe转换为IMaybe[]。这类似于Task.WhenAll的反面,它将Task[]转换为Task。谢谢你的建议。
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));
}
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)
    {
        //...
    }
}