This question already has answers here:
Why doesn't pattern matching on a property of a record compile?
(2 answers)
Closed 6 years ago.
Why isn't pattern matching on an assigned value recognized
I receive a warning when I attempt to pattern match on the value called target:
[<Test>]
let ``set center cell to alive``() =
// Setup
let target = (2,2)
let grid = createGrid 9 |> Map.map (fun k v ->
match k with
| target -> { v with Status=Alive }
| _ -> v)
// Test
let center = grid |> getStatus (2,2)
// Verify
center |> should equal Alive
The warning points to:
| target -> { v with Status=Alive }
| _ -> v)
Specifically on:
| _ -> v)
The warning is:
This rule will never be reached.
Which forces me to not use target and instead hard-code the value in order to resolve the warning:
[<Test>]
let ``set center cell to alive``() =
// Setup
let grid = createGrid 9 |> Map.map (fun k v ->
match k with
| (2,2) -> { v with Status=Alive }
| _ -> v)
// Test
let center = grid |> getStatus (2,2)
// Verify
center |> should equal Alive
Can someone explain why I can't do this?
Full code:
type Status = Alive | Dead
type Cell = { X:int; Y:int; Status:Status }
let isNeighbor cell1 cell2 =
let isAbsNeighbor v1 v2 =
match abs (v1 - v2) with
| 0 | 1 -> true
| _ -> false
let isValueNeighbor v1 v2 =
match v1 >= 0
&& v2 >= 0 with
| true -> isAbsNeighbor v1 v2
| _ -> isAbsNeighbor v2 v1
match cell1.X <> cell2.X
|| cell1.Y <> cell2.Y with
| true -> isValueNeighbor cell1.X cell2.X
&& isValueNeighbor cell1.Y cell2.Y
| _ -> false
let createGrid rowCount =
[for x in 1..rowCount do
for y in 1..rowCount do
yield { X=x; Y=y; Status=Dead } ]
|> List.map (fun c -> (c.X, c.Y), { X=c.X; Y=c.Y; Status=Dead })
|> Map.ofList
let getStatus coordinate (grid:Map<(int * int), Cell>) =
match grid.TryFind coordinate with
| Some cell -> cell.Status
| None -> Dead
In a match expression, the rule
match k with
| target -> { v with Status=Alive }
unconditionally matches and binds k to a name target which shadows the existing definition. This means the following clause will never be reached. You can use a conditional match:
match k with
| t when t = target -> { v with Status = Alive }
| _ -> v
According to Pattern Matching your target is a variable pattern, so it shadows the original target value.
Pattern matching is useful for destructuring the matched object, for a simple test plain if-else is preferrable (in my opinion).
A use-case for pattern matching would be if you want to test several cases. Instead of a when guard you could then also use active patterns:
let (|Eq|_|) expected actual =
if expected = actual then Some()
else None
let target = (2,2)
let attempt = (3,2)
match attempt with
| Eq target -> Some "Bulls eye"
| (2, _) -> Some "Almost"
| t when fst t > 20 -> Some "Quite the contrary"
| _ -> None
Related
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 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"
I have several verbal expressions that I've packaged into one function:
open FsVerbalExpressions
open FsVerbalExpressions.VerbalExpression
open System.Text.RegularExpressions
open System
let createOrVerbExFromList (verbExList: VerbEx list) =
let orVerbEx =
verbExList
|> List.reduce (fun acc thing -> verbExOrVerbEx RegexOptions.IgnoreCase acc thing) //simpleVerbEx
orVerbEx
let k12VerbEx =
let kTo12 = ["SCHOOL"; "DIST"; "SD"; "HS"; "BD OF ED"]
kTo12
|> List.map (fun word -> VerbEx(word))
|> createOrVerbExFromList
let twoYearCollegeVerbEx =
VerbEx("2 Year College")
let universityVerbEx =
VerbEx("UNIV")
let privateSchoolVerbEx =
VerbEx("ACAD")
//Here there be dragons:
let newInst (x: string) =
match (isMatch x k12VerbEx) with
| true -> "K - 12"
| _ -> match (isMatch x twoYearCollegeVerbEx) with
| true -> "2 Year College"
| _ -> match (isMatch x universityVerbEx) with
| true -> "University"
| _ -> match (isMatch x privateSchoolVerbEx) with
| true -> "Private / Charter School"
| _ -> "Other"
I'd like to rewrite the newInst function so that it's no longer the "pyramid of doom. My question is how can I get rid of the pyramid of doom? Can I get rid of it? I have the suspicion that it will be some sort of async workflow or other computational expression, but those are all very new to me.
If you are only matching against booleans, then if ... elif is sufficient:
let newInst (x: string) =
if isMatch x k12VerbEx then
"K - 12"
elif isMatch x twoYearCollegeVerbEx then
"2 Year College"
elif isMatch x universityVerbEx then
"University"
elif isMatch x privateSchoolVerbEx then
"Private / Charter School"
else
"Other"
A more flexible possibility would be to create an active pattern:
let (|IsMatch|_|) f x =
if isMatch x f then Some () else None
let newInst (x: string) =
match x with
| IsMatch k12VerbEx -> "K - 12"
| IsMatch twoYearCollegeVerbEx -> "2 Year College"
| IsMatch universityVerbEx -> "University"
| IsMatch privateSchoolVerbEx -> "Private / Charter School"
| _ -> "Other"
When there is sequential repetition of exactly the same form of code, I prefer to use a data-driven approach instead:
let verbExStrings =
[
(k12VerbEx, "K - 12")
(twoYearCollegeVerbEx, "2 Year College")
(universityVerbEx, "University")
(privateSchoolVerbEx, "Private / Charter School")
]
let newInst x =
verbExStrings
|> List.tryPick (fun (verbEx, string) -> if isMatch x verbEx then Some string else None)
|> function Some x -> x | _ -> "Other"
An advantage of this approach is that the raw data (verbExStrings) can come in handy in other places and is not tied to your code implementation.
I need to check if a list starts with another, shorter list. The function, when using when guards is trivial:
let rec startsWith l1 l2 =
match l1, l2 with
| [], _ | _, [] -> true
| x::xs, y::ys when x = y -> startsWith xs ys
| _ -> false
let lst1 = [ 1; 2; 1 ]
let lst2 = [ 1; 2; 1; 2; 3; ]
let lst3 = [ 1; 3; 1; 2; 3; ]
let c1 = startsWith lst1 lst2 // true
let c2 = startsWith lst1 lst3 // false
But whatever I tried along the lines of an Active Pattern:
let (|HeadsMatch|) (l1 : ('a) list) (l2 : ('a) list) =
if l1.Head = l2.Head then Some(l1.Tail, l2.Tail) else None
let rec startsWith l1 l2 =
match l1, l2 with
| [], _ | _, [] -> true
| HeadsMatch /* need to capture the result */ -> startsWith t1 t2
| _ -> false
I could not make to compile. How to make a version of this function using Active pattern? And if this is not possible, can you explain why?
P.S. Any other nice ways to write the above function?
EDIT: I took the snippet from Daniel's answer to not distract from the real question.
EDIT: My problems started in the beginning. I have defined the active pattern function as
let (|HeadsMatch|_|) lst1 lst2 =
but it should have been
let (|HeadsMatch|_|) (lst1, lst2) =
In which case it would match as in the accepted answer.
I suppose the nicest way might be
let startsWith = Seq.forall2 (=)
If you want to write it from scratch, you need to match on both lists:
let rec startsWith l1 l2 =
match l1, l2 with
| [], _ | _, [] -> true
| x::xs, y::ys when x = y -> startsWith xs ys
| _ -> false
If you want to write it with an active pattern for learning purposes, using Tarmil's definition it would be
let rec startsWith l1 l2 =
match l1, l2 with
| [], _ | _, [] -> true
| HeadsMatch(xs, ys) -> startsWith xs ys
| _ -> false
There are two errors in the definition of your active pattern:
It's a partial active pattern (since it is possible that it doesn't match) so the syntax is (|HeadsMatch|_|).
You need to take the two lists as a pair, since you want to match lst1, lst2.
With these, the code will compile. But it will throw an exception at runtime, because you use .Head and .Tail on lists when you don't know if they have any element; you're not defining the behavior if one of the lists is empty.
Here is an idiomatic implementation of HeadsMatch:
let (|HeadsMatch|_|) (lst1, lst2) =
match (lst1, lst2) with
| (x :: xs, y :: ys) when x = y -> Some (xs, ys)
| _ -> None
Without guard:
let (|HeadsMatch|_|) (lst1, lst2) =
match (lst1, lst2) with
| (x :: xs, y :: ys) ->
if x = y then Some (xs, ys) else None
| _ -> None
As a side-note, your first implementation has the same problem. The following will throw a runtime exception:
startsWith [1;2] [1]
because you don't check that lst2 is not empty before using .Head and .Tail on it. In general, you should avoid these two methods almost all the time.
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