Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
February 24, 2022 09:52 am GMT

F - C Interop

What is Interop

Interoperability is the ability of two different systems to communicate with each other. In this case, it would be a C# and F# application communicating. Because they compile down to the same Intermediate Language we can reference F# projects in C# projects and vice versa.

Why is this useful

One of the benefits is that if you are planning to migrate your codebase from C# to F#, you can do it gradually by introducing some F# code without discarding any of the existing code base.

Sometimes we want to take advantage of the C# world, i.e. a lot of existing nuget packages are written in C# and we don't want to rewrite them in F#.

Please note that all the observations listed on this page are valid for .NET 5 and below.

Example F# Application using C# library

In this example, we are going to create an F# application that uses a C# library.

The library simulates an IO operation with await Task.Delay(300, cancellationToken); and reverses the words sent.

C# Library

namespace WordReverserLibraryCsharp{    public static class WordReverser    {        public static async Task<string> WordReverserAsync(string sentence, CancellationToken cancellationToken)        {            await Task.Delay(300, cancellationToken);            var words = sentence.Split(' ');            Array.Reverse(words);            return $"{string.Join(" ", words)}";        }    }}

F# Application

namespace WordReverserConsoleAppFsharpopen System.Threadingopen WordReverserLibraryCsharpmodule WordReverserModule =    let wordReverser (sentence: string) : Async<unit> =        async {            let cancellationTokenSource = new CancellationTokenSource(800);            let! reversedWords =                WordReverser.WordReverserAsync(sentence, cancellationTokenSource.Token)                |> Async.AwaitTask            printfn ($"{reversedWords}")        }    Async.Start(wordReverser("one two three"))            Async.RunSynchronously(Async.Sleep(1000))

Observations

To consume the C# library from F#, we have to convert the Task<T>returned from the library to Async<T> using Async.AwaitTask which waits for the Task to complete and returns its result as Async<T>. Read about Async.AwaitTask here

Just before the line Async.Start(wordReverser("one two three")) we still haven't invoked the operation, only done the setup to call wordReverser, to start the operation we can use Async.Start(wordReverser("one two three").

Example C# Application using F# library

In this example we are doing the opposite of the previous one: we create a C# application that uses an F# library.

F# Library

namespace WordReverserLibraryFsharpopen System.Threadingopen System.Threading.Tasksmodule WordReverserModule =    let wordReverser (sentence: string, cancellationToken: CancellationToken) : Task<string> =        let reverser =            async {                do! Async.Sleep(300)                return                    sentence.Split [| ' ' |]                    |> Array.rev                    |> String.concat " "            }    Async.StartAsTask(reverser, TaskCreationOptions.None, cancellationToken)

C# Application

using WordReverserLibraryFsharp;namespace WordReverserConsoleAppCsharp{    internal static class Program    {        private static async Task Main()        {            var cancellationTokenSource = new CancellationTokenSource(400);            var reversedWords = await WordReverserModule.wordReverser("one two three", cancellationTokenSource.Token);            Console.WriteLine(reversedWords);        }    }}

Observations

We can't use F# Async in C#, but the F# library can return a Task so that the C# application can consume it without any issues. We can achieve this by using Async.StartAsTask in .NET 5 and below.
Read about Async.StartAsTask here

It's good practice for any Async work in C#, to always pass a CancellationToken as an argument, while the F# code does not necessarily need one as the CancellationToken propagation is controlled by how the asynchronous work is kicked off and as a result, CancellationTokens may or may not be propagated. For example Async.Sleep doesn't accept a CancellationToken as a parameter.

Example C# Application using F# library handling exceptions

In this example, we are going to use an F# library from a C# application, handling exceptions and passing a CancellationToken.

F# Library

namespace FsharpLibraryCompleteopen System.Threadingopen System.Threading.Tasksmodule FsharpLibraryCompleteModule =    let wordReverser (sentence: string, raiseException: bool, cancellationToken: CancellationToken) : Task<string> =        let reverser =            async {                do! Async.Sleep(300)                if(raiseException) then                    raise (System.Exception("wordReverser threw Exception"))                return                    sentence.Split [| ' ' |]                    |> Array.rev                    |> String.concat " "            }        Async.StartAsTask(reverser, TaskCreationOptions.None, cancellationToken)

C# Application

using FsharpLibraryComplete;namespace ConsoleAppCsharpComplete;internal static class Program{    private static async Task Main()    {        var cancellationTokenSource = new CancellationTokenSource(500);        string reversedWords;        try        {             reversedWords = await FsharpLibraryCompleteModule.wordReverser("one two three", true, cancellationTokenSource.Token);        }        catch (Exception e)        {            Console.WriteLine(e);            throw;        }        Console.WriteLine(reversedWords);    }}

Console output

System.Exception: someAsyncFunction threw Exception   at FsharpLibraryComplete.FsharpLibraryCompleteModule.wordReverser@12-1.Invoke(Unit _arg1) in C:\projects\InteropPlayGround\Docker\ConsoleAppCsharpComplete\FsharpLibraryComplete\FsharpLibraryComplete.fs:line 13   at Microsoft.FSharp.Control.AsyncPrimitives.CallThenInvokeNoHijackCheck[a,b](AsyncActivation`1 ctxt, b result1, FSharpFunc`2 userCode) in D:\a\_work\1\s\src\fsharp\FSharp.Core\async.fs:line 464   at Microsoft.FSharp.Control.Trampoline.Execute(FSharpFunc`2 firstAction) in D:\a\_work\1\s\src\fsharp\FSharp.Core\async.fs:line 104--- End of stack trace from previous location ---   at ConsoleAppCsharpComplete.Program.Main() in C:\projects\InteropPlayGround\Docker\ConsoleAppCsharpComplete\ConsoleAppCsharpComplete\Program.cs:line 14Unhandled exception. System.Exception: someAsyncFunction threw Exception   at FsharpLibraryComplete.FsharpLibraryCompleteModule.wordReverser@12-1.Invoke(Unit _arg1) in C:\projects\InteropPlayGround\Docker\ConsoleAppCsharpComplete\FsharpLibraryComplete\FsharpLibraryComplete.fs:line 13   at Microsoft.FSharp.Control.AsyncPrimitives.CallThenInvokeNoHijackCheck[a,b](AsyncActivation`1 ctxt, b result1, FSharpFunc`2 userCode) in D:\a\_work\1\s\src\fsharp\FSharp.Core\async.fs:line 464   at Microsoft.FSharp.Control.Trampoline.Execute(FSharpFunc`2 firstAction) in D:\a\_work\1\s\src\fsharp\FSharp.Core\async.fs:line 104--- End of stack trace from previous location ---   at ConsoleAppCsharpComplete.Program.Main() in C:\projects\InteropPlayGround\Docker\ConsoleAppCsharpComplete\ConsoleAppCsharpComplete\Program.cs:line 14   at ConsoleAppCsharpComplete.Program.<Main>()

Observations

Running the code as it is, will raise an exception from the F# library that gets caught by the C# application.

Please note that if the Task gets cancelled before raising the exception on the F# library, the C# application will catch the exception as System.Threading.Tasks.TaskCanceledException: A task was canceled.

Conclusion

It's not particularly difficult to use F# libraries in a C# application and vice versa, but there are a few things that we have to keep in mind, i.e. C# does not work with F# Async, instead, we have to add additional steps to convert Async into a Task.


Original Link: https://dev.to/amedeov/f-c-interop-2a42

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To