Custom attribute on record fields F# - reflection

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

Related

Mono Bug: Error message when attempting to observe a newly assigned value after mutating an object member

I'm observing an error message on a data-tip when attempting to observe a newly assigned value after mutating an object member.
Viewmodel:
I'm trying to mutate the header member of a viewmodel object.
let mutable header : NavBarItem option = None
member x.Header with get() = header
and set(v) = header <- v
base.NotifyPropertyChanged(<# x.Header #>)
...
//----------------------------------
// THE FOLLOWING ERRORS
//----------------------------------
member x.LoadAsync(panelId:PanelId) =
let items = seq [NavBarItem() :> UITemplate]
let result = items |> Seq.map (fun v -> v :?> NavBarItem)
|> Seq.tryHead // Error recovering value
x.Header <- result
Error:
Here's the error after I hover my cursor over x.Header:
Incorrect number or types of arguments Parameter name: arguments
Here's a bug report that I observed after researching the error message.
UPDATE:
I attempted to serialize the sequence of objects that I did have access to so that I could deserialize the json and access an individual item.
That also failed:
let temp = seq [NavBarItem() :> UITemplate] |> filterToNavBarItems
|> Seq.cast<NavBarItem>
let json = JsonConvert.SerializeObject(temp)
let items = JsonConvert.DeserializeObject<NavBarItem seq>(json)
let item = items |> Seq.tryHead
x.Header <- item // Error recovering value
Json:
[{"MeasureId":"some_navbar_id","Header":"Dashboard","Subheader":"Trauma Alerting","EmblemLabel":"","IsEmblemAttached":false,"EmblemBGColor":"","EmblemFGColor":""}]
Appendix:
type UITemplate =
abstract member ToString : unit -> string
abstract member OrderIndex : int with get, set
type NavBarItem() =
inherit ViewModelBase()
let mutable header = ""
interface UITemplate with
member x.ToString() = "NavBarItem"
member val OrderIndex = -1 with get,set
member x.Header with get() = header
and set(v) = header <- v
base.NotifyPropertyChanged(<# x.Header #>)
type ViewModelBase () =
let propertyChanged = Event<PropertyChangedEventHandler,PropertyChangedEventArgs>()
let getPropertyName = function
| PropertyGet(_,pi,_) -> pi.Name
| _ -> invalidOp "Expecting property getter expression"
interface INotifyPropertyChanged with
[<CLIEvent>]
member this.PropertyChanged = propertyChanged.Publish
member this.NotifyPropertyChanged propertyName =
propertyChanged.Trigger(this,PropertyChangedEventArgs(propertyName))
member this.NotifyPropertyChanged quotation =
quotation |> getPropertyName |> this.NotifyPropertyChanged
It is hard to answer your question, because the code cannot really be compiled as-is. This makes it hard to see where the error is, because it is likely related to something that is not shown in your snippet.
I tried getting the code to compile by removing as much as possible. I ended up with the following, but this works fine:
type ViewModelBase() =
member x.NotifyPropertyChanged(a:Microsoft.FSharp.Quotations.Expr<'a>) = ()
type NavBarItem() =
inherit ViewModelBase()
let mutable header : NavBarItem option = None
member x.Header
with get() = header
and set(v) =
header <- v
base.NotifyPropertyChanged(<# x.Header #>)
member x.LoadAsync(panelId:int) = async {
let result = None
x.Header <- result }
If you can extend this minimal sample so that it exhibits the error, then I'm sure people will be able to help.

How to get the type of each union case for a given union type in F#

I am wondering in the F# code below how to fetch the type associated with each union case via reflection
type AccountCreatedArgs = {
Owner: string
AccountId: Guid
CreatedAt: DateTimeOffset
StartingBalance: decimal
}
type Transaction = {
To: Guid
From: Guid
Description: string
Time: DateTimeOffset
Amount: decimal
}
type AccountEvents =
| AccountCreated of AccountCreatedArgs
| AccountCredited of Transaction
| AccountDebited of Transaction
I tried using FSharpType.GetUnionCases(typeof<AccountEvents>) but UnionCaseInfo does not provide any information about the case type (only the declaring type aka AccountEvents so not really useful in my case) =/
The answer of glennsl really helped me https://stackoverflow.com/a/56351231/4636721
What I really found handy in my case was:
let getUnionCasesTypes<'T> =
Reflection.FSharpType.GetUnionCases(typeof<'T>)
|> Seq.map (fun x -> x.GetFields().[0].DeclaringType)
UnionCaseInfo has a GetFields method which returns an array of PropertyInfos which describe each field/argument of the union case. For example:
FSharpType.GetUnionCases(typeof<AccountEvents>)
|> Array.map(fun c -> (c.Name, c.GetFields()))
|> printfn "%A"
will print
[|("AccountCreated", [|AccountCreatedArgs Item|]);
("AccountCredited", [|Transaction Item|]);
("AccountDebited", [|Transaction Item|])|]
The name assigned to a single field union case is "Item", and if multiple is "Item1", "Item2" etc. The field type itself can be retrieved from the PropertyType property of PropertyInfo, so:
FSharpType.GetUnionCases(typeof<AccountEvents>)
|> Array.map(fun c -> (c.Name, c.GetFields() |> Array.map(fun p -> p.PropertyType.Name)))
|> printfn "%A"
will thus print
[|("AccountCreated", [|"AccountCreatedArgs"|]);
("AccountCredited", [|"Transaction"|]);
("AccountDebited", [|"Transaction"|])|]

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.

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

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

f# types' properties in inconsistent order and of slightly differing types

I'm trying to iterate through an array of objects and recursively print out each objects properties.
Here is my object model:
type firmIdentifier = {
firmId: int ;
firmName: string ;
}
type authorIdentifier = {
authorId: int ;
authorName: string ;
firm: firmIdentifier ;
}
type denormalizedSuggestedTradeRecommendations = {
id: int ;
ticker: string ;
direction: string ;
author: authorIdentifier ;
}
Here is how I am instantiating my objects:
let getMyIdeasIdeas = [|
{id=1; ticker="msfqt"; direction="buy";
author={authorId=0; authorName="john Smith"; firm={firmId=12; firmName="Firm1"}};};
{id=2; ticker="goog"; direction="sell";
author={authorId=1; authorName="Bill Jones"; firm={firmId=13; firmName="ABC Financial"}};};
{id=3; ticker="DFHF"; direction="buy";
author={authorId=2; authorName="Ron James"; firm={firmId=2; firmName="DEFFirm"}};}|]
And here is my algorithm to iterate, recurse and print:
let rec recurseObj (sb : StringBuilder) o=
let props : PropertyInfo [] = o.GetType().GetProperties()
sb.Append( o.GetType().ToString()) |> ignore
for x in props do
let getMethod = x.GetGetMethod()
let value = getMethod.Invoke(o, Array.empty)
ignore <|
match value with
| :? float | :? int | :? string | :? bool as f -> sb.Append(x.Name + ": " + f.ToString() + "," ) |> ignore
| _ -> recurseObj sb value
for x in getMyIdeas do
recurseObj sb x
sb.Append("\r\n") |> ignore
If you couldnt tell, I'm trying to create a csv file and am printing out the types for debugging purposes. The problem is, the first element comes through in the order you'd expect, but all subsequent elements come through with a slightly different (and confusing) ordering of the "child" properties like so:
RpcMethods+denormalizedSuggestedTradeRecommendationsid:
1,ticker: msfqt,direction:
buy,RpcMethods+authorIdentifierauthorId:
0,authorName: john
Smith,RpcMethods+firmIdentifierfirmId:
12,firmName: Firm1,
RpcMethods+denormalizedSuggestedTradeRecommendationsid:
2,ticker: goog,direction:
sell,RpcMethods+authorIdentifierauthorName:
Bill
Jones,RpcMethods+firmIdentifierfirmName:
ABC Financial,firmId: 13,authorId: 1,
RpcMethods+denormalizedSuggestedTradeRecommendationsid:
3,ticker: DFHF,direction:
buy,RpcMethods+authorIdentifierauthorName:
Ron
James,RpcMethods+firmIdentifierfirmName:
DEFFirm,firmId: 2,authorId: 2,
Any idea what is going on here?
Does adding this help?
for x in props |> Array.sortBy (fun p -> p.Name) do
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
In general, I think reflection returns entities (like attributes, methods, properties) in an unspecified order. So just pick a fixed sort order?
(Or did I misunderstand the issue?)
This is a reflection thing. You can't rely on the order of the properties using reflection. I need to sort using MetaTokens. I will post this solution when I get around to implementing it.

Resources