int - > int list与int类型不兼容 - > IEnumerable的<'一>

时间:2014-04-26 20:44:22

标签: c# .net linq f#

假设:

open System.Linq

这是一个可接受的表达方式:

[2; 3; 4].SelectMany(fun n -> { 1..n })
然而,这不是:

[2; 3; 4].SelectMany(fun n -> [ 1..n ])

错误消息显示:

    int -> int list    
is not compatible with type
    int -> System.Collections.Generic.IEnumerable<'a>

F#拒绝该表达式,因为该函数返回int list

考虑这个类似的C#程序:

using System;
using System.Collections.Generic;
using System.Linq;

namespace SelectManyCs
{
    class Program
    {
        static List<int> Iota(int n)
        {
            var ls = new List<int>();

            for (var i = 0; i < n; i++) ls.Add(i);

            return ls;
        }

        static void Main(string[] args)
        {
            var seq = new List<int>() { 2, 3, 4 };

            foreach (var elt in seq.SelectMany(Iota))
                Console.WriteLine(elt);
        }
    }
}

Iota返回List<int>并将其传递给SelectMany是C#可接受的。

是设计的F#行为还是bug?如果按照设计,为什么类似的操作在C#中起作用?

2 个答案:

答案 0 :(得分:9)

这是预期的行为。 C#通常在类型之间进行比F#更多的自动转换:

  • 在C#中,lambda函数的结果是List<T>,但C#编译器会自动将结果转换为IEnumerable<T>,这是函数的预期返回类型。

  • 在F#中,编译器不会自动将结果转换为IEnumerable<T>,因此您的第二个代码段不会键入check - 因为它返回的list<T>与预期的类型不同IEnumerable<T>(您可以将列表转换为可枚举,但它们是不同的类型)。

F#库定义了自己的SelectMany操作版本,名为Seq.collect。 F#函数具有以下类型:

> Seq.collect;;
val it : (('a -> #seq<'c>) -> seq<'a> -> seq<'c>) 

此处,类型#seq<'c>明确表示结果可以是任何可以转换为seq<'c>的类型(这就是名称中#的含义)。这就是answer to your previous question有效的原因:

[2; 3; 4] |> Seq.collect (fun n -> [ 1..n ])

答案 1 :(得分:4)

F#int list与C#List<int>非常不同。如果要使用C#中的类型,F#list是不可变的(在创建列表后无法更改)。另一方面,在第一个(工作)示例中,您将返回seq<int>,它与IEnumerable<int>基本相同。 F#list也实现了IEnumerable,但F#中的接口工作方式略有不同:如果要将某个参数作为特定接口传递,则需要将其显式转换为该接口实例。这意味着,这也可行:

let y = 
    [2; 3; 4].SelectMany(fun n -> [ 1..n ] :> IEnumerable<int>)

或者,可以使用list将F#IEnumerable<>转换为Seq.ofList

[2; 3; 4].SelectMany(fun n -> (Seq.ofList [ 1..n ]))

另一个区别是F#中的显式接口: 有关从F#列表转换为System.Collections.Generic.List<>的其他详细信息,请参阅here