F#从查询表达式返回值

时间:2014-07-19 18:37:44

标签: f# query-expressions

我刚刚开始学习F#,并且对类型推断印象深刻,我认为我会尝试从表中获取第一条记录的函数(使用查询表达式,Linq样式):

let getfirst data = 
    let result = query { for n in data do take 1 }
    result |> Seq.head

这样做,类型为IQueryable<'a> -> 'a

但为什么这个版本不起作用?

let getfirst2 data = 
    query { for n in data do head }

for n in data do head不应该像上次一样给出标量'a吗?有人可以解释为什么第二个版本不起作用,以及如何在不使用Seq.head的情况下使其工作?

2 个答案:

答案 0 :(得分:1)

我不知道为什么,但是当您将鼠标悬停在data getfirst2上的System.Linq.IQueryable<Linq.QuerySource<'a, System.Linq.IQueryable>>参数时,您会看到System.Linq.IQueryable<'a>类型的open System.Linq let getfirst2 (data : IQueryable<'a>) : 'a = query { for item in data do head } 确实应该是[1 .. 10] |> System.Linq.Queryable.AsQueryable |> getfirst2 |> printfn "%d" // Prints 1. 。< / p>

您可以通过添加类型注释来“修复”它:

{{1}}

然后它就像你预期的那样工作:

{{1}}

也许其他人可以解释为什么编译器会推断它所做的类型。

答案 1 :(得分:1)

原因是查询构建器有一个有点hacky重载Run方法来运行查询,具有以下重载:

QueryBuilder.Run : Quotations.Expr<'t> -> 't
QueryBuilder.Run : Quotations.Expr<Linq.QuerySource<'t, IEnumerable>> -> seq<'t>
QueryBuilder.Run : Quotations.Expr<Linq.QuerySource<'t, IQueryable>> -> IQueryable<'t>

在你的情况下,任何重载都可以应用,给定data的合适类型(尽管QuerySource<_,_>是一种用户代码并不打算使用的类型,所以两个重载是不太可能的)。不幸的是,由于这些重载被定义的奇怪方式(第一个和第二个实际上是在单独的模块中定义的扩展方法),第三个赢得了重载决策之战。