How should I expose a global Dictionary declared in f# that will have items added from different HttpModules? - asp.net

I have a dictionary (formatters) declared in the following code that will have items added to it inside of multiple HttpModules. Once those are loaded it will not be written to again. What would be the best way to expose this so it can be accessed from any .NET language? I know this seems lame and looks like I should just have them implement ToString() however part of the application requires strings to be in a certain format and I don't want clients having to implement ToString() in a way that is specific to my application.
module MappingFormatters
open System
open System.Collections.Generic
let formatters = new Dictionary<Type, obj -> string>();
let format item =
let toDateTime (d:DateTime) =
let mutable date = d;
if (date.Kind) <> System.DateTimeKind.Utc then
date <- date.ToUniversalTime()
date.ToString("yyyy-MM-ddTHH:mm:00Z")
let stripControlCharacters (str:string) =
let isControl c = not (Char.IsControl(c))
System.String( isControl |> Array.filter <| str.ToCharArray())
let defaultFormat (item:obj) =
match item with
| :? string as str-> stripControlCharacters(str)
| :? DateTime as dte -> toDateTime(dte)
| _ -> item.ToString()
let key = item.GetType();
if formatters.ContainsKey(key) then
formatters.Item(key) item
else
defaultFormat item

If the question is just one about language interoperability, then I think you should just change the type from
Dictionary<Type, obj -> string>
to
Dictionary<Type, Func<obj, string> >
and then you should be in good shape.

After researching. I have decided to create a type called MappingFormatters to hold the method for adding the formatter. The client does not need to call it, but my f# code will. I believe this will let me use the common f# conventions while exposing a way for other .net languages to inter-operate with the least confusion.
module File1
open System
let mutable formatters = Map.empty<string, obj -> string>
let format (item:obj) =
let dateToString (d:DateTime) =
let mutable date = d;
if (date.Kind) <> System.DateTimeKind.Utc then
date <- date.ToUniversalTime()
date.ToString("yyyy-MM-ddTHH:mm:00Z")
let stripCtrlChars (str:string) =
let isControl c = not (Char.IsControl(c))
System.String( isControl |> Array.filter <| str.ToCharArray())
let key = item.GetType().AssemblyQualifiedName
if Map.containsKey key formatters then
Map.find key formatters item
else
match item with
| :? DateTime as d -> dateToString d
| _ -> stripCtrlChars (item.ToString())
let add (typ:Type) (formatter:obj -> string) =
let contains = Map.containsKey
let key = typ.AssemblyQualifiedName
if not (formatters |> contains key) then
formatters <- Map.add key formatter formatters
type MappingFormatters() = class
let addLock = new obj()
member a.Add (``type``:Type, formatter:Func<obj,string>) =
lock addLock (fun () ->
add ``type`` (fun x -> formatter.Invoke(x))
)
end

Related

F# Memoization Within a Class

I am struggling to make Memoization work in F# when the function I want to Memoize is a member of a class.
The Dictionary appears to be cleared every time - and so nothing is actually memoized, the result is always recomputed. The same code but with the key functions outside of a calls of a class works just fine.
open System
module TestMemo
open System
open System.IO
open System.Collections.Generic
let Memoize f =
let dict = Dictionary<_, _>()
fun c ->
let exists, value = dict.TryGetValue c
match exists with
| true -> value
| _ ->
let value = f c
dict.Add(c, value)
value
type MyClass() as this =
let rec AddToOne(x) = if x <= 1 then 1 else x + AddToOne(x-1)
let rec AddToOneSkip(x) = if x <= 1 then 1 else x + AddToOneSkip(x-2)
member this.MemoAddToOne = Memoize AddToOne
member this.MemoAddToOneSkip = Memoize AddToOneSkip
[<EntryPoint>]
let main args =
let x = new MyClass()
for i in 1..100000 do
Console.WriteLine(x.MemoAddToOneSkip(i))
for i in 1..100000 do
Console.WriteLine(x.MemoAddToOne(i))
0
When you write this:
member this.MemoAddToOne = Memoize AddToOne
That's not a "value", but a property. A property in .NET is a pair of functions (get + set), but there are also read-only properties, which have only "get". And that's what you created here. They're basically functions in disguise.
So every time somebody accesses x.MemoAddToOne, they're basically calling a function, so every time the body is executed anew, thus making a new call to Memoize every time.
To avoid this, create the memoizers once and then return them from the property getter:
let memoAddToOne = Memoize AddToOne
member this.MemoAddToOne = memoAddToOne
Or use a shortcut for the same thing:
member val MemoAddToOne = Memoize AddToOne

F# Memoization - Persist?

What's the best way to persist/save the results of memoization so it can be loaded later?
There's this standard code snippet in F# for implementing memoization:
let memoize f =
let dict = Dictionary<_, _>();
fun c ->
let exist, value = dict.TryGetValue c
match exist with
| true -> value
| _ ->
let value = f c
dict.Add(c, value)
value
let mySlowFunc x=
// something very slow
0
let myFastFunc = memoize mySlowFunc
After calling myFastFunc many times, I will have a dictionary full of results for mySlowFunc for various x's. I want to persist these results so ideally I can do something like:
let saveMemoziationResults myFastFunc "results.file" = ... // saves the dict to a file
let loadMemoziationResults "results.file" // loads the dict from a file
I can't figure out a way to "access" that dict in order to save it.
You could move dict creation to the caller, like
let memoizeBase dict =
let memoize f = …
memoize
And using it like
let dict = new…
let memoize = memoizeBase dict
// use memoize and save/load dict when needed

Custom attribute on record fields F#

I am trying to declare a custom attribute in a record and trying to read it. It seems to be not working. Please advise.
// Custom Attribute for record fields
[<AttributeUsage(AttributeTargets.Field)>]
type Name(x: string) =
inherit Attribute()
member _.value = x
// Record type
type User =
{ [<Name("id")>]
Id: int
[<Name("email")>]
Email: string
[<Name("organization_id")>]
OrganizationId: option<string> }
// Trying to read the attribute. This is not working. I am getting <null> here.
let parse () =
FSharpType.GetRecordFields(typeof<User>)
|> Array.map (fun p -> p.Name, p.GetCustomAttribute(typeof<Name>, false))
|> Array.iter (fun (t, a) -> printfn "%s : %A" t a)
The below code fixes the problem. For more detailed answer, refer this link.
// Changing to property instead of field
[<AttributeUsage(AttributeTargets.Property)>]
type Name(x: string) =
inherit Attribute()
member _.value = x

How to get the name of a higher order function in F#? [duplicate]

How can I create a function called getFuncName that takes a function of type (unit -> 'a) and returns its name.
I was talking to one of the C# devs and they said you could use the .Method property on a Func type as shown in an example here.
I tried to convert this to F# :
for example convert (unit -> 'a) to a type Func<_> then call the property on it but it always returns the string "Invoke".
let getFuncName f =
let fFunc = System.Func<_>(fun _ -> f())
fFunc.Method.Name
let customFunc() = 1.0
// Returns "Invoke" but I want it to return "customFunc"
getFuncName customFunc
A bit of background to this problem is:
I have created an array of functions of type (unit -> Deedle.Frame). I now want to cycle through those functions invoking them and saving them to csv with the csv name having the same name as the function. Some hypothetical code is below:
let generators : (unit -> Frame<int, string>) array = ...
generators
|> Array.iter (fun generator -> generator().SaveCsv(sprintf "%s\%s.csv" __SOURCE_DIRECTORY__ (getFuncName generator)))
This is being used in a scripting sense rather than as application code.
Not sure how you searched for information, but the first query to the search engine gave me this response:
let getFuncName f =
let type' = f.GetType()
let method' = type'.GetMethods() |> Array.find (fun m -> m.Name="Invoke")
let il = method'.GetMethodBody().GetILAsByteArray()
let methodCodes = [byte OpCodes.Call.Value;byte OpCodes.Callvirt.Value]
let position = il |> Array.findIndex(fun x -> methodCodes |> List.exists ((=)x))
let metadataToken = BitConverter.ToInt32(il, position+1)
let actualMethod = type'.Module.ResolveMethod metadataToken
actualMethod.Name
Unfortunately, this code only works when F# compiler does not inline function body into calling method.
Taken from here
Although there may be a more simple way.

F#: Downloading data asynchronously

I am new to programming and F# is my first language.
Here are the relevant parts of my code:
open System.IO
open System.Net
let downloadHtmlFromUrlAsync (url: string) =
async {
let uri = new System.Uri(url)
let webClient = new WebClient()
let! html = webClient.AsyncDownloadString(uri)
return html
}
let downloadHtmlToDisk (url: string) (directoryPath: string) =
if isValidUrl url then
let name = getNameFromRedirectedUrl url
let id = getIdFromUrl url
let html = downloadHtmlFromUrlAsync url
let newTextFile = File.Create(directoryPath + "\\" + id.ToString("00000") + " " + name.TrimEnd([|' '|]) + ".html")
use file = new StreamWriter(newTextFile)
file.Write(html)
file.Close()
let downloadEntireDatabase (baseUrl: string) (totalNumberOfPeople: int) =
let allIds = [ for i in 1 .. totalNumberOfPeople -> i ]
allIds
|> Seq.map (fun id -> baseUrl + string(id))
|> Seq.filter isValidUrl
|> Seq.map downloadHtmlToDisk
|> Async.Parallel
|> Async.RunSynchronously
I have tested the functions isValidUrl, getNameFromRedirectedUrl, getIdFromUrl in F# interactive. They work fine.
My problem is this: When I try to run the code pasted above, the following error message is produced:
Program.fs(483,8): error FS0193: Type constraint mismatch. The type
seq<(string -> unit)> is not compatible with type
seq<Async<'a>> The type Async<'a> does not match the type string -> unit
What went wrong? What changes should I make?
The problem is probably this line (can you please give us the definition of downloadFighterHtmlToDisk):
allIds
...
|> Seq.map downloadFighterHtmlToDisk
...
based on the error message this functions seems to have a signature string -> string -> unit but you really need string -> Async<'something>.
Now I guess you used downloadHtmlToDisk or something similar and you can but then I would suggest rewriting it to:
let downloadHtmlToDisk (directoryPath: string) (url: string) =
async {
if isValidUrl url then
let name = getNameFromRedirectedUrl url
let id = getIdFromUrl url
let! html = downloadHtmlFromUrlAsync url
let newTextFile = File.Create(directoryPath + "\\" + id.ToString("00000") + " " + name.TrimEnd([|' '|]) + ".html")
use file = new StreamWriter(newTextFile)
file.Write(html)
}
and use it like
let downloadEntireDatabase (baseUrl: string) (totalNumberOfPeople: int) =
let allIds = [ for i in 1 .. totalNumberOfPeople -> i ]
allIds
|> Seq.map (fun id -> (id, baseUrl + string(id)))
|> Seq.filter (fun (_,url) -> isValidUrl url)
|> Seq.map (fun (id,url) -> downloadHtmlToDisk (getFighterPath id) url)
|> Async.Parallel
|> Async.RunSynchronously
See the let! html = ..? This is important - this is where the async will happen ;) - if you want you can find similar operations to write your file asynchronously. Also you don't need to close your file - dispose should handle it
remark
I have just seen that you reextract the id from the url - you might also use this instead of the way I used tuples but I think it's better to really pass the id on if you still need it - for example in downloadHtmlToDisk you really need the id and could have created the url from the id there instead - a much easier approach IMO but I don't want to rewrite everything you go - just experiment a bit with this stuff

Resources