Why does this F# function need the extra sprintf to compile? - f#

I'm an F# noob. I'm trying to create a function to format a results tuple, where the last element may or may not exist - since it's intended to hold any exceptions that might been caught during processing.
let formatResults resultsTuple =
match resultsTuple with
|(name1, name2, diff, count, correlation, None) -> (sprintf "%A and %A with diff %A had %A pairs and showed a correlation coefficient of %A" name1 name2 diff count correlation)
|(name1, name2, diff, _, _, Some(ex)) -> (sprintf "Error: %A and %A with diff %A threw exception %A" name1, name2, diff, ex) |> sprintf "%A"
See in the last line how I had to pipe the results of the first sprintf into the second sprintf? Basically, it tells me I've got a syntax error somewhere, and that the program isn't doing what I think it is. (Preliminary testing seems to be giving reasonable output, but it makes me nervous.)
Why does that compile, but this doesn't? It gives me the compile error "This expression was expected to have type string but here has type 'a * 'b * 'c * 'd".
let formatResults resultsTuple =
match resultsTuple with
|(name1, name2, diff, count, correlation, None) -> sprintf "%A and %A with diff %A had %A pairs and showed a correlation coefficient of %A" name1 name2 diff count correlation
|(name1, name2, diff, _, _, Some(ex)) -> sprintf "Error: %A and %A with diff %A threw exception %A" name1, name2, diff, ex

sprintf "Error: %A and %A with diff %A threw exception %A" name1, name2, diff, ex
You're creating a tuple that contains the function returned by sprintf "..." name1 as its first element. The other elements of the tuple are name2, diff and ex. By passing that tuple to sprintf "%A", you're turning it into a string, making the types work. But of course that still won't make it do what you want.
To do what you want, get rid of the commas.

Related

syntax issue around kprintf in F#

I have a project using NLog and there is a wrapper around the logger, in order to turn logging off in some areas:
member this.SetQuiet q = quiet <- q
member this.Trace format = Printf.kprintf (fun s -> if not quiet then logger.Trace(s)) format
member this.Debug format = Printf.kprintf (fun s -> if not quiet then logger.Debug(s)) format
member this.Info format = Printf.kprintf (fun s -> if not quiet then logger.Info(s)) format
member this.Warn format = Printf.kprintf (fun s -> if not quiet then logger.Warn(s)) format
member this.Error format = Printf.kprintf (fun s -> if not quiet then logger.Error(s)) format
member this.Fatal format = Printf.kprintf (fun s -> if not quiet then logger.Fatal(s)) format
this works quite well, but I have an issue:
logger.Info "hello"
logger.Info <| "hello"
will work properly, whereas:
"hello" |> logger.Info
will not compile with this error:
typecheck error The type 'string' is not compatible with the type 'Printf.StringFormat<'a,string>'
can someone explain me why this fails? the order kprintf-continuation-format should still be respected here, no?
Is there a workaround for this? the reason is that I'm trying to do a 'tee' to log messages in a non verbose way (the tee just applies a function and then returns the original parameter):
"my messsage"
|> tee logger.Info
|> Result.Ok
That happens because printf and similar methods uses formatting. It allows type-safe usage of these method. For example printfn "%d" enforces integer as parameter
printfn "%d" 3
printfn "%d" 3.14 // error
printfn "%s %f" enforces string and float.
printfn "%s %f" "Hello" 3.14
printfn "%s %f" '3' 14 // error
This means that you should change methods this way (added "%s")
member this.Trace format = Printf.kprintf (fun s -> if not quiet then logger.Trace(s)) "%s" format

Why does F# not like the type ('a list list) as input?

*I edited my original post to include more info.
I'm working on an F# assignment where I'm supposed to create a function that takes an "any list list" as input and outputs an "any list". It should be able to concatenate a list of lists into a single list.
Here's what my function looks like:
let llst = [ [1] ; [2;3] ; ['d';'e';'f'] ]
let concat (llst:'a list list) : 'a list =
List.concat llst
List.iter (fun elem -> printf "%d " elem) concat
This solution more or less copied directly from microsofts example of using the List.concat function, the only exception being the specification of input/output types.
When i run the code i get this error:
concat.fsx(7,43): error FS0001: This expression was expected to have type
''a list'
but here has type
''b list list -> 'b list'
So it appears that concat is turning my llst into a character list, which i don't understand.
Can anyone help me understand why I'm getting this type error and how I can write a function that takes the types that I need?
The problem is somewhere in your implementation of the concat function. It is hard to say where exactly without seeing your code, but since this is an assignment, it is actually perhaps better to explain what the error message is telling you, so that you can find the issue yourself.
The error message is telling you that the F# type inference algorithm found a place in your code where the actual type of what you wrote does not match the type that is expected in that location. It also tells you what the two mismatching types are. For example, say you write something like this:
let concat (llst:'a list list) : 'a list =
llst
You will get the error you are getting on the second line, because the type of llst is 'a list list (the compiler knows this from the type annotation you give on line 1), but the expected type is the same as the result type of the function which is 'a list - also specified by your type annotation.
So, to help you find the issue - look at the exact place where you are getting an error and try to infer why compiler thinks that the actual type is 'a list list and try to understand why it expects 'a list as the type that should be in this place.
This is correct:
let concat (llst:'a list list) : 'a list =
List.concat llst
However, it's really equivalent to let concat = List.concat
This, however, doesn't compile, the elements of the lists need to be of the same type:
let llst = [ [1] ; [2;3] ; ['d';'e';'f'] ]
This also is problematic:
List.iter (fun elem -> printf "%d " elem) concat
List.iter has two arguments and the second one needs to be a List. However in your case you are (as per compiler error) providing your concat function which is a a' List List -> a' List.
What I suspect you meant to do, is apply the concat function to your llist first:
List.iter (fun elem -> printf "%d " elem) (concat llist)
// or
llist
|> concat
|> List.iter (fun elem -> printf "%d " elem)
However, all of this is perhaps missing the point of the exercise. What perhaps you need to do is implement some simple recursion based on the empty / non-empty state of your list, ie. fill in the blanks from here:
let rec myconcat acc inlist =
match inlist with
| [] -> ??
| elt :: tail -> ??

Possible to pass Printf.TextWriterFormat to MailBoxProcessor?

I am building a parallel unit test runner using MailBoxProcessor.
I need to queue up print statements for a test, so I can print them once a test is finished. I know how to send a string and build up a list so I can print them, but that forces me to use sprintf and pipe it into my print function and is not as clean as I would like.
[1..200]
|> List.iter (fun i ->
sprintf "Test %i" i &&& fun ctx ->
ctx.printfn <| sprintf "A guid %A" (ng())
ctx.printfn <| sprintf "I am test %i" i
ctx.printfn <| sprintf "A guid %A" (ng()))
You can see the full code here:
https://github.com/lefthandedgoat/prunner/blob/master/Program.fs#L36-L41
And see that ctx is an object with a printfn method that takes a string and posts it to a single mailbox that queues up messages until a tests is done, then loops over them and prints them.
My goal is to have the ctx.printfn look like this
[1..200]
|> List.iter (fun i ->
sprintf "Test %i" i &&& fun ctx ->
ctx.printfn "A guid %A" (ng())
ctx.printfn "I am test %i" i
ctx.printfn "A guid %A" (ng()))
Your question isn't entirely clear, but you may be able to achieve your goal via kprintf:
member x.printfn fmtStr =
Printf.kprintf (fun msg -> reporter.Post(Print(msg, x.TestId))) fmtStr

F# - printfn Guid

I have following printing example in F#
for row in data.Rows do
printfn "Example: (%s)" row.A
But I received this error
Script1.fsx(15,67): error FS0001: This expression was expected to have
type
string but here has type
Guid
I didn't find any example of printing of Guid type.
I tried apply ToString() method to row.A but it is not working as well
There are two options here:
for row in data.Rows do
printfn "Example: (%s)" (row.A.ToString())
or
for row in data.Rows do
printfn "Example: (%A)" row.A
Here the %A can be used for any type and the compiler will automatically print it for you
You can use %A for a Guid, like this:
printfn "Example: (%A)" row.A
When using %s, the type must be a string.
The MSDN Documentation has more information about which format type to use and how it behaves.
In addition to the answers already given, it's worth noting that Guid also supports an overload of ToString, giving you the option to control how the GUID string is formatted.
You could, for example, write it out like this:
printfn "Example: (%s)" (row.A.ToString "n")
if you want to omit the hyphens (Example: (78e6fb89dc5045988d445c4d8aef4e28)).
Or you could use stringf for this, if you want an alternative with pipes instead of parentheses:
row.A |> stringf "n" |> printfn "Example: (%s)"
or if you don't need the formatting option:
row.A |> string |> printfn "Example: (%s)"

This F# code is not working

This is not working...
I get error FS0001: The type 'string' is not compatible with the type 'seq'
for the last line. Why?
let rec Parse (charlist) =
match charlist with
| head :: tail -> printf "%s " head
Parse tail
| [] -> None
Parse (Seq.toList "this is a sentence.") |> ignore
The problem is that printf "%s " head means that head must be a string, but you actually want it to be a char, so you'll see that Parse has inferred type string list -> 'a option. Therefore, F# expects Seq.toList to be applied to a string seq, not a string.
The simple fix is to change the line doing the printing to printf "%c " head.

Resources