Hi I have the following code which works as I expect but the compiler warns me about incomplete pattern matching when I pattern match in the Option.defaultWith function. Is there a smarter way to achieve the same effect but without warnings?
I have been thinking about throwing an exception for the rest of the cases but that's pretty ugly.
namespace JsonParser
open System
open System.Globalization
open FSharp.Data
open FSharp.Data.Runtime
type public Key = string
type public Value =
| Int of int
| Double of double
| Decimal of decimal
| String of string
| DateTime of DateTime
| Boolean of Boolean
| Array of Value []
| Guid of Guid
| Null
| Object of Record []
and public Record =
{ Key: Key
Value: Value }
module public Json =
let private culture = CultureInfo.InvariantCulture
let private emptyArray = Array.empty<String>
let rec private map (value: JsonValue) =
JsonConversions.AsInteger culture value
|> Option.map Value.Int
|> Option.orElseWith (fun () -> JsonConversions.AsDecimal culture value |> Option.map Value.Decimal)
|> Option.orElseWith (fun () -> JsonConversions.AsFloat emptyArray true culture value |> Option.map Decimal |> Option.map Value.Decimal)
|> Option.orElseWith (fun () -> JsonConversions.AsGuid value |> Option.map Value.Guid)
|> Option.orElseWith (fun () -> JsonConversions.AsDateTime culture value |> Option.map Value.DateTime)
|> Option.orElseWith (fun () -> JsonConversions.AsBoolean value |> Option.map Value.Boolean)
|> Option.defaultWith (fun () ->
match value with
| JsonValue.String x -> Value.String x
| JsonValue.Null -> Value.Null
| JsonValue.Array x ->
x
|> Array.map map
|> Value.Array
| JsonValue.Record x ->
x
|> Array.map (fun (x, y) ->
{ Key = x
Value = map y })
|> Value.Object)
The answer really depends on how you want to handle various corner cases in your JSON data.
The operations in JsonConversions are implemented in a way where they attempt to convert the value to the target type whenever this can reasonably be done. This means that using those, a value true, 1 and "yes" will all be converted to boolan true. Is this what you want? If so, then I would probably just add a case to the pattern match that throws an exception, saying that the situation should not happen:
match value with
| JsonValue.String x -> Value.String x
| JsonValue.Null -> Value.Null
| JsonValue.Array x -> (...)
| JsonValue.Record x -> (...)
| JsonValue.Float _ | JsonValue.Number _ | JsonValue.Boolean _ ->
failwith "should never happen: Numbers and booleans handled earlier!"
If you want to turn JSON value "yes" to Value.String("yes") rather than to Value.Boolean(true), then it is a lot easier if you directly pattern match on JsonValue:
let rec private map (value: JsonValue) =
match value with
| JsonValue.Float f -> Value.Double f
| JsonValue.Number n -> Value.Decimal n
| JsonValue.Boolean b -> Value.Boolean b
| JsonValue.String x -> Value.String x
| JsonValue.Null -> Value.Null
| JsonValue.Array x ->
x |> Array.map map |> Value.Array
| JsonValue.Record x ->
x |> Array.map (fun (x, y) -> { Key = x; Value = map y }) |> Value.Object
You can find the details about how JsonConversions work by looking at the relevant file in the source code: JsonConversions and TextConversions.
Related
with this union:
type T =
| A
| B
| C
and a T list
I would like to implement something like this pseudo code:
let countOfType (t: Type) (l: T list) =
l
|> List.filter (fun x -> x.GetType() = t)
|> List.length
when I would pass if I want to count the 'A', 'B', etc..
but A.GetType() and B.GetType() return the T type, so this doesn't work.
Is there a way where I could check the type by passing it as a parameter?
The practical case here is that I have a Map that gets updated every few seconds and its values are part of the same DU. I need to be able to see how many of each type, without having to update the code (like a match block) each time an entry gets added.
Addendum:
I simplified the original question too much and realized it after seeing Fyodor's answer.
So I would like to add the additional part:
how could this also be done for cases like these:
type T =
| A of int
| B of string
| C of SomeOtherType
For such enum type T as you specified, you can just use regular comparison:
let countOfType t (l: T list) =
l
|> List.filter (fun x -> x = t)
|> List.length
Usage:
> countOfType A [A; A; B; C; A]
3
> countOfType B [A; A; B; C; A]
1
Try List.choose: ('a -> 'b option) -> 'a list -> 'b list, it filters list based on 'a -> 'b option selector. If selectors evaluates to Some, then value will be included, if selector evaluates to None, then value will be skipped. If you worry about allocations caused by instantiation of Some, then you'll have to implement version that will use ValueOption
let onlyA lis =
lis |> List.choose (function
| (A _) as a -> Some a
| _ -> None)
let onlyB lis =
lis |> List.choose (function
| (B _) as b -> Some b
| _ -> None)
let lis = [
A 1
A 22
A 333
B ""
B "123"
]
lis |> onlyA |> List.length |> printfn "%d"
You can pattern match, and throw away the data, to create a function for the filter.
type T =
| A of int
| B of string
| C of float
[A 3;A 1;B "foo";B "bar";C 3.1; C 4.6]
|> List.filter (fun x ->
match x with
| A _ -> true
| B _ -> false
| C _ -> false
)
|> List.length
But in general i would asume, that you create a predicate function in your modul.
let isA x =
match x with
| A _ -> true
| _ -> false
if you have those functions you can just write
[A 3;A 1;B "foo";B "bar";C 3.1; C 4.6]
|> List.filter isA
|> List.length
I like using ROP when I have to deal with IO/Parsing strings/...
However let's say that I have a function taking 2 parameters. How can you do clean/readable partial application when your 2 parameters are already a Result<'a,'b> (not necessary same 'a, 'b)?
For now, what I do is that I use tuple to pass parameters and use the function below to get a Result of a tuple so I can then bind my function with this "tuple-parameter".
/// Transform a tuple of Result in a Result of tuple
let tupleAllResult x =
match (fst x, snd x) with
| Result.Ok a, Result.Ok b -> (a,b) |> Result.Ok
| Result.Ok a, Result.Error b -> b |> Result.Error
| Result.Error a, _ -> a |> Result.Error
let f (a: 'T, b: 'U) = // something
(A, B) |> tupleAllResult
|> (Result.bind f)
Any good idea?
Here what I wrote, which works but might not be the most elegant
let resultFunc (f: Result<('a -> Result<'b, 'c>), 'd>) a =
match f with
| Result.Ok g -> (g a) |> Result.Ok |> Result.flatten
| Result.Error e -> e |> Result.Error |> Result.flatten
I am not seeing partial application in your example, a concept related to currying and argument passing -- that's why I am assuming that you are after the monadic apply, in that you want to transform a function wrapped as a Result value into a function that takes a Result and returns another Result.
let (.>>.) aR bR = // This is "tupleAllResult" under a different name
match aR, bR with
| Ok a, Ok b -> Ok(a, b)
| Error e, _ | _, Error e -> Error e
// val ( .>>. ) : aR:Result<'a,'b> -> bR:Result<'c,'b> -> Result<('a * 'c),'b>
let (<*>) fR xR = // This is another name for "apply"
(fR .>>. xR) |> Result.map (fun (f, x) -> f x)
// val ( <*> ) : fR:Result<('a -> 'b),'c> -> xR:Result<'a,'c> -> Result<'b,'c>
The difference to what you have in your question is map instead of bind in the last line.
Now you can start to lift functions into the Result world:
let lift2 f xR yR =
Ok f <*> xR <*> yR
// val lift2 :
// f:('a -> 'b -> 'c) -> xR:Result<'a,'d> -> yR:Result<'b,'d> -> Result<'c,'d>
let res : Result<_,unit> = lift2 (+) (Ok 1) (Ok 2)
// val res : Result<int,unit> = Ok 3
A quick question on how to effectively group/filter list/seq.
Filter for only records where the optional field is not None
Remove the "option" parameter to make future processes easier (as None has been filtered out)
Group (this is of no problem I believe)
Am I using the best approach?
Thanks!
type tmp = {
A : string
B : int option }
type tmp2 = {
A : string
B : int }
let inline getOrElse (dft: 'a) (x: 'a option) =
match x with
| Some v -> v
| _ -> dft
let getGrouped (l: tmp list) =
l |> List.filter (fun a -> a.B.IsSome)
|> List.map (fun a -> {A = a.A ; B = (getOrElse 0 (a.B)) })
|> List.groupBy (fun a -> a.A)
The most natural approach for map+filter when option is involved is to use choose, which combines those two operations and drops the option wrapper from the filtered output.
Your example would look something like this:
let getGrouped (l: tmp list) =
l
|> List.choose (fun a ->
a.B
|> Option.map (fun b -> {A = a.A; B = b})
|> List.groupBy (fun a -> a.A)
The simple solution is just use the property that an option can be transformed to list with one or zero elements then you can define a function like:
let t1 ({A=a; B=b} : tmp) =
match b with
| (Some i) -> [{ A = a; B= i}]
| _ -> []
let getGrouped (l: tmp list) =
l |> List.collect t1
|> List.groupBy (fun a -> a.A)
I have a series of validation functions I want to put into an array to execute:
type result = {D: int; E: int; F: int; G: int}
type InvalidReason =
| AAA
| BBB
| CCC
| DDD
| EEE
type Validation =
| Valid
| Invalid of InvalidReason
let validators = [|AAA; BBB; CCC; DDD; EEE|]
let validateStuff result =
validators
|> Array.map(fun v -> v result)
|> Array.contains(Validation.Invalid _)
The problem is that last line of code. I am getting an "Unexpected value _ in the expression." The following does work
|> Array.contains(Validation.Valid)
|> Array.contains(Validation.Invalid InvalidReason.AAA)
But I don't want to spell out each of the sub types for InvalidReasons. Is there some syntax I am overlooking?
The function Array.contains takes a value and checks if that value is in the array. What you're trying to do is to give it a whole bunch of values to check. Well, this won't work: the function only takes one. And it doesn't help that there is no syntax like that in F# :-)
You might use another function that takes multiple values, but a better way to accomplish what you want is to use a function that takes a predicate - Array.exists. Make yourself a predicate to check if a value is "invalid":
let isInvalid x = match x with
| Valid -> false
| Invalid _ -> true
And pass it to Array.exists:
let validateStuff result =
validators
|> Array.map(fun v -> v result)
|> Array.exists isInvalid
Or you could even put that function inline:
let validateStuff result =
validators
|> Array.map(fun v -> v result)
|> Array.exists ( fun x -> match x with
| Valid -> false
| Invalid _ -> true )
Or even shorter, using the function keyword:
let validateStuff result =
validators
|> Array.map(fun v -> v result)
|> Array.exists ( function | Valid -> false | Invalid _ -> true )
Or even shorter, getting rid of as much noise as possible:
let validateStuff result =
validators
|> Array.map(fun v -> v result)
|> Array.exists ( function Invalid _ -> true | _ -> false )
I have several generic equality functions, which are used when overriding Object.Equals:
type IEqualityComparer<'T> = System.Collections.Generic.IEqualityComparer<'T>
let equalIf f (x:'T) (y:obj) =
if obj.ReferenceEquals(x, y) then true
else
match box x, y with
| null, _ | _, null -> false
| _, (:? 'T as y) -> f x y
| _ -> false
let equalByWithComparer (comparer:IEqualityComparer<_>) f (x:'T) (y:obj) =
(x, y) ||> equalIf (fun x y -> comparer.Equals(f x, f y))
Typical usage would be:
type A(name) =
member __.Name = name
override this.Equals(that) =
(this, that) ||> equalByWithComparer StringComparer.InvariantCultureIgnoreCase (fun a -> a.Name)
type B(parent:A, name) =
member __.Parent = parent
member __.Name = name
override this.Equals(that) = (this, that) ||> equalIf (fun x y ->
x.Parent.Equals(y.Parent) && StringComparer.InvariantCultureIgnoreCase.Equals(x.Name, y.Name))
I'm mostly happy with this. It reduces boilerplate[wikipedia]. But I'm annoyed having to use equalBy instead of the more concise equalByWithComparer in type B (since its equality depends on its parent's).
It feels like it should be possible to write a function that accepts a reference to the parent (or 0..N projections), which are checked for equality using Equals, along with a property to be checked and its accompanying comparer, but I've yet been unable imagine its implementation. Perhaps all this is overdone (not sure). How might such a function be implemented?
EDIT
Based on Brian's answer, I came up with this, which seems to work okay.
let equalByProjection proj (comparer:IEqualityComparer<_>) f (x:'T) (y:obj) =
(x, y) ||> equalIf (fun x y ->
Seq.zip (proj x) (proj y)
|> Seq.forall obj.Equals && comparer.Equals(f x, f y))
type B(parent:A, otherType, name) =
member __.Parent = parent
member __.OtherType = otherType //Equals is overridden
member __.Name = name
override this.Equals(that) =
(this, that) ||> equalByProjection
(fun x -> [box x.Parent; box x.OtherType])
StringComparer.InvariantCultureIgnoreCase (fun b -> b.Name)
Another implementation, based on Brian's suggestion:
open System
open System.Collections.Generic
// first arg is always 'this' so assuming that it cannot be null
let rec equals(a : 'T, b : obj) comparisons =
if obj.ReferenceEquals(a, b) then true
else
match b with
| null -> false
| (:? 'T as b) -> comparisons |> Seq.forall(fun c -> c a b)
| _ -> false
// get values and compares them using obj.Equals
//(deals with nulls in both positions then calls <first arg>.Equals(<second arg>))
let Eq f a b = obj.Equals(f a, f b)
// get values and compares them using IEqualityComparer
let (=>) f (c : IEqualityComparer<_>) a b = c.Equals(f a, f b)
type A(name) =
member __.Name = name
override this.Equals(that) =
equals (this, that) [
(fun x -> x.Name) => StringComparer.InvariantCultureIgnoreCase
]
type B(parent:A, name) =
member __.Parent = parent
member __.Name = name
override this.Equals(that) =
equals(this, that) [
Eq(fun x -> x.Parent)
(fun x -> x.Name) => StringComparer.InvariantCultureIgnoreCase
]
Are you just looking for something that takes e.g.
[
(fun x -> x.Parent), (fun a b -> a.Equals(b))
(fun x -> x.Name), (fun a b -> SC.ICIC.Equals(a,b))
]
where you have the list of (projection x comparer) to run on the object? (Probably will need more type annotations, or clever pipelining.)
Just to satisfy Daniel's curiosity, here's how to encode the existential type
exists 'p. ('t -> 'p) * ('p -> 'p -> bool)
in F#. Please don't up-vote this answer! It's too ugly to recommend in practice.
The basic idea is that the existential type above is roughly equivalent to
forall 'x. (forall 'p. ('t -> 'p) * ('p -> 'p -> bool) -> 'x) -> 'x
because the only way that we could implement a value of this type is if we really have an instance of ('t -> 'p) * ('p -> 'p -> bool) for some 'p that we can pass to the first argument to get out a return value of the arbitrary type 'x.
Although it looks more complicated than the original type, this latter type can be expressed in F# (via a pair of nominal types, one for each forall):
type ProjCheckerUser<'t,'x> =
abstract Use : ('t -> 'p) * ('p -> 'p -> bool) -> 'x
type ExistsProjChecker<'t> =
abstract Apply : ProjCheckerUser<'t,'x> -> 'x
// same as before
let equalIf f (x:'T) (y:obj) =
if obj.ReferenceEquals(x, y) then true
else
match box x, y with
| null, _ | _, null -> false
| _, (:? 'T as y) -> f x y
| _ -> false
let checkAll (l:ExistsProjChecker<_> list) a b =
// with language support, this could look more like:
// let checkProj (ExistsProjChecker(proj,check)) = check (proj a) (proj b)
// l |> List.forall checkProj
let checkProj = {new ProjCheckerUser<_,_> with
member __.Use(proj,check) = check (proj a) (proj b) }
l |> List.forall
(fun ex -> ex.Apply checkProj)
let fastIntCheck (i:int) j = (i = j)
let fastStringCheck (s:string) t = (s = t)
type MyType(id:int, name:string) =
static let checks =
// with language support this could look more like:
// [ExistsProjChecker((fun (t:MyType) -> t.Id, fastIntCheck)
// ExistsProjChecker((fun (t:MyType) -> t.Name, fastStringCheck)]
[{ new ExistsProjChecker<MyType> with
member __.Apply u = u.Use ((fun t -> t.Id), fastIntCheck) }
{ new ExistsProjChecker<MyType> with
member __.Apply u = u.Use ((fun t -> t.Name), fastStringCheck) }]
member x.Id = id
member x.Name = name
override x.Equals(y) =
equalIf (checkAll checks) x y
As you can see, the lack of language support results in a lot of boilerplate (basically all of the object creation expressions, calls the the method Use and Apply), which makes this approach unattractive.