let's assume I have this code:
let a () =
failwith "I want to fail!"
and then I have a nUnit test:
Assert.Throws(fun () -> a() |> ignore)
the test will return:
Method has non-void return value, but no result is expected
Exception doesn't have a stacktrace
How can I test cases where I have an expected failwith situation?
There are a few questions / answers regarding exceptions, but I didn't find an answer for the failwith case.
You can do like this:
Assert.Throws(typeof<System.Exception>, TestDelegate (a)) |> ignore
On a side note you may want to try FsUnit a more friendly framework on top of NUnit. The code would look like this:
(fun () -> a() |> ignore)
|> should throw typeof<System.Exception>
In
Assert.Throws(fun () -> a() |> ignore)
The ignore applies to the function you are testing, not to Assert.Throws. That's no doubt what you intended and is correct.
However, Assert.Throws - unlike most nunit Assertions - actually returns a value: the exception that was thrown. You could probably ignore that as well...
Assert.Throws(fun () -> a() |> ignore) |> ignore
Usually, you would not want to ignore the return, however. The whole point of returning it is that you may want to verify the message, look at the stacktrace, etc. If I didn't want to do anything but assert that some exception was thrown, I would normally use
Assert.That(fun () -> a() |> ignore, Throws.Exception);
Assert.That returns C# void, aka unit.
[All of this is forum code, as I don't have F# installed on this machine. YMMV.]
Related
I've been trying to figure out the idiomatic F# way to download a webpage asynchronously and handle any errors/HTTP failure codes, I think this is my closest attempt so far but I'm getting a type error on the Choice1Of2 line
I'd love to
a) Understand why this fails (I'm still learning F#)
b) Know if this is the right approach/how to make this work or if I'm completely on the wrong track here
FS0001 This expression was expected to have type
'Async<Choice<string,exn>>'
but here has type
'Choice<'a,'b>'
let fetchAsync url = async {
return! Async.Catch(async {
let! str = Http.AsyncRequestString(url)
return str })
}
let result = fetchAsync "http://www.example.bad"
match result with
| Choice1Of2 v -> logger.LogInformation("worked")
| Choice2Of2 ex -> logger.LogInformation("failed")
a) Why it fails
Your fetchAsync function returns an Async<Choice<_>> and you pattern match as if the function only returns Choice<_>. Unfortunately you can't pattern match on Async because that would be a blocking operation, exactly what Async tries move away from.
b) Idiomatic way of dealing with these things.
What you can do, however, is stay within the Async context and handle the failure within. F# provides (at least) two common ways to deal with this. E.g. you can use the async helper functions that allow you to write a pipeline:
let fetchAsyncPipeline (url: string) =
FSharp.Data.Http.AsyncRequestString(url)
|> Async.Catch
|> Async.map (function
| Choice1Of2 v -> Ok v
| Choice2Of2 e -> Error e.Message)
Unfortunately, Async.map is not yet included. But you can define it for yourself like this:
namespace global
[<RequireQualifiedAccess>]
module Async =
let map f xA =
async {
let! x = xA
return f x
}
Or, you can use F#'s computation expressions that provide syntactic sugar to write the above in a more imperative style:
let fetchAsyncCe (url: string) =
async {
try
return!
FSharp.Data.Http.AsyncRequestString(url)
|> Async.map Ok
with
| e -> return Error e.Message
}
In both these solutions, I converted the exception to F#'s result type, which I personally find the nicest way to deal with errors.
Finally, as indicated by brianberns, your expression is only wrapped in the Async type. But unlike C#'s Task, async computations represent the program how to calculate something, but that program has not yet started and you have to explicitly run the asynchronous operation. One of the ways to do this is to use Async.RunSynchronously:
Async.RunSynchronously (fetchAsyncCe "https://fsharpforfunandprofit.com/")
PS: You probably came across Scott Wlaschin's excellent F# for fun and profit, but if you didn't you should check it out. Personally, I've found it the best resource to teach you F# and functional programming in general.
The reason this doesn't compile is because you're never actually running the computation created by fetchAsync. Something like this should work instead:
let fetchAsync url =
Http.AsyncRequestString(url)
|> Async.Catch
|> Async.RunSynchronously
After a few more hours and a lot of coffee I have this
let fetchAsync2 (url: string) =
async { return! Async.Catch(Http.AsyncRequestString(url)) }
|> Async.RunSynchronously
let result = fetchAsync2 url
match result with
| Choice1Of2 html -> html
| Choice2Of2 error -> error.Message
This code works the way I want it to but I'll leave the question open in case anyone has a better solution
Merging my version and the one provided by #brianberns I think I like this one best?
let fetchAsync3 (url: string) =
Http.AsyncRequestString(url)
|> Async.Catch
|> Async.RunSynchronously
let result = fetchAsync3 url
match result with
| Choice1Of2 html -> html
| Choice2Of2 error -> error.Message
I am learning F# but I just don't understand how I am supposed to use ToString. Below are a few attempts. The syntax errors are saying it is expecting type string but that it is actually type uint -> string. So it doens't actually appear to be invoking a function? Could this be explained? This seems like such a simple thing to do but I can't figure it out.
open System
open System.IO
open FSharp.Data
[<EntryPoint>]
let main (args: string[]) =
let htmlPage = HtmlDocument.Load("https://scrapethissite.com/")
printfn "%s" htmlPage.ToString // This causes a syntax error
htmlPage.ToString
|> (fun x -> printfn "%s" x) // This also causes a syntax error
0
.ToString is a method, not a value. In F# every method and every function has a parameter. In fact, that's how functions differ from values (and methods from properties): by having a parameter.
Unlike in C#, F# methods and functions cannot be parameterless. If there is nothing meaningful that you'd want to pass to the method, that method would still have one parameter of type unit. See how this is visible in the error message? unit -> string is the type.
To call such method, you have to pass it the parameter. The sole value of type unit is denoted (). So to call the method you should do:
htmlPage.ToString ()
|> printfn "%s"
Your first example is a bit more complicated. The following would not work:
printfn "%s" htmlPage.ToString ()
Why? Because according to F# syntax this looks like calling printfn and passing it three parameters: first "%s", then htmlPage.ToString, and finally (). To get the correct order of calls you have to use parentheses:
printfn "%s" (htmlPage.ToString ())
And finally, general piece of advice: when possible try to avoid methods and classes in F# code. Most things can be done with functions. In this particular case, the ToString methods can be replaced with the equivalent function string:
printfn "%s" (string htmlPage)
I have a lot of modules that upon starting the program are supposed to add certain things to a single Dictionary found in a higher level module. However, it appears that expressions and constants within a module are packed into static constructors when compiling to a Console App, so these aren't evaluated unless explicitly referenced/when the program thinks they are needed.
There have been a few questions on here regarding initializing modules, and the consensus has been that it is not possible to force. However, I have not seen any of them explore reflection in this regard. In C# I know you are able to invoke the static constructor of a type, so I have attempted the same with F# modules.
My attempts have involved adding a custom attribute (MessageHandlerAttribute) to each module containing such an expression that I want evaluated upon starting the program, and then running this:
let initAllMessageHandlerModules =
Assembly.GetExecutingAssembly().GetTypes()
|> Array.choose (fun typ ->
typ.CustomAttributes
|> Seq.tryFind (fun attr -> attr.AttributeType = typeof<MessageHandlerAttribute>)
|> Option.map (fun _ -> typ))
|> Array.iter
(fun typ -> try typ.TypeInitializer.Invoke(null, null) |> ignore with | ex -> printfn "%A" ex)
But this gives me the following error:
System.NullReferenceException: Object reference not set to an instance of an object.
I have also tried to swap the final lambda function with this:
(fun typ -> try System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(typ.TypeHandle) |> ignore with | ex -> printfn "%A" ex)
But this appears to do nothing. Is it possible to achieve this?
Use type.GetConstructor instead of type.TypeInitializer.
(fun typ -> try typ.GetConstructor(BindingFlags.Instance ||| BindingFlags.Public, null, CallingConventions.Standard, Array.empty<Type>, Array.empty<ParameterModifier>).Invoke(null, null) |> ignore with | ex -> printfn "%A" ex)
Here are some more examples
So this is just a curiosity question.
If I want to return unit, which is better practice?
|> ignore
or
()
There's probably other ways as well. I just want to know what's best, considering these:
What is most performant
What is best practice for a production environment
What is most readable for long term maintanance
I think you are comparing things that are not quite comparable here. The () value lets you create the unit value, while |> ignore is what you can use to ignore some other result. The two are not exactly the same:
If you are calling a function and you want to ignore the result, you can write just:
doStuff () |> ignore
But doing the same with () would require you to either ignore the warning:
doStuff () // warning: Result is ignored
()
... or you could assign the result to an ignore pattern _ using let binding:
let _ = doStuff ()
()
So, in this case, using ignore is better - it is inlined, so it has no performance implications and it leads to code that is easier to read.
That said, there are cases where you just need to create a unit value and then () is what you need (and there is no obvious way ignore would let you do the same). For example:
match optMessage with
| Some message -> printfn "ANNOUNCEMENT: %s" message
| None -> ()
You could replace () with 42 |> ignore to get the same result, but it would be silly!
ignore is an inlined function so both will produce exactly the same IL.
ignore is more explicit and therefore more readable, and that's why it exists, so you should probably prefer that.
I keep running into the following problem:
(System.Console.ReadLine ()).Split [|'('; ')'|]
|> Array.filter (fun s -> not (System.String.IsNullOrEmpty (s)))
|> Array.map (fun s -> s.Split [|','|])
|> Array.map (fun s -> Array.map (fun t -> t.Trim ()) s) (* t.Trim () is underlined with a red squiggly line *)
|> [MORE CODE]
The error associated with the red squiggly line is:
Lookup on object of indeterminate type based on information prior to this program point. A type annotation may be needed prior to this program point to constrain the type of the object. This may allow the lookup to be resolved.
But when the mouse pointer hovers over t, IntelliSense correctly says that t is of type string.
I can bypass the error by writing fun (t : string) -> [CODE], but I wonder why Visual Studio draws the squiggly line at all when it is already detecting the type of the variable correctly. Is this a simple bug in the trial build, or am I misunderstanding something about F#'s type inferencing?
Thanks in advance for your responses.
There is some gap between F# intellisense and F# type checker.
The type checker works from left to right in order. Your problem can be fixed be changing:
fun s -> Array.map (fun t -> t.Trim()) s
to
fun s -> s |> Array.map (fun t -> t.Trim())
Since type of s is available before using Array.map, the type checker is able to infer type of t.
Remarks:
This error usually occurs when using instance methods. You could replace these methods by static functions with extra type annotation and refactor to make your code more composable:
let split arr (s: string) = s.Split arr
let trim (s: string) = s.Trim()
System.Console.ReadLine()
|> split [|'('; ')'|]
|> Array.filter (not << System.String.IsNullOrEmpty)
|> Array.map (split [|','|])
|> Array.map (Array.map trim)