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 )
Related
I'm trying to substitute types in a F# Expr, before converting it to an Expression for consumption by a c# lib.
But upon the call to LeafExpressionConverter.QuotationToExpression I receive the error
InvalidOperationException: The variable 't' was not found in the translation context
Basically I'm trying to substitute the equivalent of
<# fun (t: Record) -> t.A = 10 #> to
<# fun (t: Dict) -> t["A"] = 10 #>
Here is the code
type Record = {
A: int
}
type Dict () = //this is the type the c# lib wants (a dictionary representation of a type)
inherit Dictionary<string, obj>()
let substitute<'a> (ex: Expr<'a->bool>) =
let replaceVar (v: Var) = if v.Type = typeof<'a> then Var(v.Name, typeof<Dict>) else v
let tEntityItem = typeof<Dict>.GetProperty("Item")
let isATypeShapeVar = function | ShapeVar var -> var.Type = typeof<'a> | _ -> false
let rec substituteExpr =
function
| PropertyGet(exOpt, propOrValInfo, c) ->
match exOpt with
| None -> Expr.PropertyGet(propOrValInfo)
| Some ex ->
let args = c |> List.map substituteExpr
let newex = substituteExpr ex
match isATypeShapeVar ex with
| true ->
let getter = Expr.PropertyGet(newex, tEntityItem, [Expr.Value(propOrValInfo.Name)] )
Expr.Coerce(getter, propOrValInfo.PropertyType)
| false -> Expr.PropertyGet(newex, propOrValInfo, args)
| ShapeVar var -> Expr.Var (var |> replaceVar)
| ShapeLambda (var, expr) -> Expr.Lambda(var |> replaceVar, substituteExpr expr)
| ShapeCombination(shapeComboObject, exprList) ->
RebuildShapeCombination(shapeComboObject, List.map substituteExpr exprList)
substituteExpr ex |> LeafExpressionConverter.QuotationToExpression
substitute<Record> (<# fun t -> t.A = 10 #>)
I suspect I've missed something in the substitution, but I'm stumped as to what.
The the .ToString() result of the substituted F# Expr is
Lambda (t,
Call (None, op_Equality,
[Coerce (PropertyGet (Some (t), Item, [Value ("A")]), Int32),
Value (10)]))
which looks correct. And other than the coersion, is the equivalent of <# fun (t: Dict) -> t["A"] = 10 #>.ToString()
Why is the QuotationToExpression failing ?
Every time you call replaceVar, you return a different instance of Var. So when you replace the lambda parameter, it's one instance of Var, and later, when you replace newex, that's another instance of Var.
Lambda (t, Call (None, op_Equality, [Coerce (PropertyGet (Some (t), ... ))
^ ^
| |
---------------------------------------------------------
These are different `t`, unrelated, despite the same name
To make this work, you have to make it the same t. The dumbest, most straightforward way would be this:
let substitute<'a> (ex: Expr<'a->bool>) =
let newArg = Var("arg", typeof<Dict>)
let replaceVar (v: Var) = if v.Type = typeof<'a> then newArg else v
...
This will make your particular example work as expected, but it is still unsound, because you're replacing not just specifically the lambda parameter, but any variable of the same type. Which means that if the expression happens to contain any variables of the same type as the parameter, you'd still hit the same problem. For example, try converting this:
<# fun t -> let z = { A = 15 } in z.A = 15 && t.A = 10 #>
You'll get a similar error, but this time complaining about variable z.
A better way would be to maintain a map of variable substitutions as you go, insert new variables as you encounter them for the first time, but get them from the map on subsequent encounters.
An alternative approach would be to fish out specifically the lambda parameter and then replace only it, rather than comparing variable types.
But then there's the next level of weirdness: you're converting any property accessor to an indexer accessor, but in my example above, z.A shouldn't be thus converted. So you have to somehow recognize whether the object of property access is in fact the argument, and that may not be as trivial.
If you're willing to settle for just the case of t.A and fail on more complicated cases like (if true then t else t).A, then you can just match on the lambda argument and pass through any other expression:
let substitute<'a> (ex: Expr<'a->bool>) =
let arg =
match ex with
| ShapeLambda (v, _) -> v
| _ -> failwith "This is not a lambda. Shouldn't happen."
let newArg = Var("arg", typeof<Dict>)
let replaceVar (v: Var) = if v = arg then newArg else v
let tEntityItem = typeof<Dict>.GetProperty("Item")
let isATypeShapeVar = function | ShapeVar var -> var.Type = typeof<'a> | _ -> false
let rec substituteExpr =
function
| PropertyGet(Some (ShapeVar a), propOrValInfo, c) when a = arg ->
let getter = Expr.PropertyGet(Expr.Var newArg, tEntityItem, [Expr.Value(propOrValInfo.Name)] )
Expr.Coerce(getter, propOrValInfo.PropertyType)
| ShapeVar var -> Expr.Var (var |> replaceVar)
| ShapeLambda (var, expr) -> Expr.Lambda(var |> replaceVar, substituteExpr expr)
| ShapeCombination(shapeComboObject, exprList) ->
RebuildShapeCombination(shapeComboObject, List.map substituteExpr exprList)
| ex -> ex
substituteExpr ex |> LeafExpressionConverter.QuotationToExpression
> substituteExpr <# fun t -> let z = { A = 15 } in z.A = 15 && t.A = 10 #>
val it: System.Linq.Expressions.Expression =
ToFSharpFunc(arg => z => ((z.A == 15) AndAlso (Convert(arg.get_Item("A"), Int32) == 10)).Invoke(new Record(15)))
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.
I came across this question about the "pyramid of doom" in F#. The accepted answer there involves using Active Patterns, however my understanding is that it can also be solved using Computation Expressions.
How can I remove the "pyramid of doom" from this code using Computation Expressions?
match a.TryGetValue(key) with
| (true, v) -> v
| _ ->
match b.TryGetValue(key) with
| (true, v) -> v
| _ ->
match c.TryGetValue(key) with
| (true, v) -> v
| _ -> defaultValue
F# for fun and profit has an example for this specific case:
type OrElseBuilder() =
member this.ReturnFrom(x) = x
member this.Combine (a,b) =
match a with
| Some _ -> a // a succeeds -- use it
| None -> b // a fails -- use b instead
member this.Delay(f) = f()
let orElse = new OrElseBuilder()
But if you want to use it with IDictionary you need a lookup function that returns an option:
let tryGetValue key (d:System.Collections.Generic.IDictionary<_,_>) =
match d.TryGetValue key with
| true, v -> Some v
| false, _ -> None
Now here's a modified example of its usage from F# for fun and profit:
let map1 = [ ("1","One"); ("2","Two") ] |> dict
let map2 = [ ("A","Alice"); ("B","Bob") ] |> dict
let map3 = [ ("CA","California"); ("NY","New York") ] |> dict
let multiLookup key = orElse {
return! map1 |> tryGetValue key
return! map2 |> tryGetValue key
return! map3 |> tryGetValue key
}
multiLookup "A" // Some "Alice"
The pattern I like for "pyramid of doom" removal is this:
1) Create a lazy collection of inputs
2) Map them with a computation function
3) skip all the computations that yield unacceptable results
4) pick the first one that matches your criteria.
This approach, however, does not use Computation Expressions
open System.Collections
let a = dict [1, "hello1"]
let b = dict [2, "hello2"]
let c = dict [2, "hello3"]
let valueGetter (key:'TKey) (d:Generic.IDictionary<'TKey, 'TVal>) =
(
match d.TryGetValue(key) with
| (true, v) -> Some(v)
| _ -> None
)
let dicts = Seq.ofList [a; b; c] // step 1
let computation data key =
data
|> (Seq.map (valueGetter key)) // step 2
|> Seq.skipWhile(fun x -> x = None) // step 3
|> Seq.head // step 4
computation dicts 2
A short-circuiting expression can be achieved if we subvert the Bind method, where we are in a position to simply ignore the rest of the computation and replace it with the successful match. Also, we can cater for the bool*string signature of the standard dictionary lookup.
type OrElseBuilder() =
member __.Return x = x
member __.Bind(ma, f) =
match ma with
| true, v -> v
| false, _ -> f ()
let key = 2 in OrElseBuilder() {
do! dict[1, "1"].TryGetValue key
do! dict[2, "2"].TryGetValue key
do! dict[3, "3"].TryGetValue key
return "Nothing found" }
// val it : string = "2"
Is it possible in F# to match a Discriminated Union based on its case rather than by case contents? For example, if I wanted to filter a list by elements that are of the case Flag, is it possible to filter as such? Currently, I am forced to have three separate functions to filter the way I desire. This is the approach I have so far:
type Option =
{Id : string
Arg : string}
type Argument =
| Flag of string
| Option of Option
| Unannotated of string
//This is what I'm going for, but it does not work as the "other" match case will never be matched
let LocateByCase (case:Argument) (args : Argument List) =
args
|> List.filter (fun x -> match x with
| case -> true
| _ -> false)
let LocateAllFlags args =
args
|> List.filter (fun x -> match x with
| Flag y -> true
| _ -> false)
let LocateAllOptions args =
args
|> List.filter (fun x -> match x with
| Option y -> true
| _ -> false)
let LocateAllUnannotated args =
args
|> List.filter (fun x -> match x with
| Unannotated y -> true
| _ -> false)
Am I missing some facet of the F# language that would make this much easier to deal with?
There is no built-in way to find out the case of a DU value. The usual approach, when faced with such requirement, is to provide appropriate functions for each case:
type Argument =
| Flag of string
| Option of Option
| Unannotated of string
with
static member isFlag = function Flag _ -> true | _ -> false
static member isOption = function Option _ -> true | _ -> false
static member isUnannotated = function Unannotated _ -> true | _ -> false
let LocateByCase case args = List.filter case args
let LocateAllFlags args = LocateByCase Argument.isFlag args
(needless to say, the LocateByCase function is actually redundant, but I decided to keep it in to make the answer clearer)
WARNING: DIRTY HACK BELOW
Alternatively, you could provide the case as a quotation, and make yourself a function that will analyze that quotation, fish the case name out of it, and compare it to the given value:
open FSharp.Quotations
let isCase (case: Expr<'t -> Argument>) (value: Argument) =
match case with
| Patterns.Lambda (_, Patterns.NewUnionCase(case, _)) -> case.Name = value.GetType().Name
| _ -> false
// Usage:
isCase <# Flag #> (Unannotated "") // returns false
isCase <# Flag #> (Flag "") // returns true
Then use this function to filter:
let LocateByCase case args = List.filter (isCase case) args
let LocateAllFlags args = LocateByCase <# Flag #> args
HOWEVER, this is essentially a dirty hack. Its dirtiness and hackiness comes from the fact that, because you can't require a certain quotation shape at compile time, it will allow nonsensical programs. For example:
isCase <# fun() -> Flag "abc" #> (Flag "xyz") // Returns true!
isCase <# fun() -> let x = "abc" in Flag x #> (Flag "xyz") // Returns false. WTF?
// And so on...
Another gotcha may happen if a future version of the compiler decides to generate quotations slightly differently, and your code won't recognize them and report false negatives all the time.
I would recommend avoiding messing with quotations if at all possible. It may look easy on the surface, but it's really a case of easy over simple.
I have the following function for parsing an integer:
let rec digits = function
| head::tail when System.Char.IsDigit(head) ->
let result = digits tail
(head::(fst result), snd result)
| rest -> ([], rest)
If I change this function to be an active recognizer, it no longer compiles.
let rec (|Digits|) = function
| head::tail when System.Char.IsDigit(head) ->
let result = Digits tail
(head::(fst result), snd result)
// ^^^^^^ ^^^^^^ see error*
| rest -> ([], rest)
*error FS0001: This expression was expected to have type char list * 'a but here has type
char list
let rec (|Digits|) = function
| head::(Digits (a, b)) when System.Char.IsDigit(head) -> (head::a, b)
| rest -> ([], rest)
NOTE:
if you want to use active pattern as a function you still can do it:
let rec (|Digits|) = function
| head::tail when System.Char.IsDigit(head) ->
let a, b = (|Digits|) tail
(head::a, b)
| rest -> ([], rest)