What are the rules for automatic upcasting of functions? - f#

In my code I have a scenario where I'd like F# to automatically upcast values of one function type (e.g. IBase -> unit) to another, explicitly specified function type (IDerived -> unit). While this seems to be generally supported, I found cases where it fails for no obvious reason.
In the code below, case1 to case8 seem equivalent to me: In each case, there's an expression of type IBase -> unit on the right hand side that is bound to a name of type IDerived -> unit. So why are cases 4, 5 and 7 not allowed?
type IBase = interface end
type IDerived = inherit IBase
let foo (x: IBase) = ()
let bar = fun (x: IBase) -> ()
type T = T with static member (!!) (_: T) = foo
let baz = !!T
let case1: IDerived -> unit = foo // OK
let case2: IDerived -> unit = bar // OK
let case3: IDerived -> unit = baz // OK
let case4: IDerived -> unit = !!T // Expecting 'IDerived -> unit' but given 'IBase -> unit'
let case5: IDerived -> unit = upcast !!T // Type 'IBase -> unit' is not compatible with type 'IDerived -> unit'
let case6: IDerived -> unit = let z = !!T in z // OK
let case7: IDerived -> unit = fun (x: IBase) -> () // Expected x to have type 'IDerived' but here has type 'IBase'
let case8: IDerived -> unit = let z = fun (x: IBase) -> () in z // OK
EDIT: To clarify, I'm mostly wondering why the 8 cases are not treated equally by the compiler. For example, I'd expect the binding let z... in case8 to be superfluous, yet it makes a difference (compared to case7). Why?
EDIT: Here's some code to demonstrate what I'm trying to achieve: https://dotnetfiddle.net/AlpdpO It also contains a solution/workaround, so I'm really asking more for technical details/reasons rather than alternative approaches. I thought about raising an issue on GitHub already, but feel like the issue is most likely just a misunderstanding on my side.

F#'s type inference doesn't seem to be quite smart enough to handle some of those cases, but you can help it out by making your definitions of a little bit more explicit. Try this:
type IBase = interface end
type IDerived = inherit IBase
let foo (x: IBase) = ()
type T = T with static member (!!) (_: T) : #IBase -> unit = foo
let case4: IDerived -> unit = !!T
let case5: IDerived -> unit = upcast !!T
let case7: IDerived -> unit = fun (x: #IBase) -> ()
...and, of course, your original passing cases will continue to pass as well. The issue is that F# has been a little bit too strict about the type of the (!!) function and the type of the lambda, so we can help it out with an explicit type annotation that says "IBase or any of its derivatives are fine here".
By all means, file a bug for these syntactic cases. It's not a terribly bad thing for a compiler to err on the side of caution when it comes to types, as the F# compiler has done here. Far worse things can happen if it's too lenient. Here, the worst effect is that you've got to do some of the typing-work for it.
I should point out that this really has nothing to do with casting and there's no casting that happens (the upcast is ignored). The real issue is strictness of interpretation. Try this code out and you'll see the warning that indicates that.

Related

F#: currying differences between functions and static members

Can please someone explain this to me:
type IItem = interface end
type Item = {i:int} interface IItem
type Fail = static member foo (s:string) = fun (x:IItem) -> ""
let foo = fun (s:string) -> fun (x:IItem) -> ""
let works = {i=1} |> foo ""
let fails = {i=1} |> Fail.foo ""
Why does the currying with the static member function not work?
I'm using Visual Studio 2012 with .net 4.5.2 if that matters.
This isn't really a difference between static members and functions - it's a bit more subtle. Here's another repro:
type T =
static member A () (o:obj) = ()
static member B () = fun (o:obj) -> ()
T.A () 1 // ok
T.B () 1 // huh?
Note that the signatures of T.A and T.B are different (this is actually covered in section 11.2.1.1 of the spec):
type T =
class
static member A : unit -> o:obj -> unit
static member B : unit -> (obj -> unit)
end
This is a distinction that is usually unimportant, but basically it means that at a .NET representation level A is compiled to a method with two arguments (even though it looks curried in F#) while B is compiled to a method with a single argument which returns an F# function. This difference is ultimately what causes the behavior you're seeing.

Variance of function types wrt. interfaces

I'm trying to understand the variance rules for function types. It seems they don't treat input and output the same (up to duality). Consider this program.
let mk1 s = s |> Seq.iter (fun _ -> ())
// val mk1 : s:seq<'a> -> unit
let mk2 = mk1 : list<int> -> unit // Ok.
let mk3 () = [1]
// val mk3 : unit -> int list
let mk4 = mk3 : unit -> seq<int> // Type mismatch.
This is the error:
Type mismatch. Expecting a
unit -> seq<int>
but given a
unit -> int list
The type 'seq<int>' does not match the type 'int list'
It's my understanding that seq<int> is an interface type, one which int list implements, so I was expecting this cast to go through(*).
Two questions:
Why doesn't it?
Why is the cast producing mk2 ok?
(*) For theorists: I was expecting the elaborator to exhibit dual behaviour on the input and output positions of the function space type constructor. Is that wrong?
You have this:
let mk4 = mk3 : unit -> seq<int>
Which will not compile, the up-cast will happen automatically in the input parameter but never in the output of the function. This is in the spec, section 14.4.2 Implicit Insertion of Flexibility for Uses of Functions and Members.
This means that F# functions whose inferred type includes an unsealed type in argument position may be passed subtypes when called, without the need for explicit upcasts.
This makes possible to define another version of the function which is restricted to a subtype, which are the other cases you're showing.
Interestingly, you can use flexible types to define mk4 as follows:
let mk4 = mk3 : unit -> #seq<int>
It compiles but automatically lifts the type of mk4 up to unit -> int list

Value restriction when there are no generic parameters

I get the value restriction error on let makeElem in the following code:
let elemCreator (doc: XmlDocument) =
fun name (value: obj) ->
let elem = doc.CreateElement(name)
match value with
| :? seq<#XmlNode> as childs ->
childs |> Seq.iter (fun c -> elem.AppendChild(c) |> ignore)
elem
| _ -> elem.Value <- value.ToString(); elem
let doc = new XmlDocument()
let makeElem = elemCreator doc
Why I get the value restriction error if anonymous function returned from elemCreator hasn't any generic parameters?
The compiler states that the infered type of makeElem is (string -> 'a -> XmlNode). But why it infers second parameter as 'a if I've declared it as obj?
I believe that this may be the "expected" behavior (although unfortunate in this case), as a result of the compiler's generalization and condensation processes. Consider Tomas's example:
let foo (s:string) (a:obj) = a
If you were to define
let bar a = foo "test" a
then the compiler will infer the type bar : 'a -> obj because it generalizes the type of the first argument. In your case, you have the equivalent of
let bar = foo "test"
so bar is a value rather than a syntactic function. The compiler does essentially the same inference procedure, except now the value restriction applies. This is unfortunate in your case, since it means that you have to explicitly annotate makeElem with a type annotation (or make it a syntactic function).
This looks like an unexpected behavior to me. It can be demonstrated using a simpler function:
let foo (s:string) (a:obj) = a
let bar = foo "bar" // Value restriction
One possible explanation might be that the F# compiler allows you to call a function taking parameter of some type with an argument of any subtype. So, you can call foo "hi" (new A()) without explicitly casting A to obj (which used to be required some time ago).
This implicit casting could mean that the compiler actually interprets bar as something like this:
let bar a = foo "bar" (a :> obj)
...and so it thinks that the argument is generic. Anyway, this is just a speculation, so you could try sending this as a bug report to fsbugs at microsoft dot com.
(The following is based solely on observation.)
If you have a function obj -> 'a, calls to that function are not used to infer/solve the type of its argument. An illustration:
let writeLine (arg: obj) = System.Console.WriteLine(arg)
writeLine is obj -> unit
let square x =
writeLine x
x * x
In the above function x is inferred as int because of (*). If a type could be constrained by obj then this function would not work (x would be inferred as obj prior to the use of (*), which would cause an error along the lines of: type obj does not support operator (*)).
I think this behavior is a Good Thing. There's no need to restrict a type as obj because every type is already implicitly convertible to obj. This allows your program to be more generic and provides better interoperability with the .NET BCL.
In short, obj has no bearing on type inference (yay!).

Lookup on object of indeterminate type

I'm trying to figure out why this doesn't compile.
type A() =
member __.M(f:DateTime -> seq<int>) = ()
member __.M(f:DateTime -> obj) = ()
let a = A()
a.M(fun d -> seq [d.Year]) // 'd' is indeterminate type
If I remove the second overload or add a type annotation to d, it does. Is it because some aspect of overload resolution occurs prior to type checking?
Yes, basically overloads make type inference hard. In this case, it seems like you're hoping that the compiler will do some sort of generalization of the types DateTime -> seq<int> and DateTime -> obj to get DateTime -> ? and then proceed from there, but I don't think it ever does that kind of "anti-unification".

F#: Nullable<T> Support

What is the right way to use Nullable in F#?
Currently I'm using this, but it seems awefully messy.
let test (left : Nullable<int>) = if left.HasValue then left.Value else 0
Console.WriteLine(test (new System.Nullable<int>()))
Console.WriteLine(test (new Nullable<int>(100)))
let x = 100
Console.WriteLine(test (new Nullable<int>(x)))
I'm afraid there's no syntactical sugar for nullable types in F# (unlike in C# where you simply append a ? to the type). So yeah, the code you show there does look terribly verbose, but it's the only way to use the System.Nullable<T> type in F#.
However, I suspect what you really want to be using are option types. There's a few decent examples on the MSDN page:
let keepIfPositive (a : int) = if a > 0 then Some(a) else None
and
open System.IO
let openFile filename =
try
let file = File.Open (filename, FileMode.Create)
Some(file)
with
| exc -> eprintf "An exception occurred with message %s" exc.Message; None
Clearly a lot nicer to use!
Options essentially fulfill the role of nullable types in F#, and I should think you really want to be using them rather than nullable types (unless you're doing interop with C#). The difference in implementation is that option types are formed by a discriminated union of Some(x) and None, whereas Nullable<T> is a normal class in the BCL, with a bit of syntactical sugar in C#.
You can let F# infer most of the types there:
let test (left : _ Nullable) = if left.HasValue then left.Value else 0
Console.WriteLine(test (Nullable()))
Console.WriteLine(test (Nullable(100)))
let x = 100
Console.WriteLine(test (Nullable(x)))
You can also use an active pattern to apply pattern matching on nullable types:
let (|Null|Value|) (x: _ Nullable) =
if x.HasValue then Value x.Value else Null
let test = // this does exactly the same as the 'test' function above
function
| Value v -> v
| _ -> 0
I blogged some time ago about nullable types in F# [/shameless_plug]

Resources