I’m currently working on an F# library that will be accessed from C#. When doing this the obvious question is; “How will this look from C#?” This wasn't quite clear to me at first so I’ll go through a couple custom F# types and show how they are compiled.
Record type, Fermion.fs:
type Spin =
| Up = 0
| Down = 1
type Class =
| Quark = 0
| Lepton = 1
type Type =
...
| Electron = 6
...
type Fermion =
{ spin: Spin; particleClass: Class; particleType: Type }
member yada.Spin = yada.spin
member v.Class = v.particleClass
member v.Type = v.particleType
member stuff.GetDescription() =
System.String.Format(
"This {0} is a spin {1} {2}.",
stuff.particleType.ToString(),
stuff.spin.ToString(),
stuff.particleClass.ToString())
Using the record type in F#
let electron = {
spin = Spin.Down;
particleClass = Class.Lepton;
particleType = Type.Electron }
In Reflector:
public static class Fermion
{
public sealed class Fermion :
IStructuralEquatable,
IComparable,
IStructuralComparable
{
internal Particles.Class particleClass@;
internal Particles.Type particleType@;
internal Particles.Spin spin@;
public Fermion(
Particles.Spin spin,
Particles.Class particleClass,
Particles.Type particleType)
{
this.spin@ = spin;
this.particleClass@ = particleClass;
this.particleType@ = particleType;
}
public string GetDescription()
{
return string.Format("This {0} is a spin {1} {2}.",
this.particleType@.ToString(),
this.spin@.ToString(),
this.particleClass@.ToString());
}
public Particles.Class Class { get { return this.particleClass@; } }
public Particles.Class particleClass { get { return this.particleClass@; } }
public Particles.Type particleType { get { return this.particleType@; } }
public Particles.Spin spin { get { return this.spin@; } }
public Particles.Spin Spin { get { return this.spin@; } }
public Particles.Type Type { get { return this.particleType@; } }
public int CompareTo(Particles.Fermion obj);
public sealed override int CompareTo(object obj);
public sealed override int CompareTo(object obj, IComparer comp);
public bool Equals(Particles.Fermion obj);
public sealed override bool Equals(object obj);
public sealed override bool Equals(object obj, IEqualityComparer comp);
public override int GetHashCode();
public sealed override int GetHashCode(IEqualityComparer comp);
}
}
Some things to note:
- This is a “record type” so a constructor is generated to set all the private fields. No initialization is performed other than this. So record types are essentially a DTO.
- It is sealed.
- When creating an instance of this type it it not necessary to explicitly specify it because of F# type inference.
- The IComparable, IStructuralComparable and IStructuralEquatable interfaces are all automatically implemented by the F# compiler.
- A namespace was not explicitly defined in the example so the type has no namespace and the type is nested in a static class with the name of the file it was declared in. If a namespace had been defined then this would not have been nested in a static class and would have simply resided under said namespace.
- The identifiers (“v”, “yada”, “stuff”) which are equivalent to the “this” keyword in C# are all converted to "this" in the output. And as an aside they do not need to be the same throughout the type, just in the method or property declaration.
- Fields are publicly exposed via a property that is named as you named the field in the F# source. The fields are suffixed with an “@”.
- As an aside, enumerations must explicitly have a value defined or they will be compiled as discriminated unions.
Now lets mix things up a bit…
Constructed type, Fermion.fs:
namespace Particles
type Spin =
| Up = 0
| Down = 1
type Class =
| Quark = 0
| Lepton = 1
type Type =
...
| Electron = 6
...
type Fermion(spin: Spin, particleClass: Class, particleType: Type) =
let _description = System.String.Format(
"This {0} is a spin {1} {2}.",
particleType.ToString(),
spin.ToString(),
particleClass.ToString())
member yada.Spin = spin
member v.Class = particleClass
member v.Type = particleType
member stuff.GetDescription() = _description
Using the constructed type in F#:
let electron = new Fermion(Spin.Down, Class.Lepton, Type.Electron)
In Reflector:
public class Fermion
{
internal string _description;
internal Class particleClass;
internal Type particleType;
internal Spin spin;
public Fermion(Spin spin, Class particleClass, Type particleType)
{
this.spin = spin;
this.particleClass = particleClass;
this.particleType = particleType;
this._description = string.Format("This {0} is a spin {1} {2}.",
this.particleType.ToString(),
this.spin.ToString(),
this.particleClass.ToString());
}
public string GetDescription() { return this._description; }
public Class Class { get { return this.particleClass; } }
public Spin Spin { get { return this.spin; } }
public Type Type { get { return this.particleType; } }
}
Some things to note:
- This is a “constructed type” so there is a constructor. You’re constructor code is simply put in the body of the type and not in a special function like C#.
- Requires you to explicitly new up the type and pass the parameters in.
- No interfaces get automatically implemented as in the case of the record type.
- This class is not sealed.
- This type was defined under a namespace so it is not wrapped in a static class.