简体   繁体   English

F#从查询表达式返回值

[英]F# returning value from query expression

I am just starting to learn F#, and impressed by the type inference I thought I would try a function that gets the first record from a table (using query expressions, Linq style): 我刚刚开始学习F#,并为类型推断打动了我以为我会尝试从表中获取第一条记录的函数(使用查询表达式,Linq样式):

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

This works, the type is IQueryable<'a> -> 'a . 这有效,类型是IQueryable<'a> -> 'a

But why doesn't this version work? 但是,为什么这个版本不起作用?

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

Shouldn't for n in data do head give a scalar 'a just like last time? 难道不应该像上次那样for n in data do head给出标量'a吗? Can someone explain why the second version doesn't work, and how to make it work without using Seq.head? 有人可以解释为什么第二个版本不起作用以及如何在不使用Seq.head的情况下使其起作用吗?

I don't know why, but when you hover over the data argument in getfirst2 you see it's of type System.Linq.IQueryable<Linq.QuerySource<'a, System.Linq.IQueryable>> when it really should be System.Linq.IQueryable<'a> . 我不知道为什么,但是当您将鼠标悬停在getfirst2data参数上时,您会看到它的类型为System.Linq.IQueryable<Linq.QuerySource<'a, System.Linq.IQueryable>> ,而实际上它应该是System.Linq.IQueryable<'a>

You can "fix" it by adding type annotations: 您可以通过添加类型注释来“修复”它:

open System.Linq
let getfirst2 (data : IQueryable<'a>) : 'a = query { 
    for item in data do
    head
}

Then it works like you have expected: 然后,它会像您期望的那样工作:

[1 .. 10]
|> System.Linq.Queryable.AsQueryable
|> getfirst2
|> printfn "%d" // Prints 1.

Maybe someone else can shed some light on why the compiler infers the types it does. 也许其他人可以阐明编译器为何推断其类型。

The reason is that the query builder has a somewhat hacky overloaded Run method for running queries, with the following overloads: 原因是查询构建器具有用于运行查询的有点笨拙的重载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>

In your case, any of the overloads could apply, given a suitable type for data (though QuerySource<_,_> is a type which isn't ever meant to be used by user code, so two of the overloads are quite unlikely). 在您的情况下,给定适当的data类型,任何重载都可能适用(尽管QuerySource<_,_>是用户代码从未使用过的类型,因此,两个重载是不太可能的) 。 Unfortunately, due to the strange way these overloads are defined (the first and second are actually extension methods defined in separate modules), the third one wins the overload resolution battle. 不幸的是,由于定义了这些重载的奇怪方式(第一个和第二个实际上是在单独的模块中定义的扩展方法),第三个赢得了重载解决方案之战。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM