Generic function to read from a `.json` file - f#

So, I can easily write an arbitrary type to JSON with Newtonsoft.Json:
type X = {
Number: decimal
Sequence: decimal
NumList: decimal list
}
let createItem (n, s, nL) =
{Number = n;
Sequence = s;
NumList = nL}
let items =
[
(1M, 1M, [1M; 2M; 3M])
(2M, 2M, [2M; 4M; 6M])
(3M, 3M, [3M; 6M; 9M])
]
|> List.map createItem
open Newtonsoft.Json
open System.IO
let writeToJson (path: string) (obj: 'a) : unit =
let serialized = JsonConvert.SerializeObject(obj)
File.WriteAllText(path, serialized)
writeToJson "xList.json" items
How can I write a function generic enough that I can read a JSON file? In other words, I'd like something like:
let readFromJson (path: string) (t: 'T) =
let convertToQr = File.ReadAllText(path)
Newtonsoft.Json.JsonConvert.DeserializeObject<t list>(convertToQr)
where the second argument is the Type of the object in path, but I don't know how to do that. If I try to use this function as is, I get a compiler error.
How can I declare in the second argument above the type of the thing that is in path? Can I?

Generic parameters, when explicitly defined, are written in angle brackets immediately after function name, before regular parameters:
let readFromJson<'T>(path: string) =
let convertToQr = File.ReadAllText(path)
Newtonsoft.Json.JsonConvert.DeserializeObject<'T list>(convertToQr)
Usage:
readFromJson<string> "/some/file.json"
Alternatively, you can specify the return type of your function, and let the compiler infer all generic parameters and arguments for you:
let readFromJson(path: string) : 't list =
let convertToQr = File.ReadAllText(path)
Newtonsoft.Json.JsonConvert.DeserializeObject(convertToQr)
Here, the compiler knows that the generic argument of DeserializeObject must be 't list, because its result is being returned from readFromJson, and the result type of readFromJson is explicitly declared to be 't list. Similarly, just by noticing a generic type in the function definition, the compiler will infer that the function has one generic parameter.
In a similar way, you can let the compiler infer the required type when you call the function:
// call inferred to readFromJson<string>, because that's the required return type
let s: string list = readFromJson "/some/file.json"

Related

F# Chaining functions that return tuple

I am new to F# and am trying to chain functions to make a Higher Order Function.
A simplified example is
init returns a tuple
validate accepts a tuple and returns bool
let init : string * string =
("1", "2")
let validate ((a: string), (b: string)) : bool =
a.Equals(b)
let test = init >> validate
ERROR
This expression was expected to have type 'a -> 'b' but here has type 'string * string'
As the answer for Piotr explains, you are getting an error because you have a value and a function. To compose those, you can turn init into a function, but you do not really need to use composition in this case.
If you want to pass a value as an argument to a function, it is typically much simpler to just pass it as an argument:
let res = validate init
Alternatively, if you have a number of functions you want to apply to your input in a sequence, you can do this using the piping operator:
let res = init |> validate
Function composition using >> is a nice functional trick, but I think it is actually much less common in standard F# code than most people think. I use |> all the time, but >> only rarely.
You can only compose functions using the >> combinator. Your first assignment is not a function - it is a binding to a value - your tuple.
You can convert it to a function just by adding empty parameter list () (unit) parameter like this:
let init() : string * string =
("1", "2")
let validate ((a: string), (b: string)) : bool =
a.Equals(b)
let test = init >> validate
let res = test()

How do I pass optional arguments in extension methods?

This code does not work because I am getting options and optional arguments muddled up.
How can I pass an option to an optional argument?
type Foo() =
member this.Bar(?name : string, ?number : int) =
let name = defaultArg name "johndoe"
let number = defaultArg number 0
name + "-" + string number
[<AutoOpen>]
module FooExtensions =
open System
type Foo with
member this.Bar(?name : string, ?numberAsString : string) =
let number =
numberAsString
|> Option.map Int32.Parse
this.Bar(name=name, number=number) // Invalid
You can put the question mark prefix on a named parameter when calling the method, to indicate that you want to pass a value as an option.
this.Bar(?name=name, ?number=number)

How to pick correct method overload for function composition?

Here is a simple composition of functions in F#
let composedFunction = System.Text.Encoding.UTF8.GetBytes >> Array.length
"test" |> composedFunction
Type inference correctly defines the type of composed function string -> int. But compiler cannot pick correct overload of System.Text.Encoding.UTF8.GetBytes method:
Error FS0041: A unique overload for method 'GetBytes' could not be
determined based on type information prior to this program point. A
type annotation may be needed. Candidates:
System.Text.Encoding.GetBytes(chars: char []) : byte [],
System.Text.Encoding.GetBytes(s: string) : byte []Blockquote
Is there any way to compose correct overload of System.Text.Encoding.UTF8.GetBytes which accepts string parameter?
Or course, I can do following
// declare function which calls correct overload and then use it for compostion
let getBytes (s: string) = System.Text.Encoding.UTF8.GetBytes s
let composedFunction = getBytes >> Array.length
// start composition with ugly lambda
let composedFunction =
(fun (s: string) -> s) >> System.Text.Encoding.UTF8.GetBytes >> Array.length
But I wonder if there is any way without additional function declarations to make the compiler pick right overload according to the inferred string -> int type of composed function?
You can always add annotations:
let composedFunction : string -> _ = System.Text.Encoding.UTF8.GetBytes >> Array.length
or
let composedFunction = (System.Text.Encoding.UTF8.GetBytes : string -> _) >> Array.length
As your example shows, .NET methods do not always compose well - I think the idiomatic approach in such situations is just to use the .NET style when you're dealing with .NET libraries (and use functional style when you're dealing with functional libraries).
In your specific case, I would just define a normal function with type annotation and get the length using the Length member rather than using the function:
let composedFunction (s:string) =
System.Text.Encoding.UTF8.GetBytes(s).Length
The existing answer shows how to get the composition to work with type annotations. Another trick you can do (which I would definitely not use in practice) is that you can add identity function on string to the composition to constrain the types:
let composedFunction = id<string> >> System.Text.Encoding.UTF8.GetBytes >> Array.length
It's fun that this works, but as I said, I would never actually use this, because a normal function as defined above is much easier to understand.

Extend F# Arrays with lookup for bigint

I would like to extend F# Arrays such that I can use arrays without converting to the finite int. Instead I want to work with bigint directly.
I was able to add a second length method to the array type as follows:
type 'T ``[]`` with
member this.LengthI: bigint =
bigint this.Length
member this.Item(index: bigint): 'T =
this.[int index]
However the Item method cannot be called with the .[ ] syntax.
Any ideas how this could be achieved? I this possible at all?
I strongly suspect this isn't possible for native arrays. You can verify yourself that you can overload indexed access just fine for other collections.
If you compile the following code:
let myArray = [| "a" |]
let myList = [ "a" ]
let arrayElement = myArray.[11111]
let listElement = myList.[22222]
and inspect the resulting IL, you'll see that while accessing the list element compiles to a regular virtual call, there is a special CIL instruction for accessing a native array element, ldelem.
//000004: let arrayElement = myArray.[11111]
IL_002c: call string[] Fuduoqv1565::get_myArray()
IL_0031: ldc.i4 0x2b67
IL_0036: ldelem [mscorlib]System.String
IL_003b: stsfld string '<StartupCode$51dff40d-e00b-40e4-b9cc-15309089d437>'.$Fuduoqv1565::arrayElement#4
.line 5,5 : 1,33 ''
//000005: let listElement = myList.[22222]
IL_0040: call class [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1<string> Fuduoqv1565::get_myList()
IL_0045: ldc.i4 0x56ce
IL_004a: callvirt instance !0 class [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1<string>::get_Item(int32)
IL_004f: stsfld string '<StartupCode$51dff40d-e00b-40e4-b9cc-15309089d437>'.$Fuduoqv1565::listElement#5
IL_0054: ret
I would guess that the same compiler logic that special-case array access to that single instruction also bypass any overload resolution involving extension methods and the like.
One way to circumvent this is to wrap the array in a custom type, where overloaded indexers will work as you expect. Making the wrapper type a struct should reduce the performance loss in most cases:
type [<Struct>] BigArray<'T>(array : 'T[]) =
member this.LengthI: bigint =
bigint array.Length
member this.Item
with get(index : int) = array.[index]
and set (index : int) value = array.[index] <- value
member this.Item
with get(index : bigint) = array.[int index]
and set (index : bigint) value = array.[int index] <- value
let bigArray = BigArray myArray
let bigArrayElement = bigArray.[0]
let bigArrayElement2 = bigArray.[bigint 0]
Another one is to upcast the array to the base System.Array class, on which you can then define the same overloaded operator. This removes the need to create a wrapper type and duplicate all members of 'T[], as you can just upcast/downcast the same array object as necessary. However, since the base class is untyped, you will lose type safety and have to box/unbox the elements when using the indexed access, which is quite ugly:
type System.Array with
member this.Item
with get (index : int) = (this :?> 'T[]).[index]
and set (index : int) (value : 'T) = (this :?> 'T[]).[index] <- value
member this.Item
with get(index : bigint) : 'T = (this :?> 'T[]).[int index]
and set(index : bigint) (value : 'T) = (this :?> 'T[]).[int index] <- value
let untypedArray = myArray :> System.Array
let untypedArrayElement = box untypedArray.[0] :?> string
let untypedArrayElement2 = box untypedArray.[bigint 0] :?> string

F# Adding value to map result in KeyNotFoundException

type bytesLookup = Map<byte,int list>
type lookupList = bytesLookup list
let maps:bytesLookup = Map.empty
let printArg arg = printfn(Printf.TextWriterFormat<unit>(arg))
let array1 = [|byte(0x02);byte(0xB1);byte(0xA3);byte(0x02);byte(0x18);byte(0x2F)|]
let InitializeNew(maps:bytesLookup,element,index) =
maps.Add(element,List.empty<int>)(*KeyNotFoundException*)
maps.[element]
let MapArray (arr:byte[],maps:bytesLookup ) =
for i in 0..arr.Length do
match maps.TryFind(arr.[i]) with
| Some(e) -> i::e
| None -> InitializeNew(maps,arr.[i],i)
MapArray(array1,maps);
printArg( maps.Count.ToString())
Exception
System.Collections.Generic.KeyNotFoundException: The given key was not
present in the dictionary. at
Microsoft.FSharp.Collections.MapTreeModule.find[TValue,a](IComparer1
comparer, TValue k, MapTree2 m) at
Microsoft.FSharp.Collections.FSharpMap2.get_Item(TKey key) at
FSI_0012.MapArray(Byte[] arr, FSharpMap2 maps) in Script1.fsx:line 16
at .$FSI_0012.main#() in Script1.fsx:line 20
In the function I'm trying to initialize a new element in the map with a list of int. I also try to push a new int value into the list at the same time.
What am I doing wrong?
F# Map is an immutable data structure, the Add method doesn't modify the existing data structure, it returns a new Map with the additions you've requested.
Observe:
let ex1 =
let maps = Map.empty<byte, int list>
maps.Add(1uy, [1]) // compiler warning here!
maps.[1uy]
Two things about this code:
It throws System.Collections.Generic.KeyNotFoundException when you run it
It gives you a compiler warning that the line maps.Add... should have type unit but actually has type Map<byte,int list>. Don't ignore the warning!
Now try this:
let ex2 =
let maps = Map.empty<byte, int list>
let maps2 = maps.Add(1uy, [1])
maps2.[1uy]
No warning. No exception. Code works as expected, returning the value [1].

Resources