2014-04-26 2 views
4

Дано:Int -> Int список не совместим с типом Int -> IEnumerable <'a>

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 # по дизайну или это ошибка? Если это по дизайну, почему аналогичная операция работает на C#?

+0

Чтобы уточнить, тип списка, используемый в программе C#, не совпадает с типом списка, используемым в F #: 'System.Collections.Generic.List ' vs 'Microsoft.FSharp.Collections.List '. –

ответ

9

Это ожидаемое поведение. C#, как правило, делает больше автоматические преобразования между типами, чем F #:

  • В C#, результат лямбда-функции является List<T>, но компилятор С # автоматически преобразует результат в IEnumerable<T>, который является ожидаемый тип возвращаемого функции ,

  • В F #, компилятор автоматически не отбрасывать результат в IEnumerable<T> и поэтому ваш второй фрагменте не типа проверки - потому что она возвращается 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 ]) 
+0

Спасибо, Томас! – dharmatech

+1

Уместно ли утверждать, что «компилятор C# автоматически преобразует результат в« IEnumerable '"? Поскольку класс 'List' реализует' IEnumerable', он 'List' * эффективно * IEnumerable', поэтому преобразование не требуется. – dharmatech

+0

[docs] (http://msdn.microsoft.com/en-us/library/ee370372.aspx) указывают, что F # 'List' реализует' IEnumerable'. Поэтому никакого перевода или преобразования не требуется. – dharmatech

4

F # int list очень отличается от C# List<int>. Если вы хотите использовать тип из C#, список F # является неизменным (его нельзя изменить после создания списка). С другой стороны, в первом (рабочем) примере вы возвращаете seq<int>, который в основном такой же, как IEnumerable<int>. F # list также реализует IEnumerable, но взаимодействие в F # работает по-другому: если вы хотите передать какой-либо аргумент в качестве определенного интерфейса, вам нужно явно передать его этому экземпляру интерфейса.Это означает, что это будет работать, а также:

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

В качестве альтернативы, можно преобразовать F # list в IEnumerable<> с помощью Seq.ofList:

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

Другим отличием является явное сопряжение в F #: Некоторые другие детали при конверсиях из списка F # в System.Collections.Generic.List<> можно увидеть here.

+2

'System.Collections.Generic.List' действительно сильно отличается от списков F #, но эта разница не имеет ничего общего с проблемой здесь. Использование 'System.Collections.Generic.List' в F # будет вести себя точно так же, как с использованием списков F #, и для этого потребуется одно и то же« преобразование ». Обратите внимание, что списки F # реализуют seq (aka IEnumerable), как и списки .net. – sepp2k

+0

@ sepp2k Я улучшил ответ, чтобы сделать этот пункт немного яснее. –

Смежные вопросы