F# compiler warning FS0067 for exceptions with when guards - f#

In F#, we can conveniently use failwith and failwithf, which both throw Exception instances. Consequently, you may occasionally need to use a "when guard" to differentiate between different exception conditions.
Here is an illustrative example:
try
let isWednesday = DateTime.Now.DayOfWeek = DayOfWeek.Wednesday
if isWednesday then failwith "Do not run this on Wednesdays!"
with
| :? DivideByZeroException -> printfn "You divided by zero."
| :? Exception as ex when ex.Message.Contains("Wednesdays") -> printfn "I said, %s" ex.Message
| ex -> printfn "%s" ex.Message
The above code, however, results in two warnings:
Program.fs(14,7): warning FS0067: This type test or downcast will always hold
Program.fs(14,7): warning FS0067: This type test or downcast will always hold
How do I avoid that warning?

Remove the type test pattern for the Exception class. It is unnecessary.
with
| :? DivideByZeroException -> printfn "You divided by zero."
| ex when ex.Message.Contains("Wednesdays") -> printfn "I said, %s" ex.Message
| ex -> printfn "%s" ex.Message

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

Evaluating static constructors of modules with reflection

I have a lot of modules that upon starting the program are supposed to add certain things to a single Dictionary found in a higher level module. However, it appears that expressions and constants within a module are packed into static constructors when compiling to a Console App, so these aren't evaluated unless explicitly referenced/when the program thinks they are needed.
There have been a few questions on here regarding initializing modules, and the consensus has been that it is not possible to force. However, I have not seen any of them explore reflection in this regard. In C# I know you are able to invoke the static constructor of a type, so I have attempted the same with F# modules.
My attempts have involved adding a custom attribute (MessageHandlerAttribute) to each module containing such an expression that I want evaluated upon starting the program, and then running this:
let initAllMessageHandlerModules =
Assembly.GetExecutingAssembly().GetTypes()
|> Array.choose (fun typ ->
typ.CustomAttributes
|> Seq.tryFind (fun attr -> attr.AttributeType = typeof<MessageHandlerAttribute>)
|> Option.map (fun _ -> typ))
|> Array.iter
(fun typ -> try typ.TypeInitializer.Invoke(null, null) |> ignore with | ex -> printfn "%A" ex)
But this gives me the following error:
System.NullReferenceException: Object reference not set to an instance of an object.
I have also tried to swap the final lambda function with this:
(fun typ -> try System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(typ.TypeHandle) |> ignore with | ex -> printfn "%A" ex)
But this appears to do nothing. Is it possible to achieve this?
Use type.GetConstructor instead of type.TypeInitializer.
(fun typ -> try typ.GetConstructor(BindingFlags.Instance ||| BindingFlags.Public, null, CallingConventions.Standard, Array.empty<Type>, Array.empty<ParameterModifier>).Invoke(null, null) |> ignore with | ex -> printfn "%A" ex)
Here are some more examples

The block after 'let' is unfinished - try/with

Follow Code F#:
try
let result = 100/0
with
| :? Exception as ex -> printfn ex.Message
I get an error:
The block after 'let' is unfinished. Each block of code is an
expression and must have a result. 'let' can not be the final code
element in a block. Consider giving this block an explicit result.
What am I doing wrong ?
The issue is that let by itself is not an expression:
In F# everything is an expression of a certain type. But let alone is not an expression, is a binding and it has to be continued with some expression that, presumably, uses the value bound to the id result.
Since you are merely testing the try/catch functionality. I assume you are not really interested in producing any values, that is why I added the expression: () after the let.
try
let result = 100/0
()
with
ex -> printfn "%s" ex.Message
The try/with expression requires that both sides return the same type of value, just like if/then/else does. Since in the with side printfn returns unit, I made the try side also return a unit value which is (). Think of it as the equivalent to void in C#.
I can recommend different approach. This won't leave the result variable undefined.
let result =
try
Some(100/0)
with
| :? Exception as ex -> printfn "%s" ex.Message; None

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

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