F# Method overload resolution not as smart as C#? - f#

Say, I have
member this.Test (x: 'a) = printfn "generic"
1
member this.Test (x: Object) = printfn "non generic"
2
If I call it in C#
var a = test.Test(3); // calls generic version
var b = test.Test((object)3); // calls non generic version
var c = test.Test<object>(3); // calls generic version
However, in F#
let d = test.Test(3); // calls non generic version
let e = test.Test<int>(3); // calls generic version
So I have to add type annotation so as to get the correct overloaded method. Is this true? If so, then why F# doesn't automatically resolve correctly given that the argument type is already inferred? (what is the order of F#'s overload resolution anyway? always favor Object than its inherited classes?)
It is a bit dangerous if a method has both overloads, one of them takes argument as Object type and the other one is generic and both return the same type. (like in this example, or Assert.AreEqual in unit testing), as then it is very much likely we get the wrong overloading without even notice (won't be any compiler error). Wouldn't it be a problem?
Update:
Could someone explain
Why F# resolves Assert.AreEqual(3, 5) as Assert.AreEqual(Object a, Object b) but not Assert.AreEqual<T>(T a, T b)
But F# resolves Array.BinarySearch([|2;3|], 2) as BinarySearch<T>(T[]array, T value) but not BinarySearch(Array array, Object value)

F# Method overload resolution not as smart as C#?
I don't think it's true. Method overloading makes type inference much more difficult. F# has reasonable trade-offs to make method overloading usable and type inference as powerful as it should be.
When you pass a value to a function/method, F# compiler automatically upcasts it to an appropriate type. This is handy in many situations but also confusing sometimes.
In your example, 3 is upcasted to obj type. Both methods are applicable but the simpler (non-generic) method is chosen.
Section 14.4 Method Application Resolution in the spec specifies overloading rules quite clearly:
1) Prefer candidates whose use does not constrain the use of a
user-introduced generic type annotation to be equal to another type.
2) Prefer candidates that do not use ParamArray conversion. If two
candidates both use ParamArray conversion with types pty1 and pty2,
and pty1 feasibly subsumes pty2, prefer the second; that is, use the
candidate that has the more precise type.
3) Prefer candidates that do not have
ImplicitlyReturnedFormalArgs.
4) Prefer candidates that do not have
ImplicitlySuppliedFormalArgs.
5) If two candidates have unnamed actual argument types ty11...ty1n and ty21...ty2n, and each ty1i either
a. feasibly subsumes ty2i, or
b. ty2i is a System.Func type and ty1i is some other delegate
type, then prefer the second candidate. That is, prefer any candidate that has the more specific actual argument types, and
consider any System.Func type to be more specific than any other
delegate type.
6) Prefer candidates that are not extension members over
candidates that are.
7) To choose between two extension members, prefer the one that
results from the most recent use of open.
8) Prefer candidates that are not generic over candidates that are
generic—that is, prefer candidates that have empty ActualArgTypes.
I think it's users' responsibility to create unambiguous overloaded methods. You can always look at inferred types to see whether you're doing them correctly. For example, a modified version of yours without ambiguity:
type T() =
member this.Test (x: 'a) = printfn "generic"; 1
member this.Test (x: System.ValueType) = printfn "non-generic"; 2
let t = T()
let d = t.Test(3) // calls non-generic version
let e = t.Test(test) // call generic version
UPDATE:
It comes down a core concept, covariance. F# doesn't support covariance on arrays, lists, functions, etc. It's generally a good thing to ensure type safety (see this example).
So it's easy to explain why Array.BinarySearch([|2;3|], 2) is resolved to BinarySearch<T>(T[] array, T value). Here is another example on function arguments where
T.Test((fun () -> 2), 2)
is resolved to
T.Test(f: unit -> 'a, v: 'a)
but not to
T.Test(f: unit -> obj, v: obj)

Related

Is it possible to have an F# operator overload, on an internal type, available to all files in the assembly?

I have some types that enforce numeric value ranges, and I use them in many files in a single project. They look something like this:
[<Struct>]
type NonNegativeMoney =
new(x) =
if x < 0m then invalidArg "x" "Ruh-roh..."
{ Value = x }
val Value : decimal
static member (+) (x: NonNegativeMoney, y: NonNegativeMoney) = NonNegativeMoney(x.Value + y.Value)
I now want to make these types internal to the assembly and leave only my OO type model public. However, when I flip these types to internal, I got the following compiler error:
The member or object constructor 'op_Addition' is not public. Private
members may only be accessed from within the declaring type. Protected
members may only be accessed from an extending type and cannot be
accessed from inner lambda expressions.
The reason for this, has been addressed in the question Why does the F# compiler fail with this infix operator?. The solution proposed in the answer is to use F# signature files to make the types internal. This works for the OP's scenario in that question, where usage of the operator is limited to a type in the same file. However, I can't seem to find a way to make it work so the operator is accessible from all files in my project. If I use a signature file, it works intra-file, but not inter-file.
Is there any way to make this work so the types are internal to the assembly, but visible across the files in my project? I'd like to keep the operators, as I'm using library functions like Seq.sum that require them on the types being summed.
You can define the overload in a module instead of inside the type:
[<Struct>]
type internal NonNegativeMoney =
new(x) =
if x < 0m then invalidArg "x" "Ruh-roh..."
{ Value = x }
val Value : decimal
let internal (+) (x: NonNegativeMoney) (y: NonNegativeMoney) = NonNegativeMoney(x.Value + y.Value)
...but this will override the normal (+) operator, which is probably why you ruled it out. Using a custom operator (like ++) may be a reasonable compromise.
You can make the operator available to the whole project by marking the module [<AutoOpen>].

In F#, is it possible to pass a reference to a mutable, defaulted value as a parameter?

For the Froto project (Google Protobuf in F#), I am trying to update the deserialization code from using 'a ref objects to passing values byref<'a>, for performance.
However, the code below fails on the hydrator &element field line:
type Field = TypeA | TypeB | Etc
let hydrateRepeated
(hydrator:byref<'a> -> Field -> unit)
(result:byref<'a list>)
(field:Field) =
let mutable element = Unchecked.defaultof<'a>
hydrator &element field
result <- element :: result
error FS0421: The address of the variable 'element' cannot be used at this point
Is there anything I can do to get this code to work without changing the signature of the hydrator parameter?
I'm very aware that I could use hydrator:'a ref -> Field -> unit and get things to work. However, the goal is to support deserializing into record types without needing to create a bunch of ref objects on the heap every time a record is deserialize.
Note that the following code is perfectly legal and has the same signature as the hydrator function declaration, above, so I'm unclear on what the problem is.
let assign (result:byref<'a>) (x:'a) =
result <- x
let thisWorks() =
let mutable v = Unchecked.defaultof<int>
assign &v 5
printfn "%A" v
I'll try to clarify what I was saying in my comments. You're right that your definition of assign is perfectly fine, and it appears to have the signature byref<'a> -> 'a -> unit. However, if you look at the resulting assembly, you'll find that the way it's compiled at the .NET representation level is:
Void assign[a](a ByRef, a)
(that is, it's a method that takes two arguments and doesn't return anything, not a function value that takes one argument and returns a function that takes the next argument and returns a value of type unit - the compiler uses some additional metadata to determine how the method was actually declared).
The same is true of function definitions that don't involve byref. For instance, assume you've got the following definition:
let someFunc (x:int) (y:string) = ()
Then the compiler actually creates a method with the signature
Void someFunc(Int32, System.String)
The compiler is smart enough to do the right thing when you try to use a function like someFunc as a first class value - if you use it in a context where it isn't applied to any arguments, the compiler will generate a subtype of int -> string -> unit (which is FSharpFunc<int, FSharpFunc<string, unit>> at the .NET representation level), and everything works seamlessly.
However, if you try to do the same thing with assign, it won't work (or shouldn't work, but there are several compiler bugs that may make it seem like certain variations work when really they don't - you might not get a compiler error but you may get an output assembly that is malformed instead) - it's not legal for .NET type instantiations to use byref types as generic type arguments, so FSharpFunc<int byref, FSharpFunc<int, unit>> is not a valid .NET type. The fundamental way that F# represents function values just doesn't work when there are byref arguments.
So the workaround is to create your own type with a method taking a byref argument and then create subtypes/instances that have the behavior you want, sort of like doing manually what the compiler does automatically in the non-byref case. You could do this with a named type
type MyByrefFunc2<'a,'b> =
abstract Invoke : 'a byref * 'b -> unit
let assign = {
new MyByrefFunc2<_,_> with
member this.Invoke(result, x) =
result <- x }
or with a delegate type
type MyByrefDelegate2<'a,'b> = delegate of 'a byref * 'b -> unit
let assign = MyByrefDelegate2(fun result x -> result <- x)
Note that when calling methods like Invoke on the delegate or nominal type, no actual tuple is created, so you shouldn't be concerned about any extra overhead there (it's a .NET method that takes two arguments and is treated as such by the compiler). There is the cost of a virtual method call or delegate call, but in most cases similar costs exist when using function values in a first class way too. And in general, if you're worried about performance then you should set a target and measure against it rather than trying to optimize prematurely.

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
()
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.
F# 4.1 has C# optional attribute compatibility. However if you are developing function that will be consumed also by F# then you should use regular syntax:
member this.M (?i : int) =
let iv = defaultArg i 12
iv + 1
F# 4.1 optional arguments documentation:
https://learn.microsoft.com/en-us/dotnet/fsharp/language-reference/members/methods

F#: Can't hide a type abbreviation in a signature? Why not?

In F#, I'd like to have what I see as a fairly standard Abstract Datatype:
// in ADT.fsi
module ADT
type my_Type
// in ADT.fs
module ADT
type my_Type = int
In other words, code inside the module knows that my_Type is an int, but code outside does not. However, F# seems to have a restriction where type abbreviations specifically cannot be hidden by a signature. This code gives a compiler error, and the restriction is described here.
If my_Type were instead a discriminated union, then there is no compiler error. My question is, why the restriction? I seem to remember being able to do this in SML and Ocaml, and furthermore, isn't this a pretty standard thing to do when creating an abstract datatype?
Thanks
As Ganesh points out, this is a technical limitation of the F# compiler (and .NET runtime), because the type abbreviation is simply replaced by the actual type during the compilation. As a result, if you write a function:
let foo (a:MyType) : MyType = a + 1
The compiler will compile it as a .NET method with the following signature:
int foo(int a);
If the actual type of the abbreviation was hidden from the users of the library, then they wouldn't be able to recognize that the foo function is actually working with MyType (this information is probably stored in some F#-specific meta-data, but that is not accessible to other .NET languages...).
Perhaps the best workaround for this limiation is to define the type as a single-case discriminated union:
type MyType = MT of int
let foo (MT a) = MT(a + 1)
Working with this kind of type is quite convenient. It adds some overhead (there are new objects created when constructing a value of the type), but that shouldn't be a big issue in most of the situations.
Type abbreviations in F# are compiled away (i.e. the compiled code will use int, not MyType), so you can't make them properly abstract. In theory the compiler could enforce the abstraction within the F# world, but this wouldn't be very helpful as it would still leak in other languages.
Note that you can define a type abbreviation as private within a module:
// File1.fs
module File1
type private MyType = int
let e : MyType = 42
let f (x:MyType) = x+1
// Program.fs
module Program
do printfn "%A" (File1.f File1.e)
I am unclear why you can't hide it with a signature; I logged a bug to consider it.
From what I understand F# does not allow an abbreviation to be hidden by a signature.
I found this link where the blogger commented on this but I am not sure on the specifics of why this is the case.
My assumption is that this is a restraint set to allow more effective interop with other languages on the CLR.

F#: Why aren't option types compatible with nullable types?

Why aren't option types like "int option" compatible with nullable types like "Nullable"?
I assume there is some semantic reason for the difference, but I can't figure what that is.
An option in F# is used when a value may or may not exist. An option has an underlying type and may either hold a value of that type or it may not have a value.
http://msdn.microsoft.com/en-us/library/dd233245%28VS.100%29.aspx
That sure sounds like the Nullable structure.
Because of the runtime representation choice for System.Nullable<'T>.
Nullable tries to represent the absent of values by the null pointer, and present values by pointers to those values.
(new System.Nullable<int>() :> obj) = null
|> printfn "%b" // true
(new System.Nullable<int>(1) :> obj).GetType().Name
|> printfn "%s" // Int32
Now consider strings. Unfortunately, strings are nullable. So this is valid:
null : string
But now a null runtime value is ambiguous - it can refer to either the absence of a value or a presence of a null value. For this reason, .NET does not allow constructing a System.Nullable<string>.
Contrast this with:
(Some (null : string) :> obj).GetType().Name
|> printfn "%s" // Option`1
That being said, one can define a bijection:
let optionOfNullable (a : System.Nullable<'T>) =
if a.HasValue then
Some a.Value
else
None
let nullableOfOption = function
| None -> new System.Nullable<_>()
| Some x -> new System.Nullable<_>(x)
If you observe the types, these functions constrain 'T to be a structure and have a zero-argument constructor. So perhaps F# compiler could expose .NET functions receiving/returning Nullable<'T> by substituting it for an Option<'T where 'T : struct and 'T : (new : unit -> 'T)>, and inserting the conversion functions where necessary..
The two have different semantics. Just to name one, Nullable is an idempotent data constructor that only works on value types, whereas option is a normal generic type. So you can't have a
Nullable<Nullable<int>>
but you can have an
option<option<int>>
Generally, though there are some overlapping scenarios, there are also things you can do with one but not the other.
Key difference is that must test the option type to see if it has a value. See this question for a good description of its semantics: How does the option type work in F#
Again, this is from my limited understanding, but the problem probably lies in how each gets rendered in the IL. The "nullable" structure probably gets handled slightly different from the option type.
You will find that the interactions between various .Net languages really boils down to how the IL gets rendered. Mostof the time it works just fine but on occasion, it causes issues. (check out this). Just when you thought it was safe to trust the level of abstraction. :)

Resources