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.
Related
Can someone point me to a comprehensive guide on the theory behind flowtype function intersections? Behavior is confusing to me. I understand that this type:
type FnT = ((string) => string) & ((number) => string);
reduces down to (string | number) => (string & string), but why is is that i can't cast the parameter to either string or number ???
i.e const g: FnT = (p: string) => { return "hi"; } gives me
Cannot assign function togbecause string [1] is incompatible with number [2] in the first argument..
Why??? isn't string a perfectly valid subtype of string | number?
is this because it expects a super type?
if this is the case then why is it that a union of same two function types lets me cast the param to one or the other?
i.e.
const FnT = ((string) => string) | ((number) => string) works with
const g: FnT = (p: string) => ("hi") ??? wouldn't we expect a supertype of string | number here?
With flow, you need to test all alternative types before casting.
example, if your type is string|number, and you want to cast as a number, you must first test that it is not actually a string.
This is because Flow will not try and modify your values for you, it is only a type checker. You must modify your values yourself, meaning flow can not 'convert' a number to a string, it can only cast the type.
I'm trying to insert new key-value pair in dictionary, which nested in another one Dictionary:
var dict = Dictionary<Int, Dictionary<Int, String>>()
dict.updateValue([1 : "one", 2: "two"], forKey: 1)
dict[1]?[1] // {Some "one"}
if var insideDic = dict[1] {
// it is a copy, so I can't insert pair this way:
insideDic[3] = "three"
}
dict // still [1: [1: "one", 2: "two"]]
dict[1]?[3] = "three" // Cannot assign to the result of this expression
dict[1]?.updateValue("three", forKey: 3) // Could not find a member "updateValue"
I believe should be a simple way to handle it, but I spent an hour and still can't figure it out.
I can use NSDictionary instead, but I really like to understand how I should manage nested Dictionaries in Swift?
Dictionarys are value types so are copied on assignment. As a result you are going to have to get the inner dictionary (which will be a copy), add the new key, then re-assign.
// get the nested dictionary (which will be a copy)
var inner:Dictionary<Int, String> = dict[1]!
// add the new value
inner[3] = "three"
// update the outer dictionary
dict[1] = inner
println(dict) // [1: [1: one, 2: two, 3: three]]
You could use one of the new utility libraries such as ExSwift to make this a bit simpler:
dict[1] = dict[1]!.union([3:"three"])
This uses the union method that combines two dictionaries.
I'm finding some surprising behavior with optional dictionaries in Swift.
var foo:Dictionary<String, String>?
if (foo == nil) {
foo = ["bar": "baz"]
}
else {
// Following line errors with "'Dictionary<String, String>?' does
// not have a member named 'subscript'"
foo["qux"] = "quux"
}
I've played with this a lot, trying to figure out what I might be missing, but nothing seems to make this code work as expected short of making the dictionary not optional. What am I missing?
The closest I can get is the following, but of course it's ridiculous.
var foo:Dictionary<String, String>?
if (foo == nil) {
foo = ["bar": "baz"]
}
else if var foofoo = foo {
foofoo["qux"] = "quux"
foo = foofoo
}
The lightbulb moment is when you realize that an Optional dictionary is not a Dictionary. An Optional anything is not that thing! It is an Optional!! And that's all it is. Optional is itself a type. An Optional is just an enum, wrapping the possible cases nil and some value. The wrapped value is a completely different object, stored inside.
So an Optional anything does not act like the type of that thing. It is not that thing! It is just an Optional. The only way to get at the thing is to unwrap it.
The same is true of an implicitly unwrapped Optional; the difference is just that the implicitly unwrapped Optional is willing to produce (expose) the wrapped value "automatically". But it is still, in fact, wrapped. And, as Bryan Chen has observed, it is wrapped immutably; the Optional is just holding it for you - it is not giving you a place to play with it.
you can use this code
if var foofoo = foo {
foofoo["qux"] = "quux"
foo = foofoo
} else {
foo = ["bar": "baz"]
}
with this code
var foo:Dictionary<String, String>? = Dictionary()
foo[""]=""
error: 'Dictionary<String, String>?' does not have a member named 'subscript'
foo[""]=""
^
the error message makes sense to me that Dictionary<String, String>? does not implement subscript method, so you need to unwrap it before able to use subscript.
one way to call method on optional is use ! i.e. foo![""], but...
var foo:Dictionary<String, String>? = Dictionary()
foo![""]=""
error: could not find member 'subscript'
foo![""]=""
~~~~~~~~^~~
whereas
var foo:Dictionary<String, String>? = Dictionary()
foo![""]
works
it is interesting these code failed to compile
var foo:Dictionary<String, String>! = Dictionary() // Implicitly unwrapped optional
foo[""]=""
error: could not find an overload for 'subscript' that accepts the supplied arguments
foo[""]=""
~~~~~~~^~~
var foo:Dictionary<String, String>! = Dictionary() // Implicitly unwrapped optional
foo.updateValue("", forKey: "")
immutable value of type 'Dictionary<String, String>' only has mutating members named 'updateValue'
foo.updateValue("", forKey: "")
^ ~~~~~~~~~~~
the last error message is most interesting, it is saying the Dictionary is immutable, so updateValue(forKey:) (mutating method) can't be called on it
so what happened is probably that the Optional<> store the Dictionary as immutable object (with let). So even Optional<> it is mutable, you can't modify the underlying Dictionary object directly (without reassign the Optional object)
and this code works
class MyDict
{
var dict:Dictionary<String, String> = [:]
subscript(s: String) -> String? {
get {
return dict[s]
}
set {
dict[s] = newValue
}
}
}
var foo:MyDict? = MyDict()
foo!["a"] = "b" // this is how to call subscript of optional object
and this lead me to another question, why Array and Dictionary are value type (struct)? opposite to NSArray and NSDictionary which are reference type (class)
This is because your Dictionary is optional. If it's nil, you won't add an entry to it.
You can do this way:
var dict: [String : String]?
if let dict = dict {
dict["key"] = "value" // add a value to an existing dictionary
} else {
dict = ["key" : "value"] // create a dictionary with this value in it
}
Or, if you are given an optional dictionary, for example HTTPHeaders - which in AlamoFire is a [String : String] dictionary - and you want to either add a value if it's non-nil, or create it with this value if it's nil, you could do like so:
let headers: HTTPHeaders? // this is an input parameter in a function for example
var customHeaders: HTTPHeaders = headers ?? [:] // nil coalescing
customHeaders["key"] = "value"
I tried this for Swift 3.1 and it worked:
if (myDict?[key] = value) == nil {
myDict = [key: value]
}
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.
I've been messing about with F# and it's Reflection, trying to create a Record type object dynamically from within F#, I got most of it working (as you can see below) but one thing - the record I create through reflection has type "obj" instead the one it should ("Person") and I can't seem to be able to upcast it in any way.
#light
type Person = {
Name:string;
Age:int;
}
let example = {Name = "Fredrik"; Age = 23;}
// example has type Person = {Name = "Fredrik"; Age = 23;}
let creator = Reflection.FSharpValue.PrecomputeRecordConstructor(example.GetType(),
System.Reflection.BindingFlags.Public)
let reflected = creator [| ("thr" :> obj); (23 :> obj) |]
// here reflected will have the type obj = {Name = "thr"; Age = 23;}
// Function that changes the name of a Person record
let changeName (x:Person) (name:string) =
{ x with Name = name }
// Works with "example" which is has type "Person"
changeName example "Johan"
// But not with "reflected" since it has type "obj"
changeName reflected "Jack" // Error "This expression has type obj but is here used with type Person. "
// But casting reflected to Person doesn't work either
(reflected :> Person) // Type constraint mismatch. The type obj is not compatible with
// type Person. The type 'obj' is not compatible with the type 'Person'.
// C:\Users\thr\Documents\Visual Studio 2008\Projects\
// Reflection\Reflection\Script.fsx 34 2 Reflection
Try using the other cast operator (as you're casting the other way this time)
So changeName (reflected :?> Person) "Jack"