How do i setup a printf-style logger for f# using logging library similar to log4net.
i have Log.Debug, Info, Warn, etc. functions that are similar to DebugFormat or InfoFormat in log4net. i tried to setup type extensions for my Log class that i could call in printf style like Log.Debugf "%s" "foo". my generic log function looks like this:
let log format = Printf.kprintf (sprintf "%s") format
i am having trouble with the extension function signature to log to my Debug function...
i tried using Debugf format and Debug
I'm not familiar with log4net, but assuming you're logging to a MessageBox (like the pros do), you can do the following:
let log format = Printf.kprintf (fun msg -> System.Windows.Forms.MessageBox.Show(msg)) format
In this case, since Show takes a string, it can be shortened to:
let log format = Printf.kprintf System.Windows.Forms.MessageBox.Show format
you mean something like this ?
open System
type SomeLogger() =
member this.Error(format : string, [<ParamArray>]args : obj[] ) = ()
member this.Info(format : string, [<ParamArray>]args : obj[] ) = ()
module Extensions =
type SomeLogger with
member this.FInfo format = Printf.ksprintf (this.Info) format
member this.FError format = Printf.ksprintf (this.Error) format
open Extensions
let l = new SomeLogger()
l.FInfo "%d%s" 10 "123"
You can use standard logging subsystem that defined in System.Diagnostic namespace.
You shall be sure that your logging enviromnet correctly initialized. For example something like this (part of example in C#) but it easy is linked with f# code.
Trace.Listeners.Clear();
try {
TextWriterTraceListener infoTextLogger = new AlignedTextWriterTraceListener(#"c:\temp\log.log");
infoTextLogger.Filter = new EventTypeFilter(SourceLevels.All);
infoTextLogger.TraceOutputOptions = TraceOptions.DateTime | TraceOptions.ProcessId | TraceOptions.ThreadId;
Trace.Listeners.Add(infoTextLogger);
TextWriterTraceListener consoleWriter = new AlignedTextWriterTraceListener(System.Console.Out);
consoleWriter.Filter = new EventTypeFilter(SourceLevels.Information);
Trace.Listeners.Add(consoleWriter);
} catch (Exception exp) {
throw exp;
}
AlignedTextWriterTraceListener.TraceSourceNameLength = SOURCE_NAME_FIELD_LENGTH;
Trace.AutoFlush = true;
Trace.TraceInformation("Logging subsystem has been initiated");
so in f#
open System
open System.Diagnostics
module ClientConsole =
let Run _ =
Trace.TraceInformation("Client started");
For more convenient you can use another trace listener that definded by third party programmer.
For example lool at : AlignedTextWriterTraceListener
Related
A couple days ago, I posted a question about deserialization with enums in F#.
The question is here: Deserialization in F# vs. C#
The answer pointed to some code written by Isaac Abraham, at: https://gist.github.com/isaacabraham/ba679f285bfd15d2f53e
However I am facing another problem:
If the object to deserialize to has an object of type 'enum option', the deserialization will fail, whereas it'll work if the type is just 'enum'.
A minimal example:
type TestType =
| A = 0
| B = 1
type TestObjectA =
{
test : TestType
}
type TestObjectB =
{
test : TestType option
}
let x = "{\"test\":\"A\"}"
let TestA = Deserialize<TestObjectA> x // will work
let TestB = Deserialize<TestObjectB> x // will fail
and the large deserialization code is at: https://pastebin.com/95JZLa6j
I put the whole code in a fiddle: https://dotnetfiddle.net/0Vc0Rh
but it can't be run from there since the F# version they support will not accept the 'object' keyword.
So, my question is: why can't I use the option type on an enum, but it works on other types? As a side note, since I'm quite new to F#, I'm not fully understanding Isaac's code, although I spent some time going through it and trying to troubleshoot it.
My understanding is that this line:
|> Seq.map (fun (value, propertyInfo) -> Convert.ChangeType(value, propertyInfo.PropertyType))
will try to convert the type to the right enum, but not to the enum option.
As a bonus question, is there a working solution that does full idiomatic deserialization with enums? (without going through null types)
open System.IO
type TestType =
| A = 0
| B = 1
type TestObjectB =
{
test : TestType option
}
let jsonSerializeToString obj =
use writer = new StringWriter()
let ser = new Newtonsoft.Json.JsonSerializer()
ser.Formatting <- Newtonsoft.Json.Formatting.Indented
ser.Serialize(writer, obj)
writer.ToString()
let jsonDeserializeFromString str =
Newtonsoft.Json.JsonConvert.DeserializeObject<TestObjectB>(str)
let Test obj =
let str = jsonSerializeToString obj
let obj' = jsonDeserializeFromString str
obj'
[<EntryPoint>]
let main argv =
{ test = Some TestType.B } |> Test |> ignore
{ test = None } |> Test |> ignore
0
Note: if you need to serialize a large collection of objects, then stream them to a file instead of an in-memory string to avoid an OutOfMemoryException. Like use writer = File.CreateText(filePath).
As a bonus question, is there a working solution that does full
idiomatic deserialization with enums?
I use the Microsoft.FsharpLu.Json package in production and find it works quite well for serializing and deserializing between "plain" javascript and idiomatic F#. Note Microsoft.FsharpLu.Json relies on Newtonsoft.Json under the hood.
Below is an example with your types and your test string, using Expecto for tests.
namespace FsharpLuJsonTest
open Newtonsoft.Json
open Microsoft.FSharpLu.Json
open Expecto
open Expecto.Flip
// Setup for FSharpLu.Json
type JsonSettings =
static member settings =
let s = JsonSerializerSettings(
NullValueHandling = NullValueHandling.Ignore,
MissingMemberHandling = MissingMemberHandling.Ignore)
s.Converters.Add(CompactUnionJsonConverter())
s
static member formatting = Formatting.None
type JsonSerializer = With<JsonSettings>
// Your example
type TestType =
| A = 0
| B = 1
type TestObjectA = { test : TestType }
type TestObjectB = { test : TestType option }
module Tests =
let x = """{"test":"A"}"""
[<Tests>]
let tests =
testList "Deserialization Tests" [
testCase "To TestObjectA" <| fun _ ->
JsonSerializer.deserialize x
|> Expect.equal "" { TestObjectA.test = TestType.A }
testCase "To TestObjectB" <| fun _ ->
JsonSerializer.deserialize x
|> Expect.equal "" { TestObjectB.test = Some TestType.A }
]
module Main =
[<EntryPoint>]
let main args =
runTestsInAssembly defaultConfig args
As you can see FsharpLu.Json supports Discriminated Unions and option types out of the box in the way you prefer. FsharpLu.Json is a less flexible solution than some others like Chiron (which allow for much more customisation) but I tend to prefer the opinionated approach of FsharpLu.Json.
I haven't used it personally, but the new FSharp.SystemText.Json library with the JsonUnionEncoding.ExternalTag setting should work roughly the same way FsharpLu.Json does. That library uses Microsoft's new System.Text.Json library under the hood rather than Newtonsoft.Json.
So, I was trying to get this simple test working in an F# console app:
open System.Reflection
open System.ComponentModel.Composition
open System.ComponentModel.Composition.Hosting
[<Export(typeof<int -> string>)>]
let toString(i: int) = i.ToString()
[<EntryPoint>]
let main argv =
use catalog = new AssemblyCatalog(Assembly.GetEntryAssembly())
use container = new CompositionContainer(catalog)
let myFunction = container.GetExportedValue<int -> string>()
let result = myFunction(5)
0
I expected MEF to get the function properly resolved, but it doesn't.
Instead, I get this:
An unhandled exception of type
'System.ComponentModel.Composition.CompositionContractMismatchException'
occurred in System.ComponentModel.Composition.dll
Additional information:
Cannot cast the underlying exported value of type 'Program.toString (ContractName="Microsoft.FSharp.Core.FSharpFunc(System.Int32,System.String)")' to type 'Microsoft.FSharp.Core.FSharpFunc``2[System.Int32,System.String]'.
What am I missing here?
What is the difference between FSharpFunc(System.Int32, System.String) and FSharpFunc``2[System.Int32, System.String]?
What is the correct way to import/export F# functions via MEF?
The compiler turns top-level F# functions into methods, so your example will be compiled as:
[Export(FSharpFunc<int,string>)]
public string toString(int i) { return i.ToString(); }
This is probably causing the error. You can force the compiler to produce a property getter of FSharpFunc type by calling some operation that returns a function - even a simple identity function will don:
let makeFunc f = f
[<Export(typeof<int -> string>)>]
let toString = makeFunc <| fun (i:int) ->
i.ToString()
I have not tested this, but I think it could work. That said, it is probably safer to go with a simple single-method interface in this case.
In my first attempt to create a type provider, I have a ProvidedTypeDefinition for a message:
// Message type
let mTy = ProvidedTypeDefinition(asm, ns, message.Key, Some(typeof<ValueType>),
HideObjectMethods = true, IsErased = false)
// Direct buffer
let bufferField = ProvidedField("_directBuffer", typeof<IDirectBuffer>)
mTy.AddMember bufferField
let mCtor1 =
ProvidedConstructor(
[ProvidedParameter("buffer", typeof<IDirectBuffer>)],
InvokeCode = fun args ->
match args with
| [this;buffer] ->
Expr.FieldSet (this, bufferField, <## %%buffer:IDirectBuffer ##>)
| _ -> failwith "wrong ctor params"
)
mTy.AddMember mCtor1
Then I need to create an instance of that type in a method of another provided type. I am doing this:
let mMethod = ProvidedMethod(message.Key, [ProvidedParameter("buffer", typeof<IDirectBuffer>)], mTy)
mMethod.InvokeCode <- (fun [this;buffer] ->
let c = mTy.GetConstructors().Last()
Expr.NewObject(c, [ buffer ])
)
ILSpy shows the following C# code equivalent for the method:
public Car Car(IDirectBuffer buffer)
{
return new Car(buffer);
}
and it also shows that the Car struct is present in the test assembly (this test assembly builds OK unless I access the Car method):
But when I try to create the Car via the method like this:
type CarSchema = SbeProvider<"Path\to\SBETypeProvider\SBETypeProvider\Car.xml">
module Test =
let carSchema = CarSchema()
let car = carSchema.Car(null)
I get the following errors:
The module/namespace 'SBETypeProvider' from compilation unit 'tmp5CDE' did not contain the namespace, module or type 'Car'
A reference to the type 'SBETypeProvider.Car' in assembly 'tmp5CDE' was found, but the type could not be found in that assembly
What I am doing wrong? The picture shows that the type is here. Why I cannot create it?
I looked through many type providers on GitHub and cannot find a clear example how to generate a ProvidedTypeDefinition from another one.
This might not be the problem, but at a quick glance it looks like the line you linked might actually be the issue:
let mTy = ProvidedTypeDefinition(asm, ns, message.Key, Some(typeof<ValueType>),
HideObjectMethods = true, IsErased = false)
This type is being added to the ty provided type (the one that will actually be written to the temporary assembly) and so shouldn't have the assembly and namespace specified itself.
let mTy = ProvidedTypeDefinition(message.Key, Some(typeof<ValueType>),
HideObjectMethods = true, IsErased = false)
Might work better. Generated types are a bit of a black art though, with very little documentation, so it's possible (probable?) that there will be other issues you might find.
On a more general note, for creating provided types what I normally end up doing is returning the provided constructor as a value which can then be embedded in the invoke code for other properties/functions using Expr.Call. This is especially important for erased types, as reflection will not work on them anyway.
I'm using the FSharp.Data.JsonProvider to read Twitter Tweets.
Playing with this sample code
https://github.com/tpetricek/Documents/tree/master/Samples/Twitter.API
I want to expand the urls in the tweet with
let expandUrl (txt:string) (url:Search.DomainTypes<...>.DomainTypes.Url) =
txt.Replace( url.Url, url.ExpandedUrl )
This results in Error:
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.
My problem is how to define the TypeProvider Type for url in the expandUrl function above?
The type inferance shows me this
val urls : FSharp.Data.JsonProvider<...>.DomainTypes.Url []
but this is not accepted in the type declaration. I assume "<...>" is not F# synatx.
How to do a type annotation for using a TypeProvider type e.g. FSharp.Data.JsonProvider<...>.DomainTypes.Url ?
Here is the complete code snippet:
open TwitterAPI // github.com/tpetricek/Documents/tree/master/Samples/Twitter.API
let twitter = TwitterAPI.TwitterContext( _consumerKey, _consumerSecret, _accessToken, _accessTokenSecret )
let query = "water"
let ts = Twitter.Search.Tweets(twitter, Utils.urlEncode query, count=100)
let ret =
[ for x in ts.Statuses do
// val urls : FSharp.Data.JsonProvider<...>.DomainTypes.Url []
let urls = x.Entities.Urls
// fully declarated to help the type inference at expandUrl
let replace (txt:string) (oldValue:string) (newValue:string) =
txt.Replace( oldValue, newValue)
// Error:
// 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.
let expandUrl (txt:string) (url:FSharp.Data.JsonProvider<_>.DomainTypes.Url) =
replace txt url.Url url.ExpandedUrl
let textWithExpandedUrls = Array.fold expandUrl x.Text urls
yield textWithExpandedUrls
]
When you call Twitter.Search.Tweets (https://github.com/tpetricek/Documents/blob/master/Samples/Twitter.API/Twitter.fs#L284), the return type of that is one of the domain types of TwitterTypes.SearchTweets, which is a type alias for JsonProvider<"references\\search_tweets.json"> (https://github.com/tpetricek/Documents/blob/master/Samples/Twitter.API/Twitter.fs#L183).
Although in the tooltip it shows up as JsonProvider<...>.DomainTypes.Url, you'll have to use the type alias TwitterTypes.SearchTweets.DomainTypes.Url
I had a similar problem trying to figure out how to use the FSharp.Data HtmlProvider.
I am using Wikipedia to get information about USA presidents. The HtmlProvider does a great job of discovering the various tables in that webpage, but I wanted to extract the logic for processing a row of "president data" into a separate function called processRow.
And the problem was trying to work out what the type of such a row is for processRow's parameter row. The following code does the trick:
#load "Scripts\load-references.fsx"
open FSharp.Data
let presidents = new HtmlProvider<"https://en.wikipedia.org/wiki/List_of_Presidents_of_the_United_States">()
let ps = presidents.Tables.``List of presidents``
ps.Headers |> Option.map (fun hs -> for h in hs do printf "%s " h)
printfn ""
type Presidents = ``HtmlProvider,Sample="https://en.wikipedia.org/wiki/List_of_Presidents_of_the_United_States"``.ListOfPresidents
let processRow (row:Presidents.Row) =
printfn "%d %s" row.``№`` row.President2
ps.Rows |> Seq.iter processRow
I did not type in the long type alias for Presidents, I used Visual Studio auto-completion by guessing that the type for List of presidents would be discoverable from something starting with Html, and it was, complete with the four single back quotes.
The attribute and C# examples are noted here but it doesn't look to be possible for FSharp.
http://msdn.microsoft.com/en-us/library/system.runtime.compilerservices.callermembernameattribute.aspx?cs-save-lang=1&cs-lang=csharp#code-snippet-2
// using System.Runtime.CompilerServices
// using System.Diagnostics;
public void DoProcessing()
{
TraceMessage("Something happened.");
}
public void TraceMessage(string message,
[CallerMemberName] string memberName = "",
[CallerFilePath] string sourceFilePath = "",
[CallerLineNumber] int sourceLineNumber = 0)
{
Trace.WriteLine("message: " + message);
Trace.WriteLine("member name: " + memberName);
Trace.WriteLine("source file path: " + sourceFilePath);
Trace.WriteLine("source line number: " + sourceLineNumber);
}
Sample Output:
message: Something happened.
member name: DoProcessing
source file path: c:\Users\username\Documents\Visual Studio 2012\Projects\CallerInfoCS\CallerInfoCS\Form1.cs
source line number: 31
Is it possible to do the above in F# and if so what is the notation?
A quick search through the compiler source code shows that the name CallerMemberName does not appear anywhere in the code, so I think this feature is not supported. (You can certainly mark a parameter with the attribute, but these attributes are special - they instruct the compiler instead of being discovered and used in some way at runtime.)
Update July 2016: As of late June, F# now supports CallerLineNumber and CallerFilePath, but CallerMemberName is still absent. It seems like that one in particular is more difficult to implement, unfortunately.
On a related note, F# has a few special identifiers that let you get the current source file name and line number, so you might be able to get similar information with __SOURCE_DIRECTORY__ and __LINE__
(but not from the caller as in C#).
Here's a quick 'n' dirty hack, which abuses inline to get this info:
module Tracing =
open System
open System.Text.RegularExpressions
let (|TraceInfo|_|) (s:string) =
let m = Regex.Match(s, "at (?<mem>.+?) in (?<file>.+?\.[a-zA-Z]+):line (?<line>\d+)")
if m.Success then
Some(m.Groups.["mem"].Value, m.Groups.["file"].Value, int m.Groups.["line"].Value)
else None
let inline trace s =
printfn "%s" s
match Environment.StackTrace with
| TraceInfo(m, f, l) ->
printfn " Member: %s" m
printfn " File : %s" f
printfn " Line : %d" l
| _ -> ()
It actually does work, more or less: