Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pattern matching F# type in a C# code

Tags:

c#

interop

f#

Suppose there is a F# definitions:

type Either<'a,'b> = | Left of 'a | Right of 'b

let f (i : int) : Either<int, string> =
    if i > 0
        then Left i
        else Right "nothing"

Function f is used in C# code:

var a = Library.f(5);

How the result value a could be pattern matched for data constructors? Something like:

/*
(if a is Left x)
    do something with x
(if a is Right y)
    do something with y
*/
like image 637
ДМИТРИЙ МАЛИКОВ Avatar asked Dec 09 '22 12:12

ДМИТРИЙ МАЛИКОВ


1 Answers

Using F# discriminated unions from C# is a bit inelegant, because of how they are compiled.

I think the best approach is to define some members (on the F# side) that will simplify using the types from C#. There are multiple options, but the one I prefer is to define TryLeft and TryRight methods that behave similarly to Int32.TryParse (and so they should be familiar to C# developers using your F# API):

open System.Runtime.InteropServices

type Either<'a,'b> = 
  | Left of 'a 
  | Right of 'b
  member x.TryLeft([<Out>] a:byref<'a>) =
    match x with Left v -> a <- v; true | _ -> false
  member x.TryRight([<Out>] b:byref<'b>) =
    match x with Right v -> b <- v; true | _ -> false

Then you can use the type from C# as follows:

int a;
string s;
if (v.TryLeft(out a)) Console.WriteLine("Number: {0}", a);
else if (v.TryRight(out s)) Console.WriteLine("String: {0}", s);

You lose some of the F# safety by doing this, but that's expected in a language without pattern matching. But the good thing is that anybody familiar with .NET should be able to use the API implemented in F#.

Another alternative would be to define member Match that takes Func<'a> and Func<'b> delegates and invokes the right delegate with the value carried by left/right case. This is a bit nicer from the functional perspective, but it might be less obvious to C# callers.

like image 73
Tomas Petricek Avatar answered Dec 15 '22 00:12

Tomas Petricek