I have the next code (a parameter of a method) in C#:
Func<Func<string, Stream>, Action<string, string>, T> save
But I don't know if in F# is
save: ((string -> Stream) -> (string -> string -> unit)) -> 'T
or
save: (string -> Stream) -> (string -> string -> unit) -> 'T
Let's say that string -> Stream is 'a and string -> string -> unit is 'b and 'T is 'c.
Now that we've removed the higher order functions this is simpler to think about and the question boils down to this: What's the difference between the following type signatures?
('a -> 'b) -> 'c
'a -> 'b -> 'c
The first type is a function that takes a function from 'a to 'b as its only argument. The second one is a function that takes an 'a as its first argument and a 'b as its second argument. The second one is compatible with the C# type signature Func<A, B, C>.
Related
While playing around with map, apply and such to better understand them, the inferred generic types on the following code turned out not be what I expected, but the code still works:
Note: the // comments are written by me so I can show them, but they are the exact copy of the auto annotations printed by Ionide.
let map2 fOk fErr (a : Result<'a,'e>) (b : Result<'b,'e>)= // ('a -> 'b -> 'a0) -> ('e -> 'e -> 'e) -> Result<'a,'e> -> Result<'b,'e> -> Result<'a,'e>
match a, b with
| Ok aOk, Ok bOk -> fOk aOk bOk |> Ok
| Error aErr, Error bErr -> fErr aErr bErr |> Error
| Error aErr, _ -> Error aErr
| _, Error bErr -> Error bErr
let lift2ResultFromMap2 f= // ('a -> 'b -> 'c) -> (Result<'a,'d list> -> Result<'b,'d list> -> Result<'c,'d list>)
map2 f List.append
Here I expected that the return type of map2 is Result<'a0,'e> instead of Result<'a,'e>.
But then when I use it in defining lift2ResultFromMap2 the return type is exactly what I expect, Result<'c,'d list> instead of Result<'a,'d list> that map2 would suggest.
Am I right to think this is an IDE bug? or is there some additional wildcard like meaning to the numbered suffix?
Your constraints are respected first.
Now for your example, it tried to pick 'a for the first unconstrained value, which is the return type of fOk, saw 'a was already in use, so it went with 'a0. So, if you let it pick the constraints with:
(a : Result<_,'e>) (b : Result<_,'e>)
fOk will be inferred to be 'a -> 'b -> 'c. If you have enough values to infer up to 'e, you'll end up seeing an 'e0.
I have a discriminated-union type of the form
type ParameterName = string
type ParameterValues =
| String of string[]
| Float of float[]
| Int of int[]
type Parameter = Parameter of ParameterName * ParameterValues
I want to pass the ParameterValues part to a function taking generic arguments returning unit, such as
let func1 (name:string) (data:'a) = printfn "%s" name
To deconstruct Parameter I could wrap func1 like this
let func2 (Parameter (name, values)) =
match values with
| String s -> func1 name s
| Float s -> func1 name s
| Int s -> func1 name s
however this is inconvenient if I have to do this for multiple functions. Instead, I would like to define a more flexible wrapper like this:
let func3 (fn: ('a -> 'b -> unit)) (Parameter (name, values)) =
match values with
| String s -> fn name s
| Float s -> fn name s
| Int s -> fn name s
This however fails, as the type of b gets restricted to string[] in the first option of the match expression; consequently the match expression fails with the error Type string does not match type float.
Is this expected? How can I work around this problem?
This is an expected behaviour. The problem is that you cannot directly pass a generic function as an argument to another function in F#. When you define a function as follows:
let func3 (fn: ('a -> 'b -> unit)) (Parameter (name, values)) = (...)
... you are defining a generic function func3 that has two generic parameters and, when those are specified, can be called with a given function and a parameter. This can be written as:
\forall 'a, 'b . (('a -> 'b -> unit) -> Parameter -> unit)
What you would need to do is to make those type parameters not top-level, but make the first parameter itself a generic function. You could write this as:
(\forall 'a, 'b . ('a -> 'b -> unit)) -> Parameter -> unit
This can be clumsily written in F# using interfaces:
type IFunction<'a> =
abstract Invoke<'b> : 'a -> 'b -> unit
let func1 =
{ new IFunction<string> with
member x.Invoke<'b> name (data:'b) = printfn "%s" name }
let func3 (fn: IFunction<string>) (Parameter (name, values)) =
match values with
| String s -> fn.Invoke name s
| Float s -> fn.Invoke name s
| Int s -> fn.Invoke name s
In practice, your function does not really need to be generic, because you are never using the second argument - but you could probably achieve pretty much anything that you can achieve with this interfaces trick just by passing the data as obj and your code would be significantly simpler than this monstrosity!
Following the suggestions by Lee and Tomas, I came up with the following solution:
type Parameter = Parameter of string * obj
let func0 (name:string) (data:obj) = printfn "%s %A" name data
let func1 (fn: string->obj->unit) (Parameter (name, value)) =
fn name value
let p1 = Parameter ("p1", [|"a"; "b"|])
let p2 = Parameter ("p1", [|1.; 2.|])
func1 func0 p1
func1 func0 p2
The normal Option.bind function is defined like this:
// ('T -> 'U option) -> 'T option -> 'U option
let bind f x =
match x with
| None -> None
| Some x' -> f x'
I am in need of a slightly different version of this:
// ('T -> 'U -> 'U option) -> 'T option -> ('U -> 'U option)
let myBind f x =
match x with
| None -> Some
| Some x' -> f x'
Does this function have a standard name? (If not I'll gladly accept concise suggestions.)
For interested readers, my usecase is this: I have a business object (say Order) I want to apply a constraint to (order date, customer, etc.). The functions that apply constraints to e.g. an Order have signature 'Constraint -> Order -> Order option. They return Some order if the order passed the constraint, and None otherwise (and can thus be composed using Option.bind). Now, if the constraint is None, it's essentially a no-op and Some order should be returned. Instead of having all constraint functions accept 'Constraint option I can factor out this part of the logic using the function above (which then need to be applied during composition).
I originally called the function something like bindConstraint, but it's completely generic and really has nothing to do with my usecase (ref. Mark Seemann's article on the topic). Due to its similarity with bind I wondered if there is a standard name for this.
Let's look at your function type signature:
// ('T -> 'U -> 'U option) -> 'T option -> ('U -> 'U option)
The 'U -> 'U option type can actually be factored out of that signature. Let's call that type 'V. Then the type signature becomes:
// ('T -> 'V) -> 'T option -> 'V
Which looks rather similar to Option.map, whose signature is:
// ('T -> 'V) -> 'T option -> 'V option
So basically, your function is equivalent to Option.map followed by a defaultArg to turn that 'V option into a 'V (supplying a default value if the option was None).
So I'd probably call it something like defaultMap or mapDefault.
hmmm...
I was wondering if you factor out the 'U, that you end up working inside a reader monad, and then your function IS option.bind there's nothing to do!
'U -> .....('T -> 'U option) -> 'T option -> 'U option
i.e. this is a constraint on string?
let longerThan : int -> string -> string option =
fun i s ->
if (String.length s > i) then
Some s
else
None
and this is you using myBind
let foo = myBind longerThan (Some 1)
but this is the code without myBind
let bar =
fun s ->
(Some 1) |> Option.bind (fun i -> longerThan i s)
we've just factored out the 'U and we write our code inside a function.
(my code looks more complex because we've written longer than to work nicely with your bind, if we swap the parameter order, then your version looks more complex than mine)
my other observation is if you are just doing constraints, then isn't using
'U -> bool
a bit simpler?...and then you just use "where"...(can you do this in a computational expression?...you can in LINQ I think).
P.S.
actually the semantics of your code Is different...you want None to be interpreted as the identity map....
so actually I think in your world I would operate on the constraint directly
type Constraint<'a> = 'a -> 'a option
let applyConstraint : Constraint<'a> option -> 'a -> 'a option =
function
| None -> Some
| Some f -> f
and then simply compose constraints;
let compose : Constraint<'a> -> Constraint<'a> -> Constraint<'a> =
fun c1 c2 -> fun a -> Option.bind c2 (c1 a)
(though I may have c1 and c2 the wrong way around)
I'm working with fsharp type inferencing and I'm trying to understand how they work. Why is it that
List.filter List.head
is type bool list list -> bool list list?
List.filter has the type (just enter List.filter;; in FSI):
> List.filter;;
val it : (('a -> bool) -> 'a list -> 'a list)
so it takes a 'a -> bool and results in a 'a list -> 'a list
Now you feed it with
> List.head;;
val it : ('b list -> 'b)
(it's another 'a really so I renamed it) and now you have:
'a -> bool ~ 'b list -> 'b
you can unify this and see:
'b ~ bool (from the right-hand-sides of ->)
'a ~ 'b list ~ bool list (from the left-hand-side)
but this all together and you get the answer F#`s type-inference gives you:
'a list -> 'a list
~ ('b list) list -> ('b list) list
~ (bool list) list -> (bool list) list
~ bool list list -> bool list list
I can define a generic signature type and define a function which implements that signature:
[<Measure>] type ms
type timer<'a,'b> = ('a -> 'b) -> 'a -> 'b * int64<ms>;;
let timeit:timer<'a,'b> = fun f x -> ((f x), 1L<ms>);;
I can also put that type definition in an F# signature file (.fsi).
module Utils =
type timer<'a,'b> = ('a -> 'b) -> 'a -> 'b * int64<ms>
But when I try to use that type definition in the implementation file, the compiler says 'The type "timer" is not defined'.
[<Measure>] type ms
module Utils =
let timeit:timer<'a,'b> = fun f x -> ((f x), 1L<ms>);;
Is this the expected behavior?
You need to declare your timer<_, _> type in the .fs file too.
Basically, the signature file is a convenience to document the public API of the source file, but it doesn't by itself declare anything that can be used in the source file. If this is not something you need, you can just drop the signature file and only use a source file.