F# + C# = .net# – what is behind of the scene (F#)

It could be interesting to see, how reflector is translating IL code produced by F# to the well-known C# code.

In the last post I put there some F# code. Now we will look what reflector is showing to us when we analyzing the IL code as the C# code.

[CompilationMapping(SourceConstructFlags.Module)]
public static class From
{
    // Methods
    public static StringReader AsStringReader'(string input)
    {
        return new StringReader(input);
    }

    public static string ElementValue'(XDocument document, string elementName)
    {
        return document.Root.Element(XName.Get(elementName)).Value;
    }

    public static Tuple<string, string> Text(string input)
    {
        TextFieldParser csvParser = new TextFieldParser(AsStringReader'(input));
        csvParser.Delimiters = new string[] { ";", ",", "\t" };
        string[] row = csvParser.ReadFields();
        return new Tuple<string, string>(row[0], row[1]);
    }

    public static Tuple<string, string> Xml(string input)
    {
        XDocument document = XDocument.Load(AsStringReader'(input));
        string name = ElementValue'(document, "Name");
        return new Tuple<string, string>(name, ElementValue'(document, "Age"));
    }
}

As we can see, it is rather not a magic. C# is showing us the functions like normal C# static methods, modules as the static classes (last part of namespaces is the module, so for our example we have From and Types modules -> From and Types static classes in C#).

The another thing we can observe is powerful type inference mechanism of F#, which is much more powerful than this one which we have in C#. Then, it requires to write more code in C# to cover the same functionality from F#. We can mention about tuple declaration too (name, age), which is really simple thing in F# in comparison to C# declaration (new Tuple<string, string>(name, age)).

The another interesting thing is that this code simply cannot be compiled by C# compiler. It is because of ‘ chars in method names. It means that F# IL is not exactly compatible with C# IL, but as far as we are working in .net runtime on only IL level there is no problem of course (IL has much more possibilities than any .net language – it is aggregate of possibilities of any .net language and even more).

Okay, now we can go to the second listing from F# translated by reflector into C#:

[CompilationMapping(SourceConstructFlags.Module)]
public static class Types
{
    // Methods
    public static Person ToPerson(string name, string age)
    {
        return new Person(name, age);
    }

    // Nested Types
    [Serializable, CompilationMapping(SourceConstructFlags.ObjectType)]
    public class Person
    {
        // Fields
        internal int _age;
        internal string _name;

        // Methods
        public Person(string name, string age)
        {
            this._name = name;
            this._age = Convert.ToInt32(age);
        }

        public override string ToString()
        {
            FSharpFunc<string, FSharpFunc<int, string>> func = ExtraTopLevelOperators.PrintFormatToString<FSharpFunc<string, FSharpFunc<int, string>>>(new PrintfFormat<FSharpFunc<string, FSharpFunc<int, string>>, Unit, string, string, Tuple<string, int>>("Name: %s, Age: %d"));
            return FSharpFunc<string, int>.InvokeFast<string>(new Types.ToString@15(func), this.Name, this.Age);
        }

        // Properties
        public int Age
        {
            get
            {
                return this._age;
            }
        }

        public string Name
        {
            get
            {
                return this._name;
            }
        }
    }

    [Serializable]
    internal class ToString@15 : FSharpFunc<string, FSharpFunc<int, string>>
    {
        // Fields
        [DebuggerBrowsable(DebuggerBrowsableState.Never), CompilerGenerated, DebuggerNonUserCode]
        public FSharpFunc<string, FSharpFunc<int, string>> clo1;

        // Methods
        internal ToString@15(FSharpFunc<string, FSharpFunc<int, string>> clo1)
        {
            this.clo1 = clo1;
        }

        public override FSharpFunc<int, string> Invoke(string arg10)
        {
            return new Types.ToString@15-1(this.clo1.Invoke(arg10));
        }
    }

    [Serializable]
    internal class ToString@15-1 : FSharpFunc<int, string>
    {
        // Fields
        [DebuggerBrowsable(DebuggerBrowsableState.Never), CompilerGenerated, DebuggerNonUserCode]
        public FSharpFunc<int, string> clo2;

        // Methods
        internal ToString@15-1(FSharpFunc<int, string> clo2)
        {
            this.clo2 = clo2;
        }

        public override string Invoke(int arg20)
        {
            return this.clo2.Invoke(arg20);
        }
    }
}

On the other hand, this code is not as simple as the code before. The whole strange ‘ToString’ code is mainly caused by using sprintf method (or in any other way could be printf). While it is F# specific function, there is a lot of work performed on F# side (F# standard set of libraries) to do what is need to be done, so from C# we must use whole stack of F# api to reconstruct this code. Above of this code we have a code similar to the first one.

Wondering how to find last free version of Reflector? Just go there: ftp://support.red-gate.com/patches/Reflector/Reflector_6.5.0.135_AnyCpu_r123192.exe. Additionally, try the beta version of JetBrains decompiler: http://www.jetbrains.com/decompiler/, especially for libraries created by .net 4.5 compiler.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s