Looking for something like `let` in `match` expression - f#

Sometimes I use something like this:
match foo a with
| 1 -> printfn "%s" (foo a)
| 0 -> printfn "ok"
In this case I call foo function twice and if it is expensive call I use this code:
let tmp = foo a
match tmp with
| 1 -> printfn "%s" tmp
| 0 -> printfn "ok"
But in this case I have created variable with outer scope (regarding match expression).
I am looking for something like this:
match (foo a) as tmp with
| 1 -> printfn "%s" tmp
| 0 -> printfn "ok
What do you use in this cases ? Is there any elegant solution ?
Update - real example:
let collection = getValuesFromDatabase a
match Array.length collection with
| 0 -> printfn "nothing"
| _ -> bar collection.[0]

Option 1: use a let- or do-block
let result =
let tmp = foo a
match tmp with
| 1 -> printfn "%d" tmp
| 0 -> printfn "ok"
Nesting the whole thing under a let-block keeps from polluting the namespace with tmp. The syntax is a bit heavy, but in return it allows for arbitrary complexity of the local computation.
Alternatively, if your result is a unit, you can replace let with do:
do
let tmp = foo a
match tmp with
| 1 -> printfn "%d" tmp
| 0 -> printfn "ok"
Option 2: use pattern aliasing
When pattern-matching, you can match a value with more than one pattern at once, separating the patterns with &, e.g.:
match [1;2;3] with
| (x::_)&(_::y::_) -> printfn "First element is %d, second element is %d" x y
Here, I am matching the same list with two patterns: x::_ and _::y::_. The example is a bit silly (I could have just matched with x::y::_), but it conveys the idea.
In your example, you can use this mechanism to capture the whole value by matching it with a trivial pattern:
match foo a with
| 1&x -> printfn "%d" x
| 0 -> printfn "ok"
Update: the "real" example
This is in response to your edit, where you provided a "real" example, which deals with a collection.
This "real" example is actually different from the "toy" examples that you provided before, in that you want to capture collection, but you're matching on Array.length collection - not the same thing. In general, there is no shortcut for this, except putting it in a nested do or let block as described above. But in your specific case I could rewrite the match like this:
match getValuesFromDatabase a with
| [||] -> printfn "nothing"
| xs -> bar xs.[0]
Here, instead of calling Array.length, I match the value with an empty array. This way, since I'm matching the collection itself, I can capture it in the second match case and use it to get the first element.
If you wanted to perform a more complex check than just the empty array check, you could also use a pattern guard:
match getValuesFromDatabase a with
| xs when Array.length xs = 0 -> printfn "nothing"
| xs -> bar xs.[0]

In your real example, you can just use if. You are not really pattern matching on any complex data type which is where match shines. If you're testing whether a collection is empty, you can just write something like:
let collection = getValuesFromDatabase a
if Array.length collection = 0 then printfn "nothing"
else bar collection.[0]

Related

F# Extract value within Discriminated Union without matching

I have the following Discriminated Union (DU) declaration:
type Book =
| Dictionary of string[]
| Novel of int[]
| Comics of bool[]
An example:
let x = Dictionary [|"a"; "b"|]
How can I extract the length of the array inside without doing pattern matching and without caring about the data type of the array (in this case: string, int, bool). Note: I have no control over the DU declaration; as a result, I can't write new member method within Book, like getArrayLength()
Of course, we can do it in some way as followed:
match x with
| Dictionary (x: _[]) -> x |> Array.length
| Novel (x: _[]) -> x |> Array.length
| Comics (x: _[]) -> x |> Array.length
But typing x |> Array.length a lot is incovenient. This is a simple example, but we can think of a general problem:
type Animal =
| Dog of DogClass
| Cat of CatClass
| Cow of CowClass
...
... and DogClass, CatClass, etc. may share something. We want to get that shared thing. E.g. those classes inherit from AnimalClass, within which there is countLegs() method. Suppsed there are many animals, pattern matching for all of them while the code block after -> is almost the same. I love the principle DRY (Don't Repeat Yourself).
Is there any convenient way to tackle such problem?
==
EDITED 21.10.2019
I was also looking for some syntax like:
let numEles =
match x with
| _ (arr: _[]) -> x |> Array.Length
| _ -> failwith "No identifiers with fields as Array."
let numLegs =
match anAnimall with
| _ (animal: ?> Animal) -> animal.countLegs()
| _ -> failwith "Can't count legs because of not being an animal."
I think this still follows the spirit of matching, but seem like this approach is not supported.
Realistically, there's no getting around pattern matching here. DUs were, in a way, built for it. Since you don't control the type, you can always add a type extension:
type Book with
member this.Length =
match this with
| Dictionary d -> d.Length
| Novel n -> n.Length
| Comics c -> c.Length
let x = Dictionary [|"a"; "b"|]
printfn "%d" x.Length // Prints 2
Though it's also equally valid to define a Book module with a length function on it if you prefer that:
module Book =
let length b =
match b with
| Dictionary d -> d.Length
| Novel n -> n.Length
| Comics c -> c.Length
let x = Dictionary [|"a"; "b"|]
printfn "%d" (x |> Book.length) // prints 2
But you'll need to write a pattern match expression on the Book type at least once. The fact that every case is made up of data that all has the same property doesn't really help the fact that you need to still identify every case individually.

Second to last item in a list in F#

This is what my initial thought was and there were no issues until it was called; What is wrong with this exactly?
let SecLastItem myList=
List.rev myList
|>printfn myList.[1]
How do I rectify the problem here? What is the best way to find the 2nd to last item of a list?
printfn expects a format first printfn "%A" arg
But there is more, functional programming favors immutability so List.rev returns a new list without modifying mylist
So printing the second item in myList won't give the second to last just the second from start (if it exists otherwise it'll crash)
That said you should separate function doing something and logging/printing that' better for reuse and composability.
// trySecLastItem : 'a list -> 'a option
let trySecLastItem = List.rev >> List.tryItem 2
// usage
printfn "%d" (trySecLastItem someList) // ex Some 42 or None
Now as trySecLastItem returns an option you have to take care of that (using defaultArg for example)
// maybeSecLast : int option
let maybeSecLast = trySecLastItem someList
printfn "%d" (defaultArg maybeSecLast 42)
What is the best way to find the 2nd to last item of a list?
If you do not want to reverse the list, you can use pattern matching and return an option if the list is not long enough for a 'second to last item'. Something like this.
let rec secondToLast ls =
match ls with
| [] -> None
| h :: ht :: [] -> Some(h)
| h :: t -> secondToLast t
test with
printfn "%A" (secondToLast [1; 2; 3; 4])
You need to have a formatter with printfn
let SecLastItem myList=
let revList = List.rev myList
printfn "%A" revList.[1]

How to check the case of a discriminated union with FsUnit?

I'd like to check that a value is of a particular case of a discriminated union, without having to also check any included data. My motivation is to only test one thing with each unit test.
An example is as follows (the last two lines give compilation errors):
module MyState
open NUnit.Framework
open FsUnit
type MyState =
| StateOne of int
| StateTwo of int
let increment state =
match state with
| StateOne n when n = 10 -> StateTwo 0
| StateOne n -> StateOne (n + 1)
| StateTwo n -> StateTwo (n + 1)
[<Test>]
let ``incrementing StateOne 10 produces a StateTwo`` ()=
let state = StateOne 10
(increment state) |> should equal (StateTwo 0) // works fine
(increment state) |> should equal (StateTwo _) // I would like to write this...
(increment state) |> should be instanceOfType<StateTwo> // ...or this
Can this be done in FsUnit?
I'm aware of this answer but would prefer not to have to write matching functions for each case (in my real code there are far more than two).
If you don't mind using reflections, the isUnionCase function from this answer could be handy:
increment state
|> isUnionCase <# StateTwo #>
|> should equal true
Note that it's a bit verbose because you need a function call before comparing values.
A similar but lighter approach could be comparison of tags:
// Copy from https://stackoverflow.com/a/3365084
let getTag (a:'a) =
let (uc,_) = Microsoft.FSharp.Reflection.FSharpValue.GetUnionFields(a, typeof<'a>)
uc.Name
increment state
|> getTag
|> should equal "StateTwo"
Beware that this is not type-safe and you can easily misspell a union case name.
What I would do is to create a similar DUs for comparison purpose:
type MyStateCase =
| StateOneCase
| StateTwoCase
let categorize = function
| StateOne _ -> StateOneCase
| StateTwo _ -> StateTwoCase
In this way, you define categorize once and use it multiple times.
increment state
|> categorize
|> should equal StateTwoCase
It appears FSUnit doesn't (or can't, I'm not sure) directly support this use case.
The next best thing I've found is to declare a TestResult type like the following and use a match to reduce the result to this type.
type TestResult =
| Pass
| Fail of obj
Here is the reducing match
let testResult =
match result with
| OptionA(_) -> Pass
| other -> Fail(other)
Now you can just use should equal to ensure the correct result.
testResult |> should equal Pass
The benefits of this solution are strong typing but more importantly in the failure case you can see what the invalid result was.
It doesn't look very elegant, but you can extract type from a value of state:
let instanceOfState (state: 'a) =
instanceOfType<'a>
And then use it in the test:
(increment state) |> should be (instanceOfState <| StateTwo 88)
EDIT
Yes, unfortunately the type is always MyState. Looks like pattern matching or ugly reflection are inevitable.
What if FsUnit already supports an assertion against a specific union case, albeit one restricted to values of the type Microsoft.FSharp.Core.Choice<_,...,_>?
Let's leverage this with a multi-case active pattern, which uses Reflection to check against the union case name.
open System.Reflection
open Microsoft.FSharp.Reflection
let (|Pass|Fail|) name (x : obj) =
let t = x.GetType()
if FSharpType.IsUnion t &&
t.InvokeMember("Is" + name,
BindingFlags.GetProperty, null, x, null )
|> unbox then Pass
else Fail x
Should be working now:
increment state
|> (|Pass|Fail|) "StateTwo"
|> should be (choice 1)

Using incomplete pattern matching as filter?

Suppose I have the following code:
type Vehicle =
| Car of string * int
| Bike of string
let xs = [ Car("family", 8); Bike("racing"); Car("sports", 2); Bike("chopper") ]
I can filter above list using incomplete pattern matching in an imperative for loop like:
> for Car(kind, _) in xs do
> printfn "found %s" kind;;
found family
found sports
val it : unit = ()
but it will cause a:warning FS0025: Incomplete pattern matches on this expression. For example, the value 'Bike (_)' may indicate a case not covered by the pattern(s). Unmatched elements will be ignored.
As the ignoring of unmatched elements is my intention, is there a possibility to get rid of this warning?
And is there a way to make this work with list-comprehensions without causing a MatchFailureException? e.g. something like that:
> [for Car(_, seats) in xs -> seats] |> List.sum;;
val it : int = 10
Two years ago, your code was valid and it was the standard way to do it. Then, the language has been cleaned up and the design decision was to favour the explicit syntax. For this reason, I think it's not a good idea to ignore the warning.
The standard replacement for your code is:
for x in xs do
match x with
| Car(kind, _) -> printfn "found %s" kind
| _ -> ()
(you could also use high-order functions has in pad sample)
For the other one, List.sumBy would fit well:
xs |> List.sumBy (function Car(_, seats) -> seats | _ -> 0)
If you prefer to stick with comprehensions, this is the explicit syntax:
[for x in xs do
match x with
| Car(_, seats) -> yield seats
| _ -> ()
] |> List.sum
You can silence any warning via the #nowarn directive or --nowarn: compiler option (pass the warning number, here 25 as in FS0025).
But more generally, no, the best thing is to explicitly filter, as in the other answer (e.g. with choose).
To explicitly state that you want to ignore unmatched cases, you can use List.choose and return None for those unmatched elements. Your codes could be written in a more idomatic way as follows:
let _ = xs |> List.choose (function | Car(kind, _) -> Some kind
| _ -> None)
|> List.iter (printfn "found %s")
let sum = xs |> List.choose (function | Car(_, seats)-> Some seats
| _ -> None)
|> List.sum

Is this correct use of pattern matching and active patterns?

I'm new to F# and functional and am working on some HTML parsing code. I want to remove from a HTML document elements that match some criteria. Here I have a sequence of objects (HtmlNodes) and want to remove them from the document.
Is this idiomatic way of using pattern matching? Also as HtmlNode.Remove() has a side-effect on the original HtmlDocument object, is there any particular way of structuring the code to make the side-effect obvious or how should this be handled. You can be as pedantic as you like with the code.
open HtmlAgilityPack
let removeNodes (node : HtmlNode) =
let (|HiddenNodeCount|) (n : HtmlNode) =
match n.SelectNodes("*[#style[contains(.,'visibility:hidden')]]") with
| null -> 0
| _ as x -> Seq.length x
match node with
| x when x.Name.ToLower() = "script" -> node.Remove()
| x when x.NodeType = HtmlNodeType.Comment -> node.Remove()
| HiddenNodeCount x when x > 0 -> node.Remove()
| _ -> ()
let html = "some long messy html code would be here"
let dom = new HtmlDocument(OptionAutoCloseOnEnd=true)
dom.LoadHtml(html)
let nodes = dom.DocumentNode.DescendantNodes()
nodes |> Seq.toArray |> Array.iter removeNodes
Personally, I prefer if elif else over pattern matching when you don't have a data structure to decompose (it's just less typing, and may also serve to differentiate between when a structure is being decomposed versus simpler case testing).
There are some odd things in your code. The Active Pattern isn't very helpful here for two reasons: first, its scope is limited to removeNodes so it is only used once. I'll address the second issues later, but first I will show how I would write this by eliminating the Active Pattern and, for me at least, making the side-effects more obvious (by separating the code which tests whether a node should be removed from the code that does the removing):
let shouldRemoveNode (node : HtmlNode) =
if node.Name.ToLower() = "script" then true
elif node.NodeType = HtmlNodeType.Comment then true
else match node.SelectNodes("*[#style[contains(.,'visibility:hidden')]]") with
| null -> false
| x -> Seq.length x > 0
let removeNode (node: HtmlNode) =
if shouldRemoveNode(node) then node.Remove() else ()
Notice I do use a pattern match in the visibility hidden query since I do get to match against null and bind to x otherwise (rather than binding to x, and then testing x with if else).
The second odd thing with your Active Pattern is that you are using it for converting a node to an int, but the length you obtain isn't immediately useful (you still need to perform a test against it). Whereas the more powerful use of an Active Pattern here would be to carve up nodes into different kinds (assuming this isn't ad-hoc, which was may first point). So you could have:
//expand to encompass several other kinds of nodes
let (|Script|Comment|Hidden|Other|) (node : HtmlNode) =
if node.Name.ToLower() = "script" then Script
elif node.NodeType = HtmlNodeType.Comment then Comment
else match node.SelectNodes("*[#style[contains(.,'visibility:hidden')]]") with
| null -> Other
| x -> if Seq.length x > 0 then Hidden
else Other
let removeNode (node: HtmlNode) =
match node with
| Script | Comment | Hidden -> node.Remove()
| Other -> ()
Edit:
#Pascal made the observation in the comments that shouldRemoveNode can be further condensed into one big boolean expression:
let shouldRemoveNode (node : HtmlNode) =
node.Name.ToLower() = "script" ||
node.NodeType = HtmlNodeType.Comment ||
match node.SelectNodes("*[#style[contains(.,'visibility:hidden')]]") with
| null -> false
| x -> Seq.length x > 0
It isn't clear to me that this is any better than using functions and if-then-else, e.g.
let HiddenNodeCount (n : HtmlNode) =
match n.SelectNodes("*[#style[contains(.,'visibility:hidden')]]") with
| null -> 0
| x -> Seq.length x
if node.Name.ToLower() = "script" then
node.Remove()
elif node.NodeType = HtmlNodeType.Comment then
node.Remove()
elif HiddenNodeCount node > 0 then
node.Remove()

Resources