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
Related
I'm trying to use the bind (>>=) and Kleisli composition (>=>) operators with the basic Result type, but either they are not defined or are not in scope:
let f x =
if x%2 = 0 then Ok (x/2)
else Error ()
let ff x = Ok x >>= f >>= f
let ff' = f >=> f
[<EntryPoint>]
let main _ =
printfn "%A" (ff 12)
printfn "%A" (ff' 28)
0
Error FS0043 Expecting a type supporting the operator '>>=' but given a function type. You may be missing an argument to a function.
I've tried to open a few different namespaces to bring the definition into scope, but no luck.
It seems from this like the operators can't be defined in general without extensions, but are there definitions for the standard Result anywhere?
Haskell-like operators are not defined in the F# core library, nor are they likely to ever be. You'll need to either write them in your own prelude, or use a library like FSharpPlus for these and other more Haskell-like (Well, typelevel) programming approaches.
In addition to what Phillip said in his answer, it might be useful to add that you can rewrite your example using the built-in Result.bind operation and function composition or piping:
let f x =
if x%2 = 0 then Ok (x/2)
else Error ()
let ff x = Ok x |> Result.bind f |> Result.bind f
let ff' = f >> Result.bind f
printfn "%A" (ff 12)
printfn "%A" (ff' 28)
This is of course just a toy example, so it's hard to say what you actually want to do, but if I was trying to use Result, my first choice would be to use the standard library functions - they may make your code longer, but it is arguably more readable.
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
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
I keep running into the following problem:
(System.Console.ReadLine ()).Split [|'('; ')'|]
|> Array.filter (fun s -> not (System.String.IsNullOrEmpty (s)))
|> Array.map (fun s -> s.Split [|','|])
|> Array.map (fun s -> Array.map (fun t -> t.Trim ()) s) (* t.Trim () is underlined with a red squiggly line *)
|> [MORE CODE]
The error associated with the red squiggly line is:
Lookup on object of indeterminate type based on information prior to this program point. A type annotation may be needed prior to this program point to constrain the type of the object. This may allow the lookup to be resolved.
But when the mouse pointer hovers over t, IntelliSense correctly says that t is of type string.
I can bypass the error by writing fun (t : string) -> [CODE], but I wonder why Visual Studio draws the squiggly line at all when it is already detecting the type of the variable correctly. Is this a simple bug in the trial build, or am I misunderstanding something about F#'s type inferencing?
Thanks in advance for your responses.
There is some gap between F# intellisense and F# type checker.
The type checker works from left to right in order. Your problem can be fixed be changing:
fun s -> Array.map (fun t -> t.Trim()) s
to
fun s -> s |> Array.map (fun t -> t.Trim())
Since type of s is available before using Array.map, the type checker is able to infer type of t.
Remarks:
This error usually occurs when using instance methods. You could replace these methods by static functions with extra type annotation and refactor to make your code more composable:
let split arr (s: string) = s.Split arr
let trim (s: string) = s.Trim()
System.Console.ReadLine()
|> split [|'('; ')'|]
|> Array.filter (not << System.String.IsNullOrEmpty)
|> Array.map (split [|','|])
|> Array.map (Array.map trim)
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.