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
Related
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.
I can't understand what is wrong with following bit of code:
let toClass (problem:Problem<'a>) (classID:int) (items:'a list) =
let newFreqTable = (problem.FreqTables.[classID]).count items
{ problem with FreqTables = newFreqTable :: (problem.FreqTables |> List.filter (fun i -> i.ClassID <> classID)) }
type Problem<'a> when 'a : equality with member this.toClass (classID:int) (items:list<'a>) = toClass this classID items
I have a Problem type which is nothing but a way to group up any number of FreqTables - short for "Frequency tables". So toClass method just takes appropriate freqTable (by classID argument) and returns a new one - with calculated given items.
let typeIndependentCall = toClass p 0 ["word"; "word"; "s"] // this works perfectly
let typeDependentCall = typeIndependentCall.toClass 1 ["word"; "s"]
// gives an error: "One or more of the overloads of this method has
// curried arguments. Consider redesigning these members to take
// arguments in tupled form".
I am pretty new to F# and functional programming. What is the right way to attach behavior to my type?
In F# there are 2 main ways of passing arguments to a function: curried and tupled. The curried form is what you are using in your code above, and has a few key benefits, the first and foremost being partial application.
For example, instead of thinking of
fun add a b = a + b
as a function that takes in 2 arguments and returns a value, we can think of it as a function of one argument that returns a function that with one argument. This is why the type signature of our function is
Int -> Int -> Int
or, more clearly,
Int -> (Int -> Int)
However, when overloading methods, we can only use the tupled argument form
(Int, Int) -> Int
The reason for this is for optimization, as is discussed here
To get your code to work, use
type Problem<'a> when 'a : equality with member this.toClass (classID:int, items:list<'a>) = toClass this classID items
and call it like such:
let typeDependentCall = typeIndependentCall.toClass(1, ["word"; "s"])
While refactoring some F# code, I came across a problem which I fail to understand or resolve. I have a class Problem, with 2 constructors, one default for F# consumption, one for C# convenience, which takes in Funcs and "converts" them into F# functions:
open System
type Problem<'d, 's> (data: 'd, generate: 'd -> Random -> 's, mutate: 'd -> Random -> 's -> 's, evaluate: 's -> float) =
member this.Data = data
member this.Generate = generate this.Data
member this.Mutate = mutate this.Data
member this.Evaluate = evaluate
new (data: 'd, generator: Func<'d, Random, 's> , mutator: Func<'d, 's, Random, 's> , evaluator: Func<'s, float>) =
let generate (data: 'd) rng = generator.Invoke(data, rng)
let mutate (data: 'd) (rng: Random) (solution: 's) = mutator.Invoke(data, solution, rng)
let evaluate (solution: 's) = evaluator.Invoke(solution)
Problem(data, generate, mutate, evaluate)
As far as I can tell, this builds and works as expected.
Being somewhat obsessive-compulsive, I noted that the mutator Func in the C#-friendly constructor had a different order for the arguments, so I proceeded and rewrote it this way (first part unchanged):
new (data: 'd, generator: Func<'d, Random, 's> , mutator: Func<'d, Random, 's, 's> , evaluator: Func<'s, float>) =
let generate = fun (data: 'd) rng -> generator.Invoke(data, rng)
let mutate = fun (data: 'd) (rng: Random) (solution: 's) -> mutator.Invoke(data, rng, solution)
let evaluate = fun (solution: 's) -> evaluator.Invoke(solution)
Problem(data, generate, mutate, evaluate)
While the 3 functions seem to have the right signature, the last line fails and displays a red squiggly, telling me that "A unique overload for method 'Problem`2' could not be determined based on type information prior to this program point. The available overloads are shown below (or in the Error List window). A type annotation may be needed."
Can anyone help me see what I am missing? I tried to type-annotate the 4 arguments in the last line, to no avail - and I have no idea how to fix this. What also strikes me as odd is that the previous version worked, simply by having 2 arguments reversed in a Func.
Reading the spec (8.13.6) gives:
The first type-directed conversion converts anonymous function expressions and other function-valued arguments to delegate types.
Given:
A formal parameter of delegate type D
· An actual argument farg of known type ty1 -> ... -> tyn -> rty
· Precisely n arguments to the Invoke method of delegate type D
Then:
· The parameter is interpreted as if it were written:
new D(fun arg1 ... argn -> farg arg1 ... argn)
As these conditions apply, the Func delegates are converted to curried form, so an ambiguity exists.
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!).
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".