позволять! не допускается в seq {...} в асинхронном режиме? - PullRequest
0 голосов
/ 30 августа 2018

Я пытаюсь преобразовать следующий код 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 'в выражениях последовательности не допускается. Вместо этого используйте «для х в колл».

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 голосов
/ 30 августа 2018

В общем, вы обычно хотите избежать вложения одного выражения вычисления в другое, потому что это может сбить с толку то, что относится к чему. В частности, здесь вы намереваетесь интерпретировать выражение 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 не удастся, вам придется преобразовать этот код, чтобы использовать списки вместо seqs. Должно быть достаточно просто, поэтому я оставлю это вам, но дайте мне знать, если у вас есть проблемы с этим.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...