Writing unit tests against PrintfFormat - f#

I have a type that I'm trying to understand by writing unit tests against it, however I can't reason what to do with PrintfFormat
type ValueFormat<'p,'st,'rd,'rl,'t,'a> = {
format: PrintfFormat<'p,'st,'rd,'rl,'t>
paramNames: (string list) option
handler: 't -> 'a
}
with
static member inline construct (this: ValueFormat<_,_,_,_,_,_>) =
let parser s =
s |> tryKsscanf this.format this.handler
|> function Ok x -> Some x | _ -> None
let defaultNames =
this.format.GetFormatterNames()
|> List.map (String.replace ' ' '_' >> String.toUpperInvariant)
|> List.map (sprintf "%s_VALUE")
let names = (this.paramNames ?| defaultNames) |> List.map (sprintf "<%s>")
let formatTokens = this.format.PrettyTokenize names
(parser, formatTokens)
I feel confident that I can figure everything out but PrintfFormat is throwing me with all those generics.
The file I'm looking at for the code I want to unit test is here for the FSharp.Commandline framework.
My question is, what is PrintfFormat and how should it be used?
A link to the printf.fs file is here. It contains the definition of PrintfFormat

The PrintfFormat<'Printer,'State,'Residue,'Result,'Tuple> type, as defined in the F# source code, has four type parameters:
'Result is the type that your formatting/parsing function produces. This is string for sprintf
'Printer is a type of a function generated based on the format string, e.g. "%d and %s" will give you a function type int -> string -> 'Result
'Tuple is a tuple type generated based on the format string, e.g. "%d and %s" will give you a tuple type int * string.
'State and 'Residue are type parameters that are used when you have a custom formatter using %a, but I'll ignore that for now for simplicity (it's never needed unless you have %a format string)
There are two ways of using the type. Either for formatting, in which case you'll want to write a function that returns 'Printer as the result. The hard thing about this is that you need to construct the return function using reflection. Here is an example that works only with one format string:
open Microsoft.FSharp.Reflection
let myformat (fmt:PrintfFormat<'Printer,obj,obj,string,'Tuple>) : 'Printer =
unbox <| FSharpValue.MakeFunction(typeof<'Printer>, fun o ->
box (o.ToString()) )
myformat "%d" 1
myformat "%s" "Yo"
This simply returns the parameter passed as a value for %d or %s. To make this work for multiple arguments, you'd need to construct the function recursively (so that it's not just e.g. int -> string but also int -> (int -> string))
In the other use, you define a function that returns 'Tuple and it needs to create a tuple containing values according to the specified formatting string. Here is a small sample that only handles %s and %d format strings:
open FSharp.Reflection
let myscan (fmt:PrintfFormat<'Printer,obj,obj,string,'Tuple>) : 'Tuple =
let args =
fmt.Value
|> Seq.pairwise
|> Seq.choose (function
| '%', 'd' -> Some(box 123)
| '%', 's' -> Some(box "yo")
| _ -> None)
unbox <| FSharpValue.MakeTuple(Seq.toArray args, typeof<'Tuple>)
myscan "%d %s %d"

Related

A function that accepts multiple types

I am fairly new to f#, but I want to know if it is possible to make a function that accepts multiple types of variables.
let add x y = x + y
let integer = add 1 2
let word = add "He" "llo"
Once a function use a type of variable it cannot accept another one.
You need to read about statically resolved type parameters and inline functions. It allows to create functions which may take any type that supports operation and/or have member. So your add function should be defined this way:
let inline add x y = x + y
Don't overuse inlined functions because their code inlined in call site and may increase assembly size, but may increase performance (test each case, don't make predictions!). Also inlined function are supported only by F# compiler and may not work with other languages (important when designing libraries).
Example of SRTP magic:
let inline (|Parsed|_|) (str: string) =
let mutable value = Unchecked.defaultof<_>
let parsed = ( ^a : (static member TryParse : string * byref< ^a> -> bool) (str, &value))
if parsed then
Some value
else
None
match "123.3" with
| Parsed 123 -> printfn "int 123"
| Parsed 123.4m -> printfn "decimal 123.4"
| Parsed 123.3 -> printfn "double 123.3"
// | Parsed "123.3" -> printfn "string 123.3" // compile error because string don't have TryParse static member
| s -> printfn "unmatched %s" s

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

How to interpret a function signature

I am still confused on how to read function signatures.
The Option.map signature is the following:
/// map f inp evaluates to match inp with None -> None | Some x -> Some (f x).
/// mapping: A function to apply to the option value.
/// option: The input option.
val map : mapping:('T -> 'U) -> option:'T option -> 'U option
However, I have no clue what that signature means.
I read it as the following:
There's a function called map that takes a function as an input that we'll call "mapping" and it will yield a result that is also a function that we'll call "option".
Mapping Parameter:
mapping:('T -> 'U)
The function that we pass in as input takes Titanium (i.e. 'T) as the input and yields Uranium (i.e. 'U) as output.
The Option returned
option:'T option -> 'U option
We'll call the output of the map function "option".
Thus, this "option" that is returned from executing the map function is also a function as referenced above. It's takes a Titanium option and yields a Uranium option.
Example:
type String20 = String20 of string
type Name = { First:String20
Last:String20
Suffix:String20 option }
let tryCreateName (first:string) (last:string) (suffix:string option) =
let isValid = [first; last]
|> List.forall (fun x -> x.Length > 2 && x.Length <= 20)
if isValid then
Some { First = String20(first);
Last = String20(last);
Suffix = Option.map String20 suffix }
else None
How does the following expression map:
Option.map String20 suffix
Based on the expression above, where is the "returned function" of Titanium option -> Uranium option?
First off take a look at Option.map<'T,'U> Function (F#) and notice
The expression map f inp evaluates to match inp with None -> None |
Some x -> Some (f x).
So lets convert this comment to working code. First map is a method of the type Option, but to make it easier we will make it a function outside of a type and to avoid conflicts with other map functions we will give it the name OptionMap.
let OptionMap = f inp =
match inp with
| None -> None
| Some x -> Some (f x)
Now to get the signature of this function just send it to F# Interactive
val OptionMap : f:('a -> 'b) -> inp:'a option -> 'b option
and to make the types obvious we will type the parameters of the function.
let optionMap (f : ('a -> 'b)) (inp : 'a option) : 'b option =
match inp with
| None -> None
| Some x -> Some (f x)
Now to test this we can use
let (test : String20 option) = optionMap String20 (Some("something"))
printfn "test: %A" test
// test: Some (String20 "something")
So what happened in OptionMap that allowed this to work
If we add some print statements let sees what happens
let optionMap (f : ('a -> 'b)) (inp : 'a option) : 'b option =
printfn "f: %A" f
printfn "inp: %A" inp
match inp with
| None -> None
| Some x ->
let result = Some (f x)
printfn "result: %A" result
result
we get
f: <fun:test#63>
inp: Some "something"
result: Some (String20 "something")
we see that f is a function which is String20
So how can String20 be a function?
If we send String20 to F# Interactive it gives.
> String20;;
val it : arg0:string -> String20 = <fun:clo#4>
So String20 is a function that takes a string and returns a type of String20. That smells of a constructor.
Let's test that out in F# interactive.
> let test1 = String20 "something";;
val test1 : String20 = String20 "something"
Sure enough, String20 is a constructor, but we didn't specifically create a constructor as is done in the Object-Oriented world.
You have to think of a type, even a discriminated union as having constructors. The constructors are not specifically written but do exist. So String20 is a constructor that takes one value, a string, which is a function with the correct type signature for the Option.map function.
I gave the answer a lot more detail so that one can learn a process on how to break down problems and look at the inner workings as a tool to solving these kinds of problems.
To learn more about the lower level details of how functional programming works one needs to understand lambda calculus. The best way I know to learn it is to read An Introduction To Functional Programming Through Lambda Calculus by Greg Michaelson and look at the info in the lambda calculus tag.
Also if you like the book, you should buy a copy instead of using the free version.
There are two ways of looking at it.
let f x y = x + y
// val f: x:int -> y:int -> int
One way is to say that function f takes two parameters, x of type int and y of type int, and returns an int. So I can supply two arguments and get the result:
let a = f 4 5
// val a: int = 9
The other way is that the function takes one parameter, x of type int, and returns another function, which takes one parameter, y of type int, and returns an int. So I can supply one argument and get a function as result:
let b = f 4
// val b: int -> int
Mathematically, it's always the second way - all functions are one-parameter functions. This notion is very convenient for programming with higher-order functions.
But the first way is usually more understandable to humans, so you will often see functions discussed as if they take multiple parameters.
String20 is the case constructor for the String20 Discriminated Union case. It's a function with the type string -> String20. So string takes the place of 'T, and String20 takes the place of 'U in the mapping you supply to Option.map.
Generally, if you have a function T1 -> T2 -> ... and you apply it to one parameter, you get a function T2 -> .... In the case of Option.map, T1 is itself a function, but that is of no consequence to the way the arguments are applied.
I find it confusing to call a function "option", a string "Titanium" and a type called String20 "Uranium", so I'll stick with type names.
You ask where the "returned function" that maps a string option to a String20 option is in the expression Option.map String20 suffix. It is simply
Option.map String20
Since String20 constructs a String20 from a string, Option.map String20 is a function that maps a string to a String20 if it is present, and to None otherwise.
If you write Option.map String20 suffix, this function is applied to suffix, which would be a string option. The result of this expression is a String20 option.

Can I use StringFormat as TextWriterFormat? kfprintf / kprintf usage

I've got function to log to console
Printf.kprintf
(printfn
"[%s][%A] %s"
<| level.ToString()
<| DateTime.Now)
format // fprint to System.Console.Out maybe
but it's using Printf.StringFormat as format and now I want to follow same logic and print it to file.
So I try
Printf.kfprintf
(fun f ->
fprintfn file "[%s][%A] "
<| level.ToString()
<| DateTime.Now
) file (format)
And there are two things I can't understand. Why there is unit -> 'A instead of string -> 'A ? How should I use it? And Can I use my StringFormat here as TextWriterFormat ?
Another trouble with this is that with first snippet I inherit format to string -> 'Result thing but in kfprintf I can't do it because there is unit -> 'Result and format message appears before [x][x] stuff. I guess I can somehow inherit format to f but I can't find good example, the only I found is part of F# compiler:
[<CompiledName("PrintFormatToTextWriter")>]
let fprintf (os: TextWriter) fmt = kfprintf (fun _ -> ()) os fmt
[<CompiledName("PrintFormatLineToTextWriter")>]
let fprintfn (os: TextWriter) fmt = kfprintf (fun _ -> os.WriteLine()) os fmt
But how can I use this unit ? How can I post message after my message?
I don't think you need to use Printf.kfprintf, you can carry on using Printf.kprintf as the inner fprintfn uses the TextWriter.
let logToWriter writer level format =
Printf.kprintf (fprintfn writer "[%s][%A] %s"
<| level.ToString()
<| System.DateTime.Now) format
Also see this for an example of using Printf.kfprintf.

F# apply sprintf to a list of strings

How can I best create a function (lets call it myPrint) that takes sprintf, a format string, and a list of strings as arguments and produces a result such that each element in the list of strings is applied/folded into sprintf?
i.e.
myPrint (sprintf "one: %s two: %s three: %s") ["first"; "second"; "third"];;
would produce the output
val myPrint : string = "one: first two: second three: third"
Note that format strings in F# are specifically designed to take a statically known number of arguments, so there's not a particularly elegant way to do what you want. However, something like this should work:
let rec myPrintHelper<'a> (fmt:string) : string list -> 'a = function
| [] -> sprintf (Printf.StringFormat<_>(fmt))
| s::rest -> myPrintHelper<string -> 'a> fmt rest s
let myPrint fmt = myPrintHelper<string> fmt
myPrint "one: %s two: %s three: %s" ["first"; "second"; "third"]
This will throw an exception at runtime if the number of "%s"es in your string doesn't match the number of strings in the list.
List.map myPrint ["first"; "second"; "third;"]
Will return a new sprintf'ed List...
To just print out to console... List.iter will iterate a function over each value in a list, and perform that function - but will only work with functions that don't return a value. (i.e. printf...)
map takes an 'T -> 'U - and will convert a list of any format into any other
So
let myPrint in = sprintf "%s" in
would be allowed, but reading your question again - it doesn't do what you want.
- you could use mapi, which adds the index in (int -> 'T -> 'U) and would let you define myPrint as
let myPrint index val = sprintf "%d : %s" index val
Which would return ["1 : first"; "2 : second"; "3 : third"] which is getting closer...
But it looks like you want a single string returned - so either to String.Join on the output of that - or use fold:
let final = List.fold (fun (builder, index) in -> builder.AppendFormat("{0}: {1}", index, in), index + 1) (new StringBuilder()) ["first"; "second"; "third"]

Resources