How to use HtmlDocument's TryGetHtml - f#

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.

Related

final output of Result, in F#

This seems like a question that has an ultra simple answer, but I can't think of it:
Is there a built in method, within Result, for:
let (a: Result<'a, 'a>) = ...
match a with
| Ok x -> x
| Error e -> e
No, because this function requires the Ok type and the Error type to be the same, which makes Result less general.
No, there isn't any function which will allow you to do so. But you can easily define it:
[<RequireQualifiedAccess>]
module Result =
let join (value: Result<'a, 'a>) =
match value with
| Ok v -> v
| Error e -> e
let getResult s =
if System.String.IsNullOrEmpty s then
Error s
else
Ok s
let a =
getResult "asd"
|> Result.join
|> printfn "%s"
It doesn't make Result less general (as said by #brianberns), because it's not an instance member. Existence of Unwrap doesn't make Task less general
Update
After more scrupulous searching inside FSharpPlus and FSharpx.Extras I've found necessary function. It's signature ('a -> 'c) -> ('b -> 'c) -> Result<'a,'b> -> c instead of Result<'a, 'a> -> 'a and it's called Result.either in both libraries (source 1 and source 2). So in order to get value we may pass id as both parameters:
#r "nuget:FSharpPlus"
open FSharpPlus
// OR
#r "nuget:FSharpx.Extras"
open FSharpx
getResult "asd"
|> Result.either id id
|> printfn "%s"
Also it's may be useful to define shortcut and call it Result.join or Result.fromEither as it's called in Haskell

type mismatch error for async chained operations

Previously had a very compact and comprehensive answer for my question.
I had it working for my custom type but now due to some reason I had to change it to string type which is now causing type mismatch errors.
module AsyncResult =
let bind (binder : 'a -> Async<Result<'b, 'c>>) (asyncFun : Async<Result<'a, 'c>>) : Async<Result<'b, 'c>> =
async {
let! result = asyncFun
match result with
| Error e -> return Error e
| Ok x -> return! binder x
}
let compose (f : 'a -> Async<Result<'b, 'e>>) (g : 'b -> Async<Result<'c, 'e>>) = fun x -> bind g (f x)
let (>>=) a f = bind f a
let (>=>) f g = compose f g
Railway Oriented functions
let create (json: string) : Async<Result<string, Error>> =
let url = "http://api.example.com"
let request = WebRequest.CreateHttp(Uri url)
request.Method <- "GET"
async {
try
// http call
return Ok "result"
with :? WebException as e ->
return Error {Code = 500; Message = "Internal Server Error"}
}
test
type mismatch error for the AsyncResult.bind line
let chain = create
>> AsyncResult.bind (fun (result: string) -> (async {return Ok "more results"}))
match chain "initial data" |> Async.RunSynchronously with
| Ok data -> Assert.IsTrue(true)
| Error error -> Assert.IsTrue(false)
Error details:
EntityTests.fs(101, 25): [FS0001] Type mismatch. Expecting a '(string -> string -> Async<Result<string,Error>>) -> 'a' but given a 'Async<Result<'b,'c>> -> Async<Result<'d,'c>>' The type 'string -> string -> Async<Result<string,Error>>' does not match the type 'Async<Result<'a,'b>>'.
EntityTests.fs(101, 25): [FS0001] Type mismatch. Expecting a '(string -> string -> Async<Result<string,Error>>) -> 'a' but given a 'Async<Result<string,'b>> -> Async<Result<string,'b>>' The type 'string -> string -> Async<Result<string,Error>>' does not match the type 'Async<Result<string,'a>>'.
Edit
Curried or partial application
In context of above example, is it the problem with curried functions? for instance if create function has this signature.
let create (token: string) (json: string) : Async<Result<string, Error>> =
and then later build chain with curried function
let chain = create "token" >> AsyncResult.bind (fun (result: string) -> (async {return Ok "more results"}))
Edit 2
Is there a problem with following case?
signature
let create (token: Token) (entityName: string) (entityType: string) (publicationId: string) : Async<Result<string, Error>> =
test
let chain = create token >> AsyncResult.bind ( fun (result: string) -> async {return Ok "more results"} )
match chain "test" "article" "pubid" |> Async.RunSynchronously with
Update: At the front of the answer, even, since your edit 2 changes everything.
In your edit 2, you have finally revealed your actual code, and your problem is very simple: you're misunderstanding how the types work in a curried F# function.
When your create function looked like let create (json: string) = ..., it was a function of one parameter. It took a string, and returned a result type (in this case, Async<Result<string, Error>>). So the function signature was string -> Async<Result<string, Error>>.
But the create function you've just shown us is a different type entirely. It takes four parameters (one Token and three strings), not one. That means its signature is:
Token -> string -> string -> string -> Async<Result<string, Error>>
Remember how currying works: any function of multiple parameters can be thought of as a series of functions of one parameter, which return the "next" function in that chain. E.g., let add3 a b c = a + b + c is of type int -> int -> int -> int; this means that add3 1 returns a function that's equivalent to let add2 b c = 1 + b + c. And so on.
Now, keeping currying in mind, look at your function type. When you pass a single Token value to it as you do in your example (where it's called as create token, you get a function of type:
string -> string -> string -> Async<Result<string, Error>>
This is a function that takes a string, which returns another function that takes a string, which returns a third function which takes a string and returns an Async<Result<whatever>>. Now compare that to the type of the binder parameter in your bind function:
(binder : 'a -> Async<Result<'b, 'c>>)
Here, 'a is string, so is 'b, and 'c is Error. So when the generic bind function is applied to your specific case, it's looking for a function of type string -> Async<Result<'b, 'c>>. But you're giving it a function of type string -> string -> string -> Async<Result<string, Error>>. Those two function types are not the same!
That's the fundamental cause of your type error. You're trying to apply a function that returns a function that returns function that returns a result of type X to a design pattern (the bind design pattern) that expects a function that returns a result of type X. What you need is the design pattern called apply. I have to leave quite soon so I don't have time to write you an explanation of how to use apply, but fortunately Scott Wlaschin has already written a good one. It covers a lot, not just "apply", but you'll find the details about apply in there as well. And that's the cause of your problem: you used bind when you needed to use apply.
Original answer follows:
I don't yet know for a fact what's causing your problem, but I have a suspicion. But first, I want to comment that the parameter names for your AsyncResult.bind are wrong. Here's what you wrote:
let bind (binder : 'a -> Async<Result<'b, 'c>>)
(asyncFun : Async<Result<'a, 'c>>) : Async<Result<'b, 'c>> =
(I moved the second parameter in line with the first parameter so it wouldn't scroll on Stack Overflow's smallish column size, but that would compile correctly if the types were right: since the two parameters are lined up vertically, F# would know that they are both belonging to the same "parent", in this case a function.)
Look at your second parameter. You've named it asyncFun, but there's no arrow in its type description. That's not a function, it's a value. A function would look like something -> somethingElse. You should name it something like asyncValue, not asyncFun. By naming it asyncFun, you're setting yourself up for confusion later.
Now for the answer to the question you asked. I think your problem is this line, where you've fallen afoul of the F# "offside rule":
let chain = create
>> AsyncResult.bind (fun (result: string) -> (async {return Ok "more results"}))
Note the position of the >> operator, which is to the left of its first operand. Yes, the F# syntax appears to allow that in most situations, but I suspect that if you simply change that function definition to the following, your code will work:
let chain =
create
>> AsyncResult.bind (fun (result: string) -> (async {return Ok "more results"}))
Or, better yet because it's good style to make the |> (and >>) operators line up with their first operand:
let chain =
create
>> AsyncResult.bind (fun (result: string) -> (async {return Ok "more results"}))
If you look carefully at the rules that Scott Wlaschin lays out in https://fsharpforfunandprofit.com/posts/fsharp-syntax/, you'll note that his examples where he shows exceptions to the "offside rule", he writes them like this:
let f g h = g // defines a new line at col 15
>> h // ">>" allowed to be outside the line
Note how the >> character is still to the right of the = in the function definition. I don't know exactly what the F# spec says about the combination of function definitions and the offside rule (Scott Wlaschin is great, but he's not the spec so he could be wrong, and I don't have time to look up the spec right now), but I've seen it do funny things that I didn't quite expect when I wrote functions with part of the function definition on the same line as the function, and the rest on the next line.
E.g., I once wrote something like this, which didn't work:
let f a = if a = 0 then
printfn "Zero"
else
printfn "Non-zero"
But then I changed it to this, which did work:
let f a =
if a = 0 then
printfn "Zero"
else
printfn "Non-zero"
I notice that in Snapshot's answer, he made your chain function be defined on a single line, and that worked for him. So I suspect that that's your problem.
Rule of thumb: If your function has anything after the = on the same line, make the function all on one line. If your function is going to be two lines, put nothing after the =. E.g.:
let f a b = a + b // This is fine
let g c d =
c * d // This is also fine
let h x y = x
+ y // This is asking for trouble
I would suspect that the error stems from a minor change in indentation since adding a single space to an FSharp program changes its meaning, the FSharp compiler than quickly reports phantom errors because it interprets the input differently. I just pasted it in and added bogus classes and removed some spaces and now it is working just fine.
module AsyncResult =
[<StructuralEquality; StructuralComparison>]
type Result<'T,'TError> =
| Ok of ResultValue:'T
| Error of ErrorValue:'TError
let bind (binder : 'a -> Async<Result<'b, 'c>>) (asyncFun : Async<Result<'a, 'c>>) : Async<Result<'b, 'c>> =
async {
let! result = asyncFun
match result with
| Error e -> return Error e
| Ok x -> return! binder x
}
let compose (f : 'a -> Async<Result<'b, 'e>>) (g : 'b -> Async<Result<'c, 'e>>) = fun x -> bind g (f x)
let (>>=) a f = bind f a
let (>=>) f g = compose f g
open AsyncResult
open System.Net
type Assert =
static member IsTrue (conditional:bool) = System.Diagnostics.Debug.Assert(conditional)
type Error = {Code:int; Message:string}
[<EntryPoint>]
let main args =
let create (json: string) : Async<Result<string, Error>> =
let url = "http://api.example.com"
let request = WebRequest.CreateHttp(Uri url)
request.Method <- "GET"
async {
try
// http call
return Ok "result"
with :? WebException as e ->
return Error {Code = 500; Message = "Internal Server Error"}
}
let chain = create >> AsyncResult.bind (fun (result: string) -> (async {return Ok "more results"}))
match chain "initial data" |> Async.RunSynchronously with
| Ok data -> Assert.IsTrue(true)
| Error error -> Assert.IsTrue(false)
0

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.

F# match pattern for Expr<int>

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.

How to Get the F# Name of a Module, Function, etc. From Quoted Expression Match

I continue to work on a printer for F# quoted expressions, it doesn't have to be perfect, but I'd like to see what is possible. The active patterns in Microsoft.FSharp.Quotations.Patterns and Microsoft.FSharp.Quotations.DerivedPatterns used for decomposing quoted expressions will typically provide MemberInfo instances when appropriate, these can be used to obtain the name of a property, function, etc. and their "declaring" type, such as a module or static class. The problem is, I only know how to obtain the CompiledName from these instances but I'd like the F# name. For example,
> <# List.mapi (fun i j -> i+j) [1;2;3] #> |> (function Call(_,mi,_) -> mi.DeclaringType.Name, mi.Name);;
val it : string * string = ("ListModule", "MapIndexed")
How can this match be rewritten to return ("List", "mapi")? Is it possible?
FYI, here is my final polished solution from Stringer Bell and pblasucci's help:
let moduleSourceName (declaringType:Type) =
FSharpEntity.FromType(declaringType).DisplayName
let methodSourceName (mi:MemberInfo) =
mi.GetCustomAttributes(true)
|> Array.tryPick
(function
| :? CompilationSourceNameAttribute as csna -> Some(csna)
| _ -> None)
|> (function | Some(csna) -> csna.SourceName | None -> mi.Name)
//usage:
let sourceNames =
<# List.mapi (fun i j -> i+j) [1;2;3] #>
|> (function Call(_,mi,_) -> mi.DeclaringType |> moduleSourceName, mi |> methodSourceName);
You can use F# powerpack for that purpose:
open Microsoft.FSharp.Metadata
...
| Call(_, mi, _) ->
let ty = Microsoft.FSharp.Metadata.FSharpEntity.FromType(mi.DeclaringType)
let name = ty.DisplayName // name is List
However, I don't think if it's possible to retrieve function name with powerpack.
Edit:
As hinted by pblasucci, you can use CompilationSourceName attribute for retrieving source name:
let infos = mi.DeclaringType.GetMember(mi.Name)
let att = infos.[0].GetCustomAttributes(true)
let fName =
(att.[1] :?> CompilationSourceNameAttribute).SourceName // fName is mapi

Resources