How can I use a map as a type in F#? - f#

I have the following:
Map [
"A", new Dictionary<int64, int64 * float>()
"B", new Dictionary<int64, int64 * float>()
]
and I would like a dictionary of those, something like:
let a =
new Dictionary<
string,
Map [
"A", new Dictionary<int64, int64 * float>()
"B", new Dictionary<int64, int64 * float>()
]>()
How to I get the type for the map to create the dictionary?

It is not entirely clear whether your question is how to write the type of the dictionary you want or whether you are asking how to construct a value of this type. Map is a generic type Map<'K, 'V>, much like the type of the Dictionary, so to write the type you'd use:
Dictionary<string, Map<string, Dictionary<int64, int64 * float>>>
To create a value, you can write the full type, or you can use the fact that F# can often infer the type arguments for you and so you can replace them with _. For example:
let map =
Map [
"A", new Dictionary<int64, int64 * float>()
"B", new Dictionary<int64, int64 * float>()
]
let a = new Dictionary<_, _>(dict ["first", map])
I would also add that dictionary of maps of dictionaries is a pretty incomprehensible type. It would probably be a good idea to extract some of those into simple named classes that convey some idea about the meaning of the type.

Related

Tuple vs * star types in F#

Consider the following code:
let pair = System.Tuple.Create (10, "foo") // val pair : int * string = (10, "foo")
let tuple = System.Tuple.Create <| (10, "foo") // val tuple : System.Tuple<int * string> = ((10, "foo"))
Why doesn't the two lines yield values of the same type? Does the type of the argument (10, "foo") somehow change between the two lines?
What's the exact difference between int * string and System.Tuple<int * string>?
For 2, at least the latter has null as a value (this is how this question came up). Are there other differences?
There are two different overloads of Tuple.Create:
Tuple.Create<'T1>(item1: 'T1)
Tuple.Create<'T1, 'T2>(item1: 'T1, item2: 'T2)
In the first case you just calling a method with two arguments. So the second Tuple.Create overload is obviously picked. No surprise.
But with piping you first create a tuple instance. And then pass it to Tuple.Create method. This is what happens in the second example
let intermediate : Tuple<int, string> = (10, "foo")
let tuple = Tuple.Create(intermediate)
With a single argument the first Tuple.Create overload will be picked.
Note: star type is a way tuple type names are written in F#. So Tuple<int, string, bool> will be (int * string * bool). It's the same thing.
Your tuple is a tuple of one (1) element, namely the tuple 10,"foo". It is equivalent of
System.Tuple.Create(System.Tuple.Create(10, "foo"))
Your pair on the other hand is a tuple of the two elements 10 and "foo".
So pair has type System.Tuple<int,string> (which is the same as int * string), but tuple has type System.Tuple<System.Tuple<int,string>> (which is System.Tuple<int * string>)

How to deserialise a missing property to an empty list in an F# record instead of null using Json.Net

Consider an F# record that contains a list value such as this:
type MyRecord = {
Name: string
SomeList: string list
}
Using Netwonsoft.Json.JsonConvert to deserialise JSON to this record when the JSON does not contain a property for the list value Values of the record will lead to the deserialised record having a null value for the list instead of an empty list [].
That is,
open Newtonsoft.Json
JsonConvert.DeserializeObject<MyRecord>("""{ "Name": "Some name"}""" ) |> printfn "%A"
// Gives: { Name = "Some name"; SomeList = null; }
How can you deserialise using Netwonsoft.Json so that the list is initialised to an empty list? For example:
{ Name = "Some name"; SomeList = []; }
You can do this with a custom contract resolver such as the following:
type ParameterizedConstructorInitializingContractResolver() =
inherit DefaultContractResolver()
// List is a module not a static class so it's a little inconvenient to access via reflection. Use this wrapper instead.
static member EmptyList<'T>() = List.empty<'T>
override __.CreatePropertyFromConstructorParameter(matchingMemberProperty : JsonProperty, parameterInfo : ParameterInfo) =
let property = base.CreatePropertyFromConstructorParameter(matchingMemberProperty, parameterInfo)
if (not (matchingMemberProperty = null) && property.PropertyType.IsGenericType && property.PropertyType.GetGenericTypeDefinition() = typedefof<_ list>) then
let genericMethod = typeof<ParameterizedConstructorInitializingContractResolver>.GetMethod("EmptyList", BindingFlags.Public ||| BindingFlags.NonPublic ||| BindingFlags.Static)
let concreteMethod = genericMethod.MakeGenericMethod(property.PropertyType.GetGenericArguments())
let defaultValue = concreteMethod.Invoke(null, null)
property.DefaultValue <- defaultValue
property.DefaultValueHandling <- new System.Nullable<DefaultValueHandling>(DefaultValueHandling.Populate)
matchingMemberProperty.DefaultValue <- defaultValue
matchingMemberProperty.DefaultValueHandling <- new System.Nullable<DefaultValueHandling>(DefaultValueHandling.Populate)
property
And then use it as follows:
let settings = JsonSerializerSettings(ContractResolver = new ParameterizedConstructorInitializingContractResolver())
let myrecord1 = JsonConvert.DeserializeObject<MyRecord>("""{ "Name": "Missing SomeList"}""", settings )
let myrecord2 = JsonConvert.DeserializeObject<MyRecord>("""{ "Name": "Populated SomeList", "SomeList" : ["a", "b", "c"]}""", settings )
let myrecord3 = JsonConvert.DeserializeObject<MyRecord>("""{ "Name": "null SomeList", "SomeList" : null}""", settings )
Notes:
The contract resolver works for any object that is deserialized via a parameterized constructor, which includes, but is not limited to, f# records. If any such object has a constructor argument with type T list for any T then the value will default to List.empty<T> when missing or null.
The contract resolver reuses the same instance of the default value List.empty<T> for all deserialized objects, which is fine here since f# lists are immutable (and List.empty<T> seems to be a singleton anyway). The same approach would not work for providing a default value for mutable collections as a constructor argument.
You may want to cache the contract resolver for best performance.
The constructor parameter must have the same name (modulo case) as the corresponding property.
Demo fiddle here.

Can we create a Dictionary with Generic?

In Stanford's class(see the picture 1), the professor initialized a Dictionary like this:
var a = Dictionary<String: Int>()
But it cannot work in my computer(see the picture 2), is there something wrong?
Dictionary is a generic struct, whose generic type parameters can be defined in the same way as any other generic struct, using a comma separated list in angle brackets:
let a = Dictionary<String, Int>()
There's also a special syntactic sugar that's specific to dictionaries, that let you express the same as above as:
let a = [String: Int]()
Mixing the two together as in Dictionary<String: Int> is invalid.

Swift - 'NSArray' is not of type 'inout C'

I'm getting this error in XCode beta 3. Don't know whether this is a bug or if I'm doing something I shouldn't. Anyway, this is an example that I read on Apple's official documentation.
Here's the code:
var names = ["Mark" , "Bob" , "Tracy" , "John"]
var reversed = sort(names , { (s1: String , s2: String) -> Bool in return s1 > s2
})
It is a simple sort using a closure.
There are several problems here:
Never declare a generic without specifying its type. Don't use Array, use Array<String>. However, in this case, you don't need the : Array<String part, you don't need the typing as Array<String>. Just let the type be inferred.
let names = ["Mark", "Bob", "Tracy", "John"]
Now, this is a constant array. Array is a value type. Constant value types cannot be modified.
The sort function is trying to sort the array. It doesn't create a new array, it sorts the one you pass as a parameter. However, since it is a value type, it cannot be a constant and you have to pass it by reference:
var names = ["Mark", "Bob", "Tracy", "John"]
sort(&names, >)
If you don't want to sort the original array and you want to create a new array instead, use sorted function.
let names = ["Mark", "Bob", "Tracy", "John"]
let sortedNames = sorted(names, >)
Note this has changed substantially between Beta 2 and Beta 3.

Unexpected result from call to Newtonsoft.Json from F#

I am not getting the expected result from this F# code. I would expect t to contain values as a result of the call to JsonSchema.Parse(json) but instead it is empty. What am I doing wrong?
open Newtonsoft.Json
open Newtonsoft.Json.Schema
let json = """{
"Name": "Bad Boys",
"ReleaseDate": "1995-4-7T00:00:00",
"Genres": [
"Action",
"Comedy"
]
}"""
[<EntryPoint>]
let main argv =
let t = JsonSchema.Parse(json)
0 // return an integer exit code
As John Palmer points out, JsonSchema.Parse parses a JSON schema, but from your question, it looks as though you want to parse a normal JSON value. This is possible with JsonConvert.DeserializeObject:
let t = JsonConvert.DeserializeObject json
However, the signature of DeserializeObject is to return obj, so that doesn't particularly help you access the values. In order to do so, you must cast the return value to JObject:
let t = (JsonConvert.DeserializeObject json) :?> Newtonsoft.Json.Linq.JObject
let name = t.Value<string> "Name"
Json.NET is designed to take advantage of C#'s dynamic keyword, but the exact equivalent of that isn't built into F#. However, you can get a similar syntax via FSharp.Dynamic:
open EkonBenefits.FSharp.Dynamic
let t = JsonConvert.DeserializeObject json
let name = t?Name
Notice the ? before Name. Keep in mind that JSON is case-sensitive.
Now, name still isn't a string, but rather a JValue object, but you can get the string value by calling ToString() on it, but you can also use JValue's Value property, which can be handy if the value is a number instead of a string:
let jsonWithNumber = """{ "number" : 42 }"""
let t = JsonConvert.DeserializeObject jsonWithNumber
let actual = t?number?Value
Assert.Equal(42L, actual)
I recommend to use Json type provider instead.

Resources