让!不允许在seq {...}中异步使用?

时间:2018-08-30 18:53:40

标签: c# f#

我正在尝试将以下C#代码转换为F#。

static async Task<IEnumerable<string>> ListingObjectsAsync()
{
    ListObjectsV2Request request = new ListObjectsV2Request
    {
        BucketName = bucketName,
        MaxKeys = 10
    };
    ListObjectsV2Response response;
    do
    {
        response = await client.ListObjectsV2Async(request);

        foreach (S3Object entry in response.S3Objects)
        {
            yield return entry.Key;
        }
        request.ContinuationToken = response.NextContinuationToken;
    } while (response.IsTruncated);
}

但是,以下未完成的代码出现了错误

  

错误FS0795使用'let!不允许在序列表达式中使用x = coll'。使用“ for coll中的x”代替。

let listObjects bucketName = async {
    use client = new AmazonS3Client(RegionEndpoint.USEast2)
    let request = new ListObjectsRequest(BucketName = bucketName, MaxKeys = 10)
    // do while... todo
    seq {
        let! response = client.ListObjectsV2Async(request) |> Async.AwaitTask
        for entry in response.S3Objects do
            yield entry.Key
        request.ContinuationToken <- response.NextContinuationToken
    }

如何将C#do {...} while(...);转换为F#btw?

1 个答案:

答案 0 :(得分:9)

通常,您通常希望避免将一个计算表达式嵌套在另一个表达式中,因为它会使哪个表达式引用什么变得令人困惑。特别是在这里,您希望将let!表达式解释为与async { let! foo = AsyncFoo() }中相同,但正是seq { }计算表达式正在解释let!

我建议将seq { }拆分成自己的函数,并使用异步的循环部分。 listObjects函数的其余部分不需要异步,

let keysFromPartialResponse response = seq {
    for entry in response.S3Objects do
        yield entry.Key
}

let doRequest request resultSoFar = async {
    let! response = client.ListObjectsV2Async(request) |> Async.AwaitTask
    request.ContinuationToken <- response.NextContinuationToken
    let result = Seq.append resultSoFar (keysFromPartialResponse response)
    if request.IsTruncated then
        return! doRequest request result  // This is the loop step
    else
        return result
    }

let listObjects bucketName = async {
    use client = new AmazonS3Client(RegionEndpoint.USEast2)
    let request = new ListObjectsRequest(BucketName = bucketName, MaxKeys = 10)
    return! doRequest request Seq.empty
}

使用AsyncSeq可能会有更好的解决方案,但我将其作为练习留给读者。

请注意,您的F#代码存在一个问题,该问题仍然存在于我的代码中,这是序列是惰性的,在被评估之前不会真正运行seq { ... }代码。并且因为您使用过use,所以一旦client对象超出范围(即,一旦异步返回),就将处置AmazonS3Client实例。因此,当您评估序列时(它将评估response.S3Objects),客户端将不再有效。如果这意味着评估response.S3Objects将失败,那么您将不得不将此代码转换为使用列表而不是seq。应该足够简单,所以我将其留给您,但是如果您有任何麻烦,请告诉我。