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
()
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With