In F#, How can I cast a function to a different delegate? - f#

I need to cast a function with the signature Thing -> Thing -> int to a Comparison<Thing>.
For example, when I have:
Array.Sort(values, mySort)
I get an error stating "This expression was expected to have type Comparison but here has type Thing -> Thing -> int"
When I try this:
Array.Sort(values, (fun (a, b) -> mySort a b))
(actually, this is by way of a wrapper object)
I get an error stating "Type constraint mismatch. The type ''a -> 'b' is not compatible with the type 'Comparison'"
How am I supposed to provide a Comparison<T>?
FYI: Comparison<T> is a delegate with the signature 'T * 'T -> int

This works for me:
let values = [|1;2;3|]
let mySort a b = 0
Array.Sort(values, mySort)
But have you tried:
Array.Sort(values, Comparison(mySort))

Oddly, despite the fact that delegate signatures are expressed in tupled form (e.g. 'T * 'T -> int), you need to provide your function in curried form (e.g. fun a b -> mySort a b). See section 6.4.2 of the spec.

Related

In F#, what does error of type mismatch between "Expecting" and "Given" mean?

I am receiving the following error when using the Binding.subModelSelectedItem():
FS0001: Type mismatch. Expecting a 'string -> Binding<(Model * 'a), b>'
but given a
'string -> Binding<Model,Msg>'.
The type 'Model * 'a' does not match the type 'Model'
From the following F# code:
type Msg =
| SetAppointmentKey of int option
Then, in the bindings:
"SelectedAppointmentKey" |> Binding.subModelSelectedItem("AppointmentKeys", (fun m -> m.SelectedAppointmentKey), SetAppointmentKey)
I do not understand the error message. What does the error message mean? "Expecting" from who? "Given" from what?
Sorry for my ignorance here, but nothing this newbie has tried has fixed this.
Thank you for any help.
TIA
I'm not sure where the specific error is in your code, but I can try to help you understand what the error message says. A simple example to illustrate the same error:
let foo (f:int -> int) = ()
let bar x = ""
foo bar
This does not work, because foo expects a function int -> int, but bar returns a string. You get:
error FS0001: Type mismatch. Expecting a int -> int but given a
int -> string
The type int does not match the type string
The error message tells you that the function you're using as an argument has a wrong type. It tells you the two types and also a part of it where it goes wrong (here, the return types do not match).
Looking at your error message:
FS0001: Type mismatch. Expecting a string -> Binding<(Model * 'a), 'b>
but given a
string -> Binding<Model,Msg>.
The type Model * 'a does not match the type Model
It seems that you are creating a function somewhere that takes a string and returns a Binding. This function is expected to return a Binding with Model as the first type parameter, but your code returns a tuple formed by Model and some other thing.
Something like this can easily happen if you have fun x -> ..., something in your code, perhaps in a place where you wanted (fun x -> ...), something. You would get a similar error if you wrote, e.g.:
let foo (f:int -> int) = ()
let bar = fun x -> 0, 1
foo bar

Error FS0001, this function was expected to have type byref<int>

I have a made a simple function that wraps common F# functions of signature 'a -> 'b -> 'c option to more "C# compliant" function as : 'a -> b -> byref<'c> -> bool. But somehow when I try to wrap such a method in a class I am getting error FS0001 and I can't locate the error.
Code below
open System
open System.Runtime.InteropServices
// Given a function, f: 'a -> 'b -> 'c option returns
// A new function g: 'a -> 'b -> byref<'c> -> bool
let wrapOptionF f a b (g:byref<'c>) =
match f a b with
| Some v ->
do g <- v
true
| None ->
false
let tryDivide (a:int) (b:int) =
match Math.DivRem(a,b) with
| v, 0 -> Some v
| _ -> None
type TryDivideWrapper() =
static member TryDivide(a, b, [<Out>]cRef:byref<int>) : bool =
let f = wrapOptionF tryDivide a b
f cRef
The offending line is f cRef.
This post contains a more in-depth explanation, but in short you can replace your final type definition with the following:
type TryDivideWrapper() =
static member TryDivide(a, b, [<Out>]cRef:byref<int>) : bool =
wrapOptionF tryDivide a b &cRef
The reason for this is that your wrapOptionF takes a byref parameter. However, byref<int> isn't really a type like int or int ref - it's just an indication to the compiler that your parameter should be passed by reference (like out in C#). Once inside your function, however, what you have is a regular int.
Edit: Note that Intellisense will show cRef as having type byRef<int>. However, if you bind another variable to cRef, you'll see that the type you get is a regular int. You can put this line inside TryDivide and then hover your mouse over a to see it:
let a = cRef
Using the & operator tells the compiler that you're passing cRef into f by reference - which is exactly what f needs - making the type system happy. I've tested this using a C# project, and TryDivideWrapper.TryDivide(a, b, out c) works as expected. Add in #MarkSeemann's tryDivide and you should be good to go.
I'm not exactly sure I understand the reason, but this seems to work:
type TryDivideWrapper() =
static member TryDivide(a, b, [<Out>]cRef:byref<int>) : bool =
wrapOptionF tryDivide a b &cRef
BTW, the OP tryDivide implementation throws an exception on tryDivide 1 0. Here's an alternative implementation that works:
let tryDivide (a:int) (b:int) =
if b = 0
then None
else Some (a / b)
FSI:
> tryDivide 1 0;;
val it : int option = None
> tryDivide 10 5;;
val it : int option = Some 2

Why is Type Annotation Required Here?

This function has the signature: (UnionCaseInfo -> bool) -> 'T option
let private findCase<'T> f =
match FSharpType.GetUnionCases typeof<'T> |> Array.filter f with
|[|case|] -> Some (FSharpValue.MakeUnion(case,[||]) :?> 'T)
|_ -> None
This function, which calls the above function, has the signature: int -> obj
let CreateFromId<'T> id =
match findCase (fun case -> case.Tag = id) with
| Some c -> c
| None -> failwith (sprintf "Lookup for union case by \"%d\" failed." id)
In the pattern for CreateFromId, intellisense shows that c is inferred to be of type obj, even though it shows the correct signature for findCase. Why does the type seem to have been "lost" in the pattern?
(I can workaround this by specifying the return type of CreateFromId to 'T)
Because the type parameter 'T is not referenced in the body of the function, so type inference has no way to know your intention was to name 'T the return type.
So you can either add it in the body as the return type (as you already figured it out) or remove it from the declaration:
let CreateFromId id = ...
By removing it works because F# does automatic generalization, the only different is it will use an arbitrary name for the type variable, but even if you want to name that type variable 'T what I would do is add it as a return type but no in the declaration between brackets:
let CreateFromId id : 'T = ...
The type of CreateFromId was inferred to by int -> obj because there's nothing "linking" the two 'T type arguments on your functions.
findCase is properly generic over 'T, but CreateFromId is only declared to be generic, and the generic type argument is never used.
Annotating the function with the desired type is good enough to make the types line up. You can also call findCase explicitly providing the type:
let CreateFromId<'T> id =
match findCase<'T> (fun case -> case.Tag = id) with
| Some c -> c
| None -> failwith (sprintf "Lookup for union case by \"%d\" failed." id)
Or as the other answer suggests, just drop the 'T from CreateFromId and let type inference do its thing.

Where to add type annotations to curried functions?

In F# interactive, I can find the type of sprintf.
>sprintf;;
val it : (Printf.StringFormat<'a> -> 'a) = <fun:clo#163>
I can find the type of sprintf curried with the first parameter, if the curried function is not generic.
> sprintf "%g";;
val it : (float -> string) = <fun:it#134-16>
But if it is generic, then I get the value restriction error.
> sprintf "%A";;
error FS0030: Value restriction. The value 'it' has been inferred to have generic type
val it : ('_a -> string)
Either make the arguments to 'it' explicit or, if you do not intend for it to be generic, add a type annotation.
I can add a type annotation to get rid of the value restriction like this, specializing the function for a type, eg. DateTime.
>let f : (DateTime -> string) = sprintf "%A";;
val f : (DateTime -> string)
How can I add the type annotation without the binding? I've tried the following ...
>sprintf "%A" : (DateTime -> string);;
error FS0010: Unexpected symbol ':' in interaction. Expected incomplete structured construct at or before this point, ';', ';;' or other token.
This is a similar example but harder ...
>sprintf "%a";;
error FS0030: Value restriction. The value 'it' has been inferred to have generic type
val it : ((unit -> '_a -> string) -> '_a -> string)
Either make the arguments to 'it' explicit or, if you do not intend for it to be generic, add a type annotation.
You just need to enclose your expression in parenthesis:
open System;;
(sprintf "%A" : DateTime -> string);;
val it : (DateTime -> string) = <fun:it#2>
That way you can specify the type annotation without the binding.
What is actually happening is that fsi binds the last thing you type to a variable called it. It effectively does
let it = sprintf "%a";;
Type annotations would need to go on the left hand side of the = which you can't access. The problem is that you need a concrete type to give to any variable (in this case it). A workaround could be
(fun t:DateTime -> sprintf "%a" t)

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".

Resources