0

I'm trying to convert some F# code to C# but I don't understand this particular block:

let GetItems = asyncResult {
    let! items = StaticClass.getItems
    return 
        items
        |> AsyncSeq.map (fun item -> 
            fun handleItem -> asyncResult {
                let! itemCanBeRemoved = handleItem item|> AsyncResult.mapError HandleItemError
                // some code removed for clarity
            })
 }

My attempt to convert this is:

public static async Task<IEnumerable<T>> GetItems<T>() where T : Item
{
    var items = await StaticClass.GetItems();

    var tasks = new List<Task>();
    foreach (var item in items)
    {
        bool itemCanBeRemoved;
        if (itemCanBeRemoved)
        {
            // do something
        }
        else
        {
            // do something else
        }
    }

    await Task.WhenAll(tasks);

    return items;
}

Assuming my conversion is on the right track, I don't understand how the handleItem func is defined and then invoked within its own implementation block. Can someone please explian what handleItem is doing and how it invokes itself?

Craig
  • 1,890
  • 1
  • 26
  • 44
  • 1
    What's the type of `GetItems` in the F# code? The function provided to `AsyncSeq.map` appears to have a type like `item -> ((item -> AsyncResult<'a>) -> AsyncResult<'a>)` so `handleItem` is a function provided as an argument. It looks like your C# return type should be something like `Task>>>`. – Lee Nov 20 '19 at 18:18
  • Unless this is just a learning exercise, the easiest solution might be to reference the F# project from the C# project. Assuming the F# code works as expected, then your task would be reduced to making the F# project's public API friendly to C#. That might be much easier than converting it all to C#. – Scott Hutchinson Nov 21 '19 at 00:17

2 Answers2

2

It's hard to tell exactly what the original code is doing without a complete sample, but if I had to guess, I'd say the F# code is likely using a fixed-point combinator to process the items in the list. This can be accomplished in C# by using null-initialized Func definition to support a recursive-lambda in a LINQ statement.

Func<Item, ValueTuple<Item, bool>> handleItem = null;
handleItem = item => {
    var itemCanBeRemoved = handleItem item; // Should do something so that it's not infinitely recursive here
    return (item, itemCanBeRemoved);

};

return items.Select(handleItem);

You can reference a similar question here for a more detailed explanation: https://stackoverflow.com/a/61206/1748071

Aaron M. Eshbach
  • 6,380
  • 12
  • 22
1

The key part of your code sample does not actually define a self-calling (i.e. recursive) lambda.

items 
|> AsyncSeq.map (fun item -> 
    fun handleItem -> asyncResult {
        let! itemCanBeRemoved = handleItem item|> AsyncResult.mapError HandleItemError
        // some code removed for clarity
    })

What this does is that it iterates over all items and generates a new AsyncSeq<T> where each item is generated from the original one by calling:

fun handleItem -> asyncResult {
    let! itemCanBeRemoved = handleItem item|> AsyncResult.mapError HandleItemError
    // some code removed for clarity
}

This is not recursive - it defines a lambda that takes handleItem (presumably a function that can be used to handle the individual items) and returns asyncResult.

If I understand the code correctly, it would then return something like:

AsyncSeq<(Item -> AsyncResult<Result, Err>) -> AsyncResult<Result, Err>>

This would be an asynchronously generated collection of functions that take function (to process each item) and return result of this processing.

This looks like a very odd thing to do, so I guess this is either badly copied from your actual source code, or it is just very bad F# code.

That said, neither AsyncResult nor AsyncSeq have a direct corresponding type in C#, so translating this might be quite hard.

Tomas Petricek
  • 240,744
  • 19
  • 378
  • 553
  • I thought there is Async IEnumerable now in C# 8. Or is ASyncSeq different? https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.iasyncenumerable-1?view=dotnet-plat-ext-3.0 – s952163 Nov 21 '19 at 03:05
  • 1
    Yeah, I think `IAsyncEnumerable` is roughly like `AsyncSeq` (depends a bit on details). For `AsyncResult`, you'll probably just want to use exceptions instead. – Tomas Petricek Nov 21 '19 at 11:42
  • IAsyncEnumerable<_> and AsyncSeq<_> are mostly equivalent; they both provide asynchronous enumeration/pulling of data. Since in F# this didn't need language work to add this feature (F# has computation expressions to generically define these things yourself) it comes from a community maintained library instead. It's been around for a few years now so it predates IAsyncEnumerable<_>. – akara Nov 22 '19 at 04:10
  • It's been around since 2011 :-) http://tomasp.net/blog/async-sequences.aspx/ – Tomas Petricek Nov 22 '19 at 10:47