As development continues on Arcana ERP, we see that in order to fulfill our design goals of high availability and distributed concurrency, portions of ArcanaErp.Core are being re-written piece by piece in F# (a general purpose functional programming langauge, targeting .NET, and originally developed at Microsoft Research, Cambridge).
An interesting outcome of this experiment is that we can see concise, expressive, and terse idiomatic F# "expand" into equivalent, and more verbose, C#:
F# Source
namespace ArcanaErp.Core.Lambda
open System
module Interfaces =
type IBaseErpModel =
abstract member Id : int with get, set
abstract member Description : string with get, set
abstract member ExternalIdentifier : string with get, set
abstract member ExternalIdSource : string with get, set
abstract member CreatedAt : DateTime with get, set
abstract member UpdatedAt : DateTime with get, set
dotPeek'd C#
using Microsoft.FSharp.Core;
using System;
namespace ArcanaErp.Core.Lambda
{
[CompilationMapping(SourceConstructFlags.Module)]
public static class Interfaces
{
[CompilationMapping(SourceConstructFlags.ObjectType)]
[Serializable]
public interface IBaseErpModel
{
int Id { get; set; }
string Description { get; set; }
string ExternalIdentifier { get; set; }
string ExternalIdSource { get; set; }
DateTime CreatedAt { get; set; }
DateTime UpdatedAt { get; set; }
}
}
}
A Bigger Example
First, in F#
The previous example was fairly straightforward. We see some extra curly braces and some attributes we can get rid of. Here is a sample from the FSharp.Core assembly in the Microsoft.FSharp.Collections namespace:
[<CompiledName("IterateIndexed")>]
let iteri f (source : seq<'T>) =
checkNonNull "source" source
use e = source.GetEnumerator()
let f = OptimizedClosures.FSharpFunc<_,_,_>.Adapt(f)
let mutable i = 0
while e.MoveNext() do
f.Invoke(i, e.Current);
i <- i + 1;
Decompiled to C#
[CompilationArgumentCounts(new int[] {1, 1})]
[CompilationSourceName("iteri")]
public static void IterateIndexed<T>(FSharpFunc<int, FSharpFunc<T, Unit>> action, IEnumerable<T> source)
{
if ((object) source == null)
throw new ArgumentNullException("source");
System.Collections.Generic.IEnumerator<T> enumerator = source.GetEnumerator();
try
{
OptimizedClosures.FSharpFunc<int, T, Unit> fsharpFunc = OptimizedClosures.FSharpFunc<int, T, Unit>.Adapt(action);
int num = 0;
while (enumerator.MoveNext())
{
fsharpFunc.Invoke(num, enumerator.Current);
++num;
}
}
finally
{
IDisposable disposable = enumerator as IDisposable;
if (disposable != null)
disposable.Dispose();
}
}
Wrapping Up
It is clear to see that F# is the winner here. The F# code is more compact, more expressive, and easier to reason about (after, admittedly, a small learning curve on the F# syntax).
Writing software as complex as an ERP systems, and enterprise software in general, requires code that can be reasoned about fairly easily. On all accounts, for us, F# makes sense.