How can I OR union cases? - f#

I referenced this link. However, I still don't see how I can OR the following union cases:
match count with
| x when x < 2 -> grid |> setCell { X=x; Y=y; State=Dead }
| x when x > 3 -> grid |> setCell { X=x; Y=y; State=Dead }
| _ -> grid
I want to do this:
match count with
| x when x < 2
| x when x > 3 -> grid |> setCell { X=x; Y=y; State=Dead }
| _ -> grid
Is this possible?

With a boolean OR operator ||
match count with
| x when x > 3 || x < 2 -> grid |> setCell { X=x; Y=y; State=Dead }
| _ -> grid

How about inverting the matches?
match count with
| 1
| 2 -> grid
| _ -> grid |> setCell { X=count; Y=y; State=Dead }

Related

Base case not getting picked up in my F# function

This function is supposed to just return the index of a list. That part works. However when a element is not in a list it must return -1.
For some reason it does not return -1.
let rec search f list =
match list with
| head::tail ->
if f head then 0
else 1 + search f tail
| [] -> -1
printfn "%A" (search (fun x -> x = 5) [ 5; 4; 3; 2 ])
//>> return index 0 for #5
printfn "%A" (search (fun x -> x = 6) [ 5; 4; 3; 2 ])
//>> should return -1 but it returns 3 which is the len of the list not -1
EDIT: Can not use nested functions.
You could use e.g.
let search f list =
let rec where at list =
match list with
| [] -> -1
| head::tail ->
if f head then at
else where (at + 1) tail
where 0 list
which has the benefit of being tail-recursive. Regarding your comment:
let rec search f list =
match list with
| [] -> -1
| head::tail ->
if f head then 0 else
match search f tail with
| -1 -> -1
| i -> i + 1

How to properly pattern match JsonConversions

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.

Removing nested matches in F#?

Suppose I have types like this:
type C =
| W of int
| Z of int
type B =
{
C : C
D : int
}
type A =
| X of int
| Y of B
And I would like to do something for the Z case only:
let a =
Y
{
C = Z 123
D = 456
}
match a with
| X _ -> ()
| Y b ->
match b.C with
| W _ -> ()
| Z z -> printfn "%i" z
Is there a way to achieve this in a single match?
Can this be made more concise?
Yes, there is indeed! Patterns can be nested, that's their primary point. You can match on Y, and inside that match on the fields of B, and inside that match on C.
Like this:
match a with
| X _ -> ()
| Y { C = Z z } -> printfn "%i" z
| Y { C = W _ } -> ()
And since you're returning unit in both non-Y.C.Z cases, you can combine them in a catch-all pattern:
match a with
| Y { C = Z z } -> printfn "%i" z
| _ -> ()

Do I have to use an explicit match statement to identify its wildcard value?

Do I have to use an explicit match statement to identify its wildcard value?
For example, take the following function:
let (|Positive|Neutral|Negative|) = function
| x when x > 0 -> Positive
| x when x = 0 -> Neutral
| x when x < 0 -> Negative
| _ -> failwith (sprintf "unknown: %d" _)
Error:
Unexpected symbol '_' in expression
I learned that I can do this without any errors:
let (|Positive|Neutral|Negative|) v =
match v with
| x when x > 0 -> Positive
| x when x = 0 -> Neutral
| x when x < 0 -> Negative
| _ -> failwith (sprintf "unknown: %d" v)
UPDATE
Here's the result from a posted answer:
let (|Positive|Neutral|Negative|) = function
| x when x > 0 -> Positive
| x when x = 0 -> Neutral
| x when x < 0 -> Negative
| x -> failwith (sprintf "unknown: %d" x)
You can change it to this and it will work:
let (|Positive|Neutral|Negative|) = function
| x when x > 0 -> Positive
| x when x = 0 -> Neutral
| x when x < 0 -> Negative
| f -> failwith (sprintf "unknown: %d" f)

OR pattern matching

I'm trying to use an OR pattern, as described here:
let foo = function
| Some (0, x) when x > 0 | None -> "bar"
| _ -> "baz"
However, this gives a compiler error:
error FS0010: Unexpected symbol '|' in pattern matching. Expected '->'
or other token.
What am I doing wrong? Does it have to do with the when guard?
A when guard refers to a single case, regardless of how many patterns are combined. The cases need to be separated:
let foo = function
| Some (0, x) when x > 0 -> "bar"
| None -> "bar"
| _ -> "baz"
For that reason, it may be better to factor out the return value, so a possibly complex expression isn't repeated:
let foo value =
let ret = "bar"
match value with
| Some (0, x) when x > 0 -> ret
| None -> ret
| _ -> "baz"
Using an active pattern is another way to avoid such repetition:
let (|Bar|_|) = function
| Some(0, x) when x > 0 -> Some()
| None -> Some()
| _ -> None
let foo = function
| Bar -> "bar"
| _ -> "baz"
You'll need two separate match cases there because the two cases bind different sets of variables (x and nothing, respectively):
| Some(0, x) when x>0 -> "bar"
| None -> "bar"
A nice trick I sometime use when you want to guard only specific bindings of a label, in a very complex pattern, is to use my own active patterns and the & (and) pattern operator:
let (|GreaterThan|_|) lowerLimit n =
if n > lowerLimit then Some () else None
let (|LesserThan|_|) upperLimit n =
if n < upperLimit then Some () else None
let (|GreaterOETo|_|) lowerLimit n =
if n >= lowerLimit then Some () else None
let (|LesserOETo|_|) upperLimit n =
if n <= upperLimit then Some () else None
let (|InRange|_|) (lowerLimit, upperLimit) n =
if n >= lowerLimit && n <= upperLimit then Some () else None
let (|Even|Odd|) n =
if n % 2 = 0 then
Even (n / 2)
else
Odd (n / 2)
type Union =
| A of int
| B of int
| A' of int
let getSpecialCases = function
| A (Even (x & GreaterThan 4 & LesserOETo 16))
| A (Odd (x & GreaterThan 0))
| B (x & LesserOETo 0)
| A' (Even (x & InRange (5, 16)))
| A' (Odd (x & GreaterThan 0)) -> Some x
| _ -> None
And of course you can just make a function to active pattern wrapper:
let (|P|_|) pred x =
if pred x then Some () else None
let ``match`` = function
| Even (x & pred (fun x -> x >= 7 && x <= 54)) -> Some x
| _ -> None

Resources