F# Issue when trying to pipe multiple values between functions - f#

I am trying to refactor this code:
for projPath in projectAssemblyInfoPaths do
let assemblyInfoLines = ReadWholeFileFn(projPath)
let updatedAssemblyInfoLines = UpdateFileFn(assemblyInfoLines, newVersion);
SaveFileFn(updatedAssemblyInfoLines, projPath);
To look something like this:
for projPath in projectAssemblyInfoPaths do
((ReadWholeFileFn(projPath), newVersion) ||> UpdateFileFn, projPath) ||> SaveFileFn
But, I'm getting type mismatch for both UpdateFileFn and SaveFileFn
UpdateFileFn:
FS0001: Type mismatch. Expecting a
'string [] -> string -> 'a'
but given a
'string [] * string -> string []'
The type 'string []' does not match the type 'string [] * string'
SaveFileFn:
FS0001: Type mismatch. Expecting a
'string [] * string -> string -> 'a'
but given a
'string [] * string -> unit'
The type 'string -> 'a' does not match the type 'unit'
And these are my function definitions:
let ReadWholeFileFn (filePath: string): string[];
let UpdateFileFn (lines: string[], newVersion: string): string[];
let SaveFileFn (lines: string[], fileName: string): unit;
I have tried a simple piece of code like:
(1, 2) ||> prinf "%d%d"
And that works fine, not sure what I'm missing when working with functions.

You do not really need the ||> operator for this. You can bind the arguments using partially applied functions which I think is appropriate for your case. However then you need to change the signatures so the most chaning part of your function is the last argument. Then it becomes like below
let ReadWholeFileFn2 (filePath: string): string[]
let UpdateFileFn2 (newVersion: string) (lines: string[]): string[]
let SaveFileFn2 (fileName: string) (lines: string[]) : unit
for projPath in projectAssemblyInfoPaths do
ReadWholeFileFn2 projPath
|> UpdateFileFn2 newVersion
|> SaveFileFn2 projPath
A clean F# pipeline.

Related

Can't get bind operator to work with discriminated union

I'm trying to use the "bind operator" (>>=) in my code.
If I use the operator I get a compile error, if I instead "inline" what the operator is supposed to do, it works.
type TestDI =
private
| A of string list
| B of int list
with
static member (>>=) (x: string list, f: TestDI -> 'a) =
f <| A x
let func (t: TestDI) =
match t with
| A _ -> "a"
| B _ -> "b"
// Expecting a type supporting the operator '>>=' but given a function type.
// You may be missing an argument to a function.
["a"] >>= func
// works
func <| A ["a"]
Obviously I'm missing something, can someone please help?
When you use an operator, F# looks for it in order:
as a let-defined operator;
as a static member-defined operator on one of the two arguments' types. Here, the arguments you are passing to the operator are string list and TestDI -> string, so it won't look at the one you defined on TestDI.
So here the solution would be to let-define it instead:
type TestDI =
private
| A of string list
| B of int list
let (>>=) (x: string list) (f: TestDI -> 'a) =
f <| A x

type mismatch error for async chained operations

Previously had a very compact and comprehensive answer for my question.
I had it working for my custom type but now due to some reason I had to change it to string type which is now causing type mismatch errors.
module AsyncResult =
let bind (binder : 'a -> Async<Result<'b, 'c>>) (asyncFun : Async<Result<'a, 'c>>) : Async<Result<'b, 'c>> =
async {
let! result = asyncFun
match result with
| Error e -> return Error e
| Ok x -> return! binder x
}
let compose (f : 'a -> Async<Result<'b, 'e>>) (g : 'b -> Async<Result<'c, 'e>>) = fun x -> bind g (f x)
let (>>=) a f = bind f a
let (>=>) f g = compose f g
Railway Oriented functions
let create (json: string) : Async<Result<string, Error>> =
let url = "http://api.example.com"
let request = WebRequest.CreateHttp(Uri url)
request.Method <- "GET"
async {
try
// http call
return Ok "result"
with :? WebException as e ->
return Error {Code = 500; Message = "Internal Server Error"}
}
test
type mismatch error for the AsyncResult.bind line
let chain = create
>> AsyncResult.bind (fun (result: string) -> (async {return Ok "more results"}))
match chain "initial data" |> Async.RunSynchronously with
| Ok data -> Assert.IsTrue(true)
| Error error -> Assert.IsTrue(false)
Error details:
EntityTests.fs(101, 25): [FS0001] Type mismatch. Expecting a '(string -> string -> Async<Result<string,Error>>) -> 'a' but given a 'Async<Result<'b,'c>> -> Async<Result<'d,'c>>' The type 'string -> string -> Async<Result<string,Error>>' does not match the type 'Async<Result<'a,'b>>'.
EntityTests.fs(101, 25): [FS0001] Type mismatch. Expecting a '(string -> string -> Async<Result<string,Error>>) -> 'a' but given a 'Async<Result<string,'b>> -> Async<Result<string,'b>>' The type 'string -> string -> Async<Result<string,Error>>' does not match the type 'Async<Result<string,'a>>'.
Edit
Curried or partial application
In context of above example, is it the problem with curried functions? for instance if create function has this signature.
let create (token: string) (json: string) : Async<Result<string, Error>> =
and then later build chain with curried function
let chain = create "token" >> AsyncResult.bind (fun (result: string) -> (async {return Ok "more results"}))
Edit 2
Is there a problem with following case?
signature
let create (token: Token) (entityName: string) (entityType: string) (publicationId: string) : Async<Result<string, Error>> =
test
let chain = create token >> AsyncResult.bind ( fun (result: string) -> async {return Ok "more results"} )
match chain "test" "article" "pubid" |> Async.RunSynchronously with
Update: At the front of the answer, even, since your edit 2 changes everything.
In your edit 2, you have finally revealed your actual code, and your problem is very simple: you're misunderstanding how the types work in a curried F# function.
When your create function looked like let create (json: string) = ..., it was a function of one parameter. It took a string, and returned a result type (in this case, Async<Result<string, Error>>). So the function signature was string -> Async<Result<string, Error>>.
But the create function you've just shown us is a different type entirely. It takes four parameters (one Token and three strings), not one. That means its signature is:
Token -> string -> string -> string -> Async<Result<string, Error>>
Remember how currying works: any function of multiple parameters can be thought of as a series of functions of one parameter, which return the "next" function in that chain. E.g., let add3 a b c = a + b + c is of type int -> int -> int -> int; this means that add3 1 returns a function that's equivalent to let add2 b c = 1 + b + c. And so on.
Now, keeping currying in mind, look at your function type. When you pass a single Token value to it as you do in your example (where it's called as create token, you get a function of type:
string -> string -> string -> Async<Result<string, Error>>
This is a function that takes a string, which returns another function that takes a string, which returns a third function which takes a string and returns an Async<Result<whatever>>. Now compare that to the type of the binder parameter in your bind function:
(binder : 'a -> Async<Result<'b, 'c>>)
Here, 'a is string, so is 'b, and 'c is Error. So when the generic bind function is applied to your specific case, it's looking for a function of type string -> Async<Result<'b, 'c>>. But you're giving it a function of type string -> string -> string -> Async<Result<string, Error>>. Those two function types are not the same!
That's the fundamental cause of your type error. You're trying to apply a function that returns a function that returns function that returns a result of type X to a design pattern (the bind design pattern) that expects a function that returns a result of type X. What you need is the design pattern called apply. I have to leave quite soon so I don't have time to write you an explanation of how to use apply, but fortunately Scott Wlaschin has already written a good one. It covers a lot, not just "apply", but you'll find the details about apply in there as well. And that's the cause of your problem: you used bind when you needed to use apply.
Original answer follows:
I don't yet know for a fact what's causing your problem, but I have a suspicion. But first, I want to comment that the parameter names for your AsyncResult.bind are wrong. Here's what you wrote:
let bind (binder : 'a -> Async<Result<'b, 'c>>)
(asyncFun : Async<Result<'a, 'c>>) : Async<Result<'b, 'c>> =
(I moved the second parameter in line with the first parameter so it wouldn't scroll on Stack Overflow's smallish column size, but that would compile correctly if the types were right: since the two parameters are lined up vertically, F# would know that they are both belonging to the same "parent", in this case a function.)
Look at your second parameter. You've named it asyncFun, but there's no arrow in its type description. That's not a function, it's a value. A function would look like something -> somethingElse. You should name it something like asyncValue, not asyncFun. By naming it asyncFun, you're setting yourself up for confusion later.
Now for the answer to the question you asked. I think your problem is this line, where you've fallen afoul of the F# "offside rule":
let chain = create
>> AsyncResult.bind (fun (result: string) -> (async {return Ok "more results"}))
Note the position of the >> operator, which is to the left of its first operand. Yes, the F# syntax appears to allow that in most situations, but I suspect that if you simply change that function definition to the following, your code will work:
let chain =
create
>> AsyncResult.bind (fun (result: string) -> (async {return Ok "more results"}))
Or, better yet because it's good style to make the |> (and >>) operators line up with their first operand:
let chain =
create
>> AsyncResult.bind (fun (result: string) -> (async {return Ok "more results"}))
If you look carefully at the rules that Scott Wlaschin lays out in https://fsharpforfunandprofit.com/posts/fsharp-syntax/, you'll note that his examples where he shows exceptions to the "offside rule", he writes them like this:
let f g h = g // defines a new line at col 15
>> h // ">>" allowed to be outside the line
Note how the >> character is still to the right of the = in the function definition. I don't know exactly what the F# spec says about the combination of function definitions and the offside rule (Scott Wlaschin is great, but he's not the spec so he could be wrong, and I don't have time to look up the spec right now), but I've seen it do funny things that I didn't quite expect when I wrote functions with part of the function definition on the same line as the function, and the rest on the next line.
E.g., I once wrote something like this, which didn't work:
let f a = if a = 0 then
printfn "Zero"
else
printfn "Non-zero"
But then I changed it to this, which did work:
let f a =
if a = 0 then
printfn "Zero"
else
printfn "Non-zero"
I notice that in Snapshot's answer, he made your chain function be defined on a single line, and that worked for him. So I suspect that that's your problem.
Rule of thumb: If your function has anything after the = on the same line, make the function all on one line. If your function is going to be two lines, put nothing after the =. E.g.:
let f a b = a + b // This is fine
let g c d =
c * d // This is also fine
let h x y = x
+ y // This is asking for trouble
I would suspect that the error stems from a minor change in indentation since adding a single space to an FSharp program changes its meaning, the FSharp compiler than quickly reports phantom errors because it interprets the input differently. I just pasted it in and added bogus classes and removed some spaces and now it is working just fine.
module AsyncResult =
[<StructuralEquality; StructuralComparison>]
type Result<'T,'TError> =
| Ok of ResultValue:'T
| Error of ErrorValue:'TError
let bind (binder : 'a -> Async<Result<'b, 'c>>) (asyncFun : Async<Result<'a, 'c>>) : Async<Result<'b, 'c>> =
async {
let! result = asyncFun
match result with
| Error e -> return Error e
| Ok x -> return! binder x
}
let compose (f : 'a -> Async<Result<'b, 'e>>) (g : 'b -> Async<Result<'c, 'e>>) = fun x -> bind g (f x)
let (>>=) a f = bind f a
let (>=>) f g = compose f g
open AsyncResult
open System.Net
type Assert =
static member IsTrue (conditional:bool) = System.Diagnostics.Debug.Assert(conditional)
type Error = {Code:int; Message:string}
[<EntryPoint>]
let main args =
let create (json: string) : Async<Result<string, Error>> =
let url = "http://api.example.com"
let request = WebRequest.CreateHttp(Uri url)
request.Method <- "GET"
async {
try
// http call
return Ok "result"
with :? WebException as e ->
return Error {Code = 500; Message = "Internal Server Error"}
}
let chain = create >> AsyncResult.bind (fun (result: string) -> (async {return Ok "more results"}))
match chain "initial data" |> Async.RunSynchronously with
| Ok data -> Assert.IsTrue(true)
| Error error -> Assert.IsTrue(false)
0

Deconstructing Discriminated Unions

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

How to call a function from the quotation of a invoke in type provider?

I have a type provider which gives me the error "Type mismatch when splicing expression into quotation literal".
I extracted the code below in order to reproduce the issue in a smaller context.
let f (s : string) : string = s //some dummy implementation
let t = ProvidedTypeDefinition(asm, ns, "Root", Some typeof<obj>)
let ctor = ProvidedConstructor(parameters = [],
InvokeCode = (fun args -> <## "root" :> obj ##>)) :> MemberInfo
let prop = ProvidedProperty(propertyName = "SomeProperty",
parameters = [],
propertyType = typeof<string>,
GetterCode = (fun args -> <## f %%(args.[0]) ##>)) :> MemberInfo
do t.AddMembers [ctor; prop]
t.SetBaseType typeof<obj>
...and when I use the type provider like
let root = Provided.Root()
let a = root.SomeProperty
I get the error:
Error: The type provider 'typeproviders.providerpoc+MyProvider' reported an error in the context of provided type
'typeproviders.providerpoc.Provided.Root', member 'get_Menu'.
The error: Type mismatch when splicing expression into quotation
literal.
The type of the expression tree being inserted doesn't match
the type expected by the splicing operation.
Expected 'System.Object', but received type 'System.String'.
Consider type-annotating with the expected expression type, e.g., (%% x :
string) or (%x : string).. Parameter name: receivedType.
How can I write the quotation to be able to call a function inside the quotation?
Thanks!
What the error message is saying is that you are putting a quoted expression of type obj in a place where a quoted expression of type string is expected.
I suspect this is happening when creating the GetterCode in the provided property:
GetterCode = (fun args -> <## f %%(args.[0]) ##>)
Here, args is an array of quoted expressions where each expression is of type obj, but the function f expects a string and so the quotation filling the hole using %% should be of type string
Adding a type conversion that would turn the obj into string should do the trick:
GetterCode = (fun args -> <## f (unbox<string> (%%(args.[0]))) ##>)

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)

Resources