Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why default arguments in F# (FSharpOption<T>) are reference types?

C# and F# has different implementation of the default (or optional) parameters.

In C# language when you add default value to the argument you'll not change its underlying type (I mean type of the parameter). Actually optional arguments in C# is a lightweight syntactic sugar:

class CSharpOptionalArgs
{
  public static void Foo(int n = 0) {}
}

// Somewhere in the call site

CSharpOptionalArgs.Foo();
// Call to Foo() will be transformed by C# compiler
// *at compile time* to something like:
const int nArg = GetFoosDefaultArgFromTheMetadata();
CSharpOptionalArgs.Foo(nArg);

But F# implements this feature in a different way. Unlike C#, F# optional arguments resolves at callee site but not at caller site:

type FSharpOptionalArgs() =
    static let defaultValue() = 42

    static member public Foo(?xArg) =
        // Callee site decides what value to use if caller does not specifies it
        let x = defaultArg xArg (defaultValue())
        printfn "x is %d" x

This implementation is absolutely reasonable and much more powerful. C# optional arguments restricted only to compile-time constants (because optional arguments stored in assemblies metadata). In F# default value could be less obvious but we can use arbitrary expression as a default value. So far so good.

F# optional arguments transformed by F# compiler to Microsoft.FSharp.Core.FSharpOption<'a> which is a reference type. This means that every call to the method with optional argument in F# will lead to additional allocation at the managed head and will lead to pressure to garbage collection.

**EDITED**
// This call will NOT lead to additional heap allocation!
FSharpOptionalArgs.Foo()
// But this one will do!
FSharpOptionalArgs.Foo(12)

I don't worry about application code, but this behavior could dramatically degrade performance for libraries. What if some library method with an optional argument will be called thousands times per second?

This implementation seems really odd to me. But maybe there is some rules that library developer should avoid using of this feature or F# team is going to change this behavior in the future version of F#?

Following unit test profs that optional arguments are reference type:

[<TestFixture>]
type FSharpOptionalArgumentTests() =

    static member public Foo(?xArg) =
        // Callee site decides what value to use if caller does not specifies it
        let x = defaultArg xArg 42
        ()

    [<Test>]
    member public this.``Optional argument is a reference type``() =
        let pi = this.GetType().GetMethod("Foo").GetParameters() |> Seq.last

        // Actually every optional parameter in F# is a reference type!!
        pi.ParameterType |> should not' (equal typeof<int>)
        pi.ParameterType.IsValueType |> should be False
        ()
like image 508
Sergey Teplyakov Avatar asked Apr 13 '13 14:04

Sergey Teplyakov


1 Answers

Because nobody in F# team is not interesting yet in compiling "simple", Option-like discriminated unions as value types, supporting pattern-matching over such unions and so on :)

Remember, tuples types are heavly used in functional languages like F# (much more than default arguments), but still implemented in CLR as refererence types - nobody cares about memory allocation and functional_languages-specific GC tweaks.

like image 122
controlflow Avatar answered Oct 04 '22 01:10

controlflow