F# match pattern for Expr<int> - f#

I try to find the correct pattern to match and run an Expr<int> using the below code:
open System.Linq
open Microsoft.FSharp.Quotations
open Microsoft.FSharp.Quotations.Patterns
let runSelectQuery (q:Expr<IQueryable<'T>>) =
match q with
| Application(Lambda(builder, Call(Some builder2, miRun, [Quote body])), queryObj) ->
query.Run(Expr.Cast<Microsoft.FSharp.Linq.QuerySource<'T, IQueryable>>(body))
| _ -> failwith "Cannot run this query %s" (q.ToString())
let runCountQuery (q:Expr<int>) =
match q with
| Application(Lambda(builder, Call(None, miRun, [builder2, Quote body])), queryObj) ->
query.Run(Expr.Cast<int>(body))
| _ -> failwith "Cannot run this query %s" (q.ToString())
let countQuery source filter =
let filter = match filter with | Some filter -> filter | _ -> <# fun _ -> true #>
<# query { for item in source do
where ((%filter) item)
count } #>
The runSelectQuery correctly matches the Expr<IQueryable<'T>> pattern. However, I cannot find the correct pattern to match my generic count query Expr<int>
The pattern in the code I derived from the signature of countQuery gives me a:
This expression was expected to have type
Expr but here has type
'a * 'b

Found it! Stupidly I first tried to match the array pattern using a comma separated pattern (as is the list delimiter in C#), that obviously did not work in F# complaining the it was not a list but a tupple and thus not a Rex.
To match agains an int result or any 'T result:
let runQueryToQueryable (q:Expr<IQueryable<'T>>) =
match q with
| Application(Lambda(builder, Call(Some builder2, miRun, [Quote body])), queryObj) ->
query.Run(Expr.Cast<Microsoft.FSharp.Linq.QuerySource<'T, IQueryable>>(body))
| _ -> failwith "Cannot run this query %s" (q.ToString())
let runQueryToType (q:Expr<'T>) =
match q with
| Application(Lambda(builder, Call(None, miRun, [builder2; Quote body])), queryObj) ->
query.Run(Expr.Cast<'T>(body))
| _ -> failwith "Cannot run this query %s" (q.ToString())
Works like a charm.

Related

How to use HtmlDocument's TryGetHtml

Say I have a a function
let GetDataFromWebsite (url:string) =
let webpage = HtmlDocument.Load(url)
let html = webpage.TryGetHtml
html
(note that this will become a longer function once I work out how to use the TryGetHtml function)
This tells me that it has a return string -> unit -> HtmlNode option. What is this exactly returning and how do I use it? I have tried
match GetDataFromWebsite(#"...") with
| None -> "None"
| _ -> (fun a -> a.ToString())
|> printfn "%s"
but visual studio states that:
This expresion was expected to have type
'unit -> FSharp.Data.HtmlNode option'
but here has type
''a option'
Nearly there :)
TryGetHtml is a function, not a property, and you likely want to evaluate it instead of assigning it:
let GetDataFromWebsite (url:string) =
let webpage = HtmlDocument.Load(url)
let html = webpage.TryGetHtml() // note braces
html
Now it returns HtmlNode option you can pattern match on:
match GetDataFromWebsite(#"...") with
| None -> "None"
| Some x -> x.ToString()
|> printfn "%s"
This should compile without errors.

Error using bool.Parse on null/empty values

I have an expression using pipe operator that converts the value to string and then to bool, however sometimes the original value can be null.
How can I use the pattern matching or something else to assume false when the value is null?
type kv = Dictionary<string, obj>
let allDayEvent (d: kv) = d.["fAllDayEvent"] |> string |> bool.Parse
There's quite a few places where you can safeguard via pattern matching: dictionary lookup, casting, parsing. Here's an example with all of those:
let allDayEvent (d: kv) =
match d.TryGetValue "fAllDayEvent" with
| true, v ->
match v with
| null -> printfn "null found"
| :? string as s ->
match bool.TryParse s with
| true, b -> printfn "found a bool: %A" b
| _ -> printfn "That's not a bool?"
| v -> printfn "Found something of type %s" (v.GetType().Name)
| _ -> printfn "No such key"
See also related questions, for example this.
Not sure why you are using a Dictionary, but I would probably have gone for a Map instead. Or at least done some Conversion to Map somewhere. And then I would maybe have thrown in some "automagically" handling of nulls.
And then Pandoras Box is kind of opened, but....
let (|Bool|) str =
match System.Boolean.TryParse(str) with
| (true,bool) -> Some(bool)
| _ -> None
let (|String|) (o:obj) =
match o with
| :? string as s -> Some(s)
| _ -> None
type kv = Dictionary<string, obj>
let allDayEvent (d: kv) =
d :> seq<_>
|> Seq.map (|KeyValue|)
|> Map.ofSeq
|> Map.tryFind "fAllDayEvent"
|> Option.bind (|String|)
|> Option.bind (|Bool|)
Note that allDayEvent in the above now is an Option, which maybe is in fact what you need/want.
And it does keep all data in place. Like true or false is not the same as "did not find stuff" or "could not convert stuff to some bool". Now it is in fact one of the following:
key found and some string like "true": Some(true)
key found and some string like "false": Some(false)
key not found or string not convertable to bool: None
Code is not tested and may need some further massaging.

using a function inside a discriminated union

I would like to do some unit tests on a function that accepts a DU and returns another :
type Commands =
| Schedule of string
| Abandon of string
type Events =
| Scheduled of string
| Abandonned of string
the function is the following :
let exec state = function
| Schedule (cmd) -> Choice1Of2( Scheduled("yes"))
| Abandon(cmd) -> Choice1Of2( Abandonned ("no"))
My tests are as follows :
let result:Choice<Events,string> = exec "initial state" <| Schedule("myCommand");;
result has the following type Choice<Events,string>, I would have loved to get some quick function in order to use them like this :
assertEvent Scheduled (fun e -> Assert.Equal("should produce GameScheduled Event",gameScheduled, e)) result
But to do that I would have the following home made assert function :
let assertEvent<'TEvent> f g result =
match result with
| Choice1Of2(e) ->
match e with
| f(evt) -> g(evt)
| _ -> Assert.None("event not recognised",Some(e))
| Choice2Of2(reason) -> Assert.None("reason",Some(reason))
I was expecting the function f to allow pattern matching on the fly but it does not. Instead I have the following error :
The pattern disciminator 'f' is not defined
Am I doing somthing wrong somewhere ? my fsharp skills are not that high...
A normal function like f can't be used as a pattern discriminator, but you can pass Active Patterns around as arguments:
let assertEvent<'TEvent> (|F|_|) g result =
match result with
| Choice1Of2(e) ->
match e with
| F(evt) -> g(evt)
| _ -> Assert.None("event not recognised",Some(e))
| Choice2Of2(reason) -> Assert.None("reason",Some(reason))
This does, however, require you to also pass an Active Pattern as an argument, which is a bit cumbersome:
assertEvent
(function Scheduled(x) -> Some x | _ -> None)
(fun e -> Assert.Equal("should produce GameScheduled Event",gameScheduled, e))
result
This isn't the way I'd approach the problem, though. What I'd prefer is to write a boolean expression that attempts to pull out and compare the values that I want to verify.
For starters, you could create a little generic helper function to pull out one of the choices from Choice<'a, 'b>:
let toOption1 = function Choice1Of2 x -> Some x | _ -> None
This function has the type Choice<'a,'b> -> 'a option. (I'll leave it as an exercise to define an equivalent toOption2 function.)
Now you can define a boolean expression that pulls out the data if it's there, and compares it with an expected value:
result
|> toOption1
|> Option.map (function Scheduled x -> x | _ -> "")
|> Option.exists ((=) expected)
This is a boolean expression, so you can use Unquote to turn it into an assertion. This is similar to this approach that I've previously described.

Pattern matching based on the function signature

In F# can you pattern match on a function signature. I want to decorate a number of functions with a function that measures the execution of the function and calls out to statsd. The current function I have is:
let WrapFunctionWithPrefix(metrics:Metric.Client.IRecorder, functionToWrap, prefix) =
let metricsIdentifier = (sprintf "%s.%s" prefix Environment.MachineName)
using (metrics.StartTimer(metricsIdentifier)) ( fun metrics -> functionToWrap)
As you can see above, the prefix will vary, and in our application this will vary per function definition. So rather than having to pass in the measure prefix every time I want to do something like the following:
let WrapFunction metrics afunc =
match afunc with
| :? (int -> int) -> WrapFunctionWithPrefix(metrics, afunc, "My function 1")
| :? (string -> string) -> WrapFunctionWithPrefix(metrics, afunc, "My function 2")
| _ -> failwith "Unknown function def"
Is there any way of pattern matching based on the function signature in F#?
Any help appreciated.
Billy
Would it be possible to declare the cases as a DU?
type MyFunctions =
| Intish of int -> int
| Stringish of string -> string
let WrapFunction metrics afunc =
match box afunc with
| :? (int -> int) -> WrapFunctionWithPrefix(metrics, afunc, "My function 1")
| :? (string -> string) -> WrapFunctionWithPrefix(metrics, afunc, "My function 2")
| _ -> failwith "Unknown function def"
will work for your pattern match. You normally end up having to box unknown types before trying to cast them, as :? doesn't like being used on value types.
I'm not totally sure how your using statement will interact with the function you return though. I think it will dispose metrics and return the function immediately, which is probably not what you want.

Filter and convert `list option` to `list`?

I have the following code which will return a seq of DownloadLink for these Urls that can be parsed.
type DownloadLink = { Url: string; Period: DateTime }
nodes |> Seq.map (fun n ->
let url = n.Attributes.["href"].Value
match url with
| Helper.ParseRegex "[a-zA-Z](?<period>\d{4})\.txt" [period] ->
{ Url = url; Period = period }
| _ ->
printfn "Cannot parse %s" url // Error
)
However, I got the following error at the printfn. What's right way to implement it? Should I make it a list option first and then filter out these None items?
Error 1 Type mismatch. Expecting a
string -> DownloadLink
but given a
string -> unit
The type 'DownloadLink' does not match the type 'unit'
The basic problem is that if you have something like
match x with
|true -> A
|false -> B
the type of A and B must be the same.
There is actually a build in function that combines the map and filter using Some that you had though of - use Seq.choose like so
nodes |> Seq.choose (fun n ->
let url = n.Attributes.["href"].Value
match url with
| Helper.ParseRegex "[a-zA-Z](?<period>\d{4})\.txt" [period] ->
Some ({ Url = url; Period = period })
| _ ->
printfn "Cannot parse %s" url // Error
None
)
Aside from Seq.choose, you can also nicely solve the problem using sequence expressions - where you can use yield to return result in one branch, but do not have to produce a value in another branch:
seq { for n in nodes do
let url = n.Attributes.["href"].Value
match url with
| Helper.ParseRegex "[a-zA-Z](?<period>\d{4})\.txt" [period] ->
yield { Url = url; Period = period }
| _ ->
printfn "Cannot parse %s" url }
Aside, I would not recommend doing a side effect (printing) as part of your processing code. If you want to report errors, it might be better to return an option (or define a type which is either Success or Error of string) so that the error reporting is separated from processing.

Resources