How to convert the following code, which uses c# HtmlAgility library, to an elegant style?
if node <> null then
let nodes = node.SelectNodes("//input[@name='xxx']")
if nodes <> null then
let first = nodes.[0]
if first <> null then
let value = first.Attributes.["value"]
if value <> null then
Some value.Value
else
None
else
None
else
None
else
None
Could the following code may work? However, it still not as concise as C# 6's ?.
operator.
let toOpt = function null -> None | x -> Some x
node |> toOpt
|> Option.map (fun x -> x.SelectNodes("//input[@name='xxx']"))
|> Option.map (fun x -> x.[0] )
|> Option.map (fun x -> x.Attributes.["value"] )
|> Option.map (fun x -> x.Value )
C# 6 version is still much more concise:
node?.SelectNodes("//input[@name='xxx']")[0]?.Attributes["value"]?.Value
Can Option.bind
helps?
FYI F#4 has added Option.ofObj
In F# null
is avoided for good reasons. When dealing with C# libraries that rely on null
my general advice would be to provide an F# idiomatic "adapter" on that library.
In practice that might be quite much work and the result might not be as succinct as the C# operator ?.
(leaving aside the argument whether such an operator is a good idea or not).
To my knowledge the F# compiler doesn't support such an operator but if you feel strongly for it you should raise it at: http://fslang.uservoice.com/ . The F# community is friendly but I suspect you will have to argue quite vigorously in order to convince the community that it's a good idea for F#.
Meanwhile; one way to make it slightly more succinct is to create a computation expression like this (getAttributeValue
is what your code will look like):
// Basically like the classic `maybe` monad
// but with added support for nullable types
module Opt =
let inline Return v : Option<'T> = Some v
let inline ReturnFrom t : Option<'T> = t
let inline ReturnFrom_Nullable ot : Option<'T> =
match ot with
| null -> None
| _ -> Some ot
let inline Bind (ot : Option<'T>) (fu : 'T -> Option<'U>) : Option<'U> =
match ot with
| None -> None
| Some vt ->
let ou = fu vt
ou
let inline Bind_Nullable (vt : 'T) (fu : 'T -> Option<'U>) : Option<'U> =
match vt with
| null -> None
| _ ->
let ou = fu vt
ou
let Delay ft : Option<'T> = ft ()
type OptBuilder() =
member inline x.Return v = Return v
member inline x.ReturnFrom v = ReturnFrom v
member inline x.ReturnFrom v = ReturnFrom_Nullable v
member inline x.Bind (t, fu) = Bind t fu
member inline x.Bind (t, fu) = Bind_Nullable t fu
member inline x.Delay ft = Delay ft
let inline ofObj o =
match o with
| null -> None
| _ -> Some o
open HtmlAgilityPack
let opt = Opt.OptBuilder()
let getAttributeValue (node : HtmlNode) (path : string) : string option =
opt {
let! nodes = node.SelectNodes path
let! node = nodes.[0]
let! attr = node.Attributes.["value"]
return! attr.Value
}
let html = """
<html>
<title>Hello</title>
<body>Yellow <div name='Test' value='Stone'>Div</div></title>
</html>
"""
[<EntryPoint>]
let main argv =
let doc = HtmlDocument ()
doc.LoadHtml html
let r = getAttributeValue doc.DocumentNode "//div[@name='Test']"
printfn "Result: %A" r
0
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With