When I have this code:
type HtmlNode =
| HtmlElement of name:string * attribute:HtmlAttribute list
| HtmlText of content:string
and HtmlAttribute =
| HtmlAttribute of name:string * value:string * parent:HtmlNode
let createElement name attrs =
let toAttributes element = [ for name, value in attrs -> HtmlAttribute(name, value, element)]
let rec element = HtmlElement(name, attributes)
and attributes = toAttributes element
element
The compiler gives the following error:
Recursive values cannot appear directly as a construction of the type 'HtmlNode' within a recursive binding. This feature has been removed from the F# language. Consider using a record instead.
Why is that? The let rec is supposed to support the creation of recursive values, and something similar works with records.
I don't know why this was changed, but one workaround is to use seq instead of list.
type HtmlNode =
| HtmlElement of name:string * attribute:HtmlAttribute seq
| HtmlText of content:string
and HtmlAttribute =
| HtmlAttribute of name:string * value:string * parent:HtmlNode
let createElement name attrs =
let rec element = HtmlElement(name, attributes)
and attributes = seq { for name, value in attrs -> HtmlAttribute(name, value, element) }
element
Related
I am struggling to get memoization to work when the memoized function is an abstract function which is overridden/defined within a subclass rather than the parent class.
When the memoized function is defined in the parent class, it works fine.
When I define the signature of the memoized function in the parent class, and then override it in the subclass, I can't figure out the appropriate syntax.
open System
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
[<AbstractClass>]
type ParentClass() as this =
let someparam = DateTime(2022,1,1)
let SlowNumber100InParentClass(t) =
System.Threading.Thread.Sleep(1000)
100.0
member val MemoParentClassSlow100 = Memoize SlowNumber100InParentClass
member this.MultiplyBy2A = (this.MemoParentClassSlow100 someparam) * 2.0
abstract MemoSubClassSlow100: DateTime->float
member this.MultiplyBy2B = (this.MemoSubClassSlow100 someparam) * 2.0
type MyClass() as this =
inherit ParentClass()
let SlowNumber100InSubClass(t) =
System.Threading.Thread.Sleep(1000)
100.0
override this.MemoSubClassSlow100(t) = Memoize SlowNumber100InSubClass t // doesn't "memoize"
//override val MemoSubClassSlow100 = Memoize SlowNumber100InSubClass // This feels intuitive to me, but error is "No abstract property was found that corresponds to this override"
// ??? somehow else ???
[<EntryPoint>]
let main args =
let x = new MyClass()
for i in 1..10 do
Console.WriteLine(x.MultiplyBy2A) // this is fast
for i in 1..10 do
Console.WriteLine(x.MultiplyBy2B) // this is slow
0
You're very close. Just use member val in the subclass to hold the memoized function, since that member doesn't override anything in the parent class:
type MyClass() =
inherit ParentClass()
let SlowNumber100InSubClass(t) =
System.Threading.Thread.Sleep(1000)
100.0
member val MemoSubClassSlow100_ = Memoize SlowNumber100InSubClass
override this.MemoSubClassSlow100(t) = this.MemoSubClassSlow100_ t
Personally, I think let is usually preferable to member val, though:
let memoSubClassSlow100 = Memoize SlowNumber100InSubClass
override _.MemoSubClassSlow100(t) = memoSubClassSlow100 t
I am new to Giraffe and having difficulties displaying an integer. My model is
type Person =
{
FirstName : string
LastName : string
Age : int
}
And my view is:
let index2 (model : Person) =
[
partial()
p [] [
encodedText model.FirstName
br []
encodedText model.LastName
br []
int model.Age
]
] |> layout
The error message returned is "All elements of a list must be of the same type. So how do I display the age?
I am not quite familiar with Giraffe (rather Fable), so I do not know what to install and set to reproduce it.
Nevertheless, looking at p [] [ ... ], it is clear that the function requires a list as a second parameter and therefore all the elements there should be of the same type.
Let's look inside. As per GiraffeViewEngine.fs, encodedText returns Text which is a case in XmlNode DU:
type XmlNode =
| ParentNode of XmlElement * XmlNode list
| VoidElement of XmlElement
| Text of string
So this sets the type for the whole list, XmlNode.
br function is defined as let br = voidTag "br" where voidTag function returns VoidElement which is another case in XmlNode DU, so it is fine.
And so I do not know what you wanted to say by int model.Age, and int is actually an operator in F# to convert object to integer. Anyways, it does not return anything of type XmlNode. Instead, try
p [] [
encodedText model.FirstName
br []
encodedText model.LastName
br []
encodedText (string model.Age)
]
Thereby you will ToString the age, it will turn to Text case of XmlNode and the code should compile.
I have a dictionary that I declare like so:
var dictBarData = [String: Any]()
I then declare and populate a second dictionary like so, and then add the second dictionary to the first:
var dictThisBar = [String:Any]()
dictThisBar["category"] = category
dictThisBar["barValue"] = value
dictThisBar["barView"] = vThisBar
dictBarData[String(barIndex)] = dictThisBar
When I go to retrieve them I do this:
for (key, value) in dictBarData {
let dictThisBar = dictBarData[key]
for(key, value) in dictThisBar {
}
}
But I am getting an error on the second for loop, type Any does not conform to protocol sequence. Type Any does not conform to sequence protocol
According to the declaration all values in dictBarData are Any.
You have to cast the value down to
let dictThisBar = dictBarData[key] as! [String:Any]
However it's highly recommended to use optional bindings
if let dictThisBar = dictBarData[key] as? [String:Any] { ...
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
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.