How can I employ the "use" declaration within a matching pattern? - f#

The situation: A function returns an Option(Stream). In the calling function I have to unwrap it in a way so that the stream is disposed after use. Where should I put the "use" declaration?
let getSteamOpt = if System.Random().NextDouble() > 0.5 then Some(new MemoryStream()) else None
let createAndUseStreamIfAny =
let streamOpt = getSteamOpt
match streamOpt with
| Some( (* use <-- doesn't work like this *) stream) -> printf "stream has to be disposed after this"
| None -> printf "blah"

let createAndUseStreamIfAny =
let streamOpt = getSteamOpt
match streamOpt with
| Some(s) ->
use stream = s
printf "stream has to be disposed after this"
| None -> printf "blah"

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.

Unable to call a function from recursive f#

trying out F#, learnt a lot today not sure if I am doing this try but I have a pattern match and recursive for some reason I am unable to call this from recursive.
// Define my active recognizer for keywords
let(|MyGirlFriend|Bye|) input =
match input with
|"lonely|"love"|"friendship"
-> MyGirlFriend
|"goodbye"|"bye"|"go"|
-> Bye
|_
-> None
I think the above code that above looks right.
//recursive response function
let rec response (token: string) (str: string) =
match token with
| Bye
-> good_bye_response ()
| RoomLocation
-> sprintf "%s" "Your call is log. Do you wish to quit?"
|_ when token.Contains("yes") -> "good bye" 0
|_ when token.Contains("no") -> answer_response ()
| None when (str.IndexOf(" ") > 0)
-> response (str.Substring(0,str.IndexOf(" ")))
(str.Substring(str.IndexOf(" ")+1))
| None when (str.IndexOf(" ") < 0)
-> response str ""
my function is :
let rec chat () =
if Break = false then
let valueInput = Console.ReadLine()
printf "Helpdesk-BCU Response --> %s \n" (response "" valueInput)
if Break = false then
chat()
else
ChatEnd()
let BCU_response (str: string) =
if (str.IndexOf(" ") > 0) then
response (str.Substring(0,str.IndexOf(" "))) (str.Substring(str.IndexOf("
")+1)) + "\n"
else
response str "" + "\n"
a couple of issues here |_ when token.Contains("yes") -> "goodbye" 0 the zero that is used in F# as an exit on here I get a red line and it states expression should have type string but has type int, I know zero is an int.
so how do I exit the recursive loop?
any sugguestion would be most welcome
It is not entirely clear what part are you struggling with, because there is quite a lot of minor issues in the code. However, a minimal working example that shows how to do the recursion is something like this:
open System
let (|Bye|Other|) input =
match input with
| "goodbye" | "bye" | "go" -> Bye
| _ -> Other
let response (token: string) =
match token with
| Bye -> false, "bye!"
| Other -> true, "sorry, I didn't get that"
let rec chat () =
let input = Console.ReadLine()
let keepRunning, message = response input
printfn ">> %s" message
if keepRunning then chat ()
The response function now also returns a Boolean - if this is true, the chat function calls itself recursively to ask another question. Otherwise, it just returns without asking more questions.

Pattern Matching Types Out Of Strings in F#

I am working on a function that pattern matches some of my user-defined types in f# and converts them to strings. Part of the code looks like the following:
let gsToString (gs : general_structure) : string =
match gs with
| Date(Scattered(Eom(Ascending))) -> "Date(Scattered(Eom(Ascending)))"
| Date(Scattered(Eom(SameDate(dt)))) -> "Date(Scattered(Eom(SameDate(" + dt.ToString() + "))))"
| Number(AllNegative(Int1(Neither))) -> "Number(AllNegative(Int1(Neither)))"
| Number(AllNegative(Int1(SameInt(si)))) -> "Number(AllNegative(Int1(SameFloat(" + si.ToString() + "))))"
There are many other types being matched in this function, but these should be enough to convey the issue. Additionally, the types causing problems are:
| SameDate of System.DateTime
| SameFloat of float
Obviously, It is pretty trivial to do the first pattern matching function that converts my general_structure types to strings. However, a problem arises in my next function (which needs to be called later on in the code), where I need to reconvert the string representation back to a general_structure. The problem areas look like the following:
let stringToGS (str : string) : general_structure =
match str with
| "Date(Scattered(Eom(Ascending)))" -> Date(Scattered(Eom(Ascending)))
| "Date(Scattered(Eom(SameDate(dt))))"-> Date(Scattered(Eom(SameDate(System.DateTime.Parse dt))))
| "Number(AllNegative(Int1(Neither)))" -> Number(AllNegative(Int1(Neither)))
| "Number(AllPositive(Float1(SameFloat(sf))))" -> Number(AllPositive(Float1(SameFloat((float) sf))))
Although the first and the third cases in the stringToGS function work just fine, I am unable to find a way to convert the others back to their original form. If there any way to take a string inside of a pattern matching statement (in this case it would be dt and fs) and somehow parse only that portion of the pattern in order to return a different value (in this case I am trying to make them System.DateTimes and Floats, respectively) and return then to their original forms of:
Date(Scattered(Eom(SameDate(dt))))
Number(AllPositive(Float1(SameFloat(sf))))
? I would appreciate any help.
EDIT:
I was able to resolve the problem by doing something like the following with if statements for the cases that were causing problems:
if str.Contains("Scattered(Eom(SameDate")
then
let p1 = str.IndexOf(")")
let p2 = str.LastIndexOf("(")
let dt1 = str.Remove(p1)
let dt2 = dt1.Substring(p2 + 1)
let date = System.DateTime.Parse dt2
Date(Scattered(Eom(SameDate(date))))
Then, I could just do the normal pattern matching on all of the types that did not contain nested data.
You could also use active patterns, if there is a limited amount of classes and you don't want to use a serialization library:
open System
let (|RegexMatch|_|) pattern input =
let matches = System.Text.RegularExpressions.Regex.Matches(input, pattern)
if matches.Count = 1 then Some matches.[0].Groups.[1].Value
else None
type GeneralStructure =
| NoPayload
| DatePayload of DateTime
| StringPayload of string option
let toString = function
| NoPayload -> "NoPayload"
| DatePayload dt -> sprintf "DatePayload(%d)" <| dt.ToBinary()
| StringPayload None -> "StringPayload(None)"
| StringPayload (Some s) -> sprintf "StringPayload(Some(%s))" s
let fromString = function
| "NoPayload" -> NoPayload
| "StringPayload(None)" -> StringPayload None
| RegexMatch #"DatePayload\((.*)\)" dt -> DatePayload <| DateTime.FromBinary(Int64.Parse dt)
| RegexMatch #"StringPayload\(Some\((.*)\)\)" msg -> StringPayload <| Some msg
| o -> failwithf "Unknown %s %s" typeof<GeneralStructure>.Name o
let serialized = StringPayload <| Some "Foo" |> toString
let deserialized = fromString serialized
let serialized' = DatePayload DateTime.UtcNow |> toString
let deserialized' = fromString serialized'
// val serialized : string = "StringPayload(Some(Foo))"
// val deserialized : GeneralStructure = StringPayload (Some "Foo")
// val serialized' : string = "DatePayload(5247430828937321388)"
// val deserialized' : GeneralStructure = DatePayload 06.08.2015 18:04:10
Note that the regex is not foolproof, I made that up just to fit these cases.

Confusing F# compiler message

The following snippet illustrates the error I'm getting. Even though both match branches return the same thing; I get error, "This expression was expected to have type unit but here has type 'a -> unit" I have no clue what the compiler wants here...
open System.IO
let FileContent contents =
match contents with
| "" -> None
| c -> Some(c)
let WriteSomething (contents:string) =
let writer = new StreamWriter("")
writer.Write( contents ) |> ignore
let DoStuffWithFileContents =
let reader = new StreamReader( "" )
let stuff = reader.ReadToEnd()
match stuff |> FileContent with
| Some(c) -> WriteSomething c
|> ignore
| None -> ignore // <- error on "ignore"
The ignore operator is actually a function that takes a single input and returns the unit type (F#'s equivalent of void). So when you have -> ignore you're returning the ignore function.
Instead, use () to represent the value of the unit type:
| Some(c) -> WriteSomething c
|> ignore
| None -> ()
But actually, since StreamWriter.Write returns void, all these ignore's are unnecessary. You could just as easily write this as:
let WriteSomething (contents:string) =
let writer = new StreamWriter("")
writer.Write(contents)
let DoStuffWithFileContents =
let reader = new StreamReader("")
let stuff = reader.ReadToEnd()
match stuff |> FileContent with
| Some(c) -> WriteSomething c
| None -> ()
Or even better, use Option.iter:
let WriteSomething (contents:string) =
let writer = new StreamWriter("")
writer.Write(contents)
let DoStuffWithFileContents =
let reader = new StreamReader("")
let stuff = reader.ReadToEnd()
stuff |> FileContent |> Option.iter(WriteSomething)
By returning ignore on your last line you are returning a function, not a simple value.
The point of ignore is to convert things to (). Your last line can simply return () directly.

Use of typeof<_> in active pattern

Given the following contrived active pattern:
let (|TypeDef|_|) (typeDef:Type) (value:obj) =
if obj.ReferenceEquals(value, null) then None
else
let typ = value.GetType()
if typ.IsGenericType && typ.GetGenericTypeDefinition() = typeDef then Some(typ.GetGenericArguments())
else None
The following:
let dict = System.Collections.Generic.Dictionary<string,obj>()
match dict with
| TypeDef typedefof<Dictionary<_,_>> typeArgs -> printfn "%A" typeArgs
| _ -> ()
gives the error:
Unexpected type application in pattern matching. Expected '->' or other token.
But this works:
let typ = typedefof<Dictionary<_,_>>
match dict with
| TypeDef typ typeArgs -> printfn "%A" typeArgs
| _ -> ()
Why is typedefof (or typeof) not allowed here?
Even if you're using a parameterized active pattern (where the argument is some expression), the compiler parses the argument as a pattern (as opposed to an expression), so the syntax is more restricted.
I think this is essentially the same problem as the one discussed here: How can I pass complex expression to parametrized active pattern? (I'm not sure about the actual compiler implementation, but the F# specification says that it should parse as a pattern).
As a workaround, you can write any expression inside a quotation, so you could do this:
let undef<'T> : 'T = Unchecked.defaultof<_>
let (|TypeDef|) (typeExpr:Expr) (value:obj) =
let typeDef = typeExpr.Type.GetGenericTypeDefinition()
// ...
let dict = System.Collections.Generic.Dictionary<string,obj>()
match dict with
| TypeDef <# undef<Dictionary<_,_>> #> typeArgs -> printfn "%A" typeArgs
| _ -> ()
Adding to Tomas' answer, the troublesome syntax in this case appears to be with the explicit type arguments. Another workaround is to use a dummy parameter to transmit the type information
let (|TypeDef|_|) (_:'a) (value:obj) =
let typeDef = typedefof<'a>
if obj.ReferenceEquals(value, null) then None
else
let typ = value.GetType()
if typ.IsGenericType && typ.GetGenericTypeDefinition() = typeDef then Some(typ.GetGenericArguments())
else None
let z =
let dict = System.Collections.Generic.Dictionary<string,obj>()
match dict with
| TypeDef (null:Dictionary<_,_>) typeArgs -> printfn "%A" typeArgs
| _ -> ()

Resources