I am trying to write a F# function that reads a CSV file and returns its lines as a sequence of strings that can be further processed in a pipelined expression. The function should handle all exceptions that can arise when opening and reading a file. This is what I came up with so far:
// takes a filename and returns a sequence of strings
// returns empty sequence in case file could not be opened
let readFile (f : string) =
try
seq {
use r = new StreamReader(f) // if this throws, exception is not caught below
while not r.EndOfStream do
yield reader.ReadLine() // same here
}
with ex
| ex when (ex :? Exception) ->
printfn "Exception: %s" ex.Message
Seq.empty
The problem here is that the exceptions that could be thrown by StreamReader() and ReadLine() are not caught in the exception handler but instead are left uncaught and lead to program termination. Also, there seems to be no way of trying to catch exceptions inside the seq {} sequence expression. Right now I cannot think of any other way to design such a function than reading the whole file into an intermediate collection like a list or an array beforehand and then returning this collection as a sequence to the callers, thereby loosing all the benefits of lazy evaluation.
Has anybody got a better idea ?
The reason the exceptions are not caught by the try-with handler here is that the body of the seq is lazily executed. readFile returns the sequence without generating an exception, but then trying to execute that sequence generates an exception in the context where it is being used.
Since F# doesn't let you use try-with within a sequence expression, you have to be a bit creative here. You could use Seq.unfold to generate the sequence like so, for instance:
let readFile (f: string) =
try
new StreamReader(f)
|> Seq.unfold
(fun reader ->
try
if not reader.EndOfStream then
Some(reader.ReadLine(), reader)
else
reader.Dispose()
None
with ex ->
printfn "Exception while reading line: %O" ex
reader.Dispose()
None)
with ex ->
printfn "Exception while opening the file: %O" ex
Seq.empty
Perhaps a less tricky approach would be to wrap StreamReader.ReadLine so that it doesn't throw exceptions. That way you can still use a seq expression and a use statement.
let readLine (reader: StreamReader) =
try
reader.ReadLine() |> Some
with ex ->
printfn "Exception while reading line: %O" ex
None
let readFile2 (f: string) =
try
let r = new StreamReader(f)
seq {
use reader = r
let mutable error = false
while not error && not reader.EndOfStream do
let nextLine = readLine reader
if nextLine.IsSome then yield nextLine.Value else error <- true
}
with ex ->
printfn "Exception while opening the file: %O" ex
Seq.empty
let readFile (f : string) =
try File.ReadLines(f)
with ex -> printfn "Exception: %s" ex.Message; Seq.empty
Related
I am pretty new in f# world. I wrote a very small application that query data from sap and show the result as output. When the application try to connect sap, it could throw some exceptions, in case something goes wrong.
Look at following code:
type Customer() =
let mutable _lastName = String.Empty
member self.LastName with get () = _lastName
member self.QueryData () =
//Some CODES here
let bapi = SapBapi()
let bapiFunc = bapi.GetBapiFunc(dest, "BAPI_CUSTOMER_GETDETAIL1")
match bapiFunc with
| Success bp ->
//Some CODES here
let addressData = bp.GetStructure("PE_PERSONALDATA")
_lastName <- addressData.GetString("LASTNAME")
None
| RfcCommunication ex ->
Some(ex :> Exception)
| RfcLogon ex ->
Some(ex :> Exception)
| RfcAbapRuntime ex ->
Some(ex :> Exception)
As you can see, I handle the error with option type and downcast the throwed exception to base exception type.
In the main function
open CustomerBapi
open System
[<EntryPoint>]
let main argv =
let customer = CustomerBapi.Customer()
let ex = customer.QueryData()
match ex with
| Some ex ->
printfn "%s" ex.Message
| None ->
printfn "%s" customer.LastName
Console.ReadLine() |> ignore
0 // return an integer exit code
This code works but do I handle exception in the right way?
I read an article in internet, that handling exception in f# should return an error code, it's more easy then the exception style.
A typical way of handling errors within the type system is to employ an Either type.
type Either<'a,'b> =
| Left of 'a
| Right of 'b
Conventionally Right value carries the success result and Left carries an error or exception (either as a string or an exc type). A simple way to think about it is to treat it like an option where Right corresponds to the Some case and instead of a None you have error information.
So your code could become:
// QueryData no longer needs to depend on side effects to work,
//so you can make it a regular function instead of a method
let result = queryData()
match result with
| Left ex ->
// handle exception
printfn "%s" ex.Message
| Right result ->
// either set the property, or make customer a record
// and set the name field here
customer.LastName <- result
printfn "%s" customer.LastName
The bit about error codes sounds very wrong, would like to know where you found it.
In general I think that your solution is okay, but can be improved.
You mix a bit the functional and OO style in your code. It feels a bit strange to me that you are working with the exception as the only optional value. Usually the customer should be the value which has the optionality included and the match should be if the customer has a value or not.
I'm trying to figure out how to properly test exceptions with FsUnit. Official documentation states, that to test for exceptions I have to right something like this:
(fun () -> failwith "BOOM!" |> ignore) |> should throw typeof<System.Exception>
But, if I don't mark my test method with [<ExpectedException>] attribute it will always fail. Sounds reasonable because if we want to test for exceptions we have to add such attribute in C# + NUnit.
But, as long as I've added this attribute it doesn't matter what kind of exception I'm trying to throw, it will be always handled.
Some snippets:
My LogicModule.fs
exception EmptyStringException of string
let getNumber str =
if str = "" then raise (EmptyStringException("Can not extract number from empty string"))
else int str
My LogicModuleTest.fs
[<Test>]
[<ExpectedException>]
let``check exception``()=
(getNumber "") |> should throw typeof<LogicModule.EmptyStringException>
Answer has been found. To test that exception was thrown I should wrap my function call in the next style:
(fun () -> getNumber "" |> ignore) |> should throw typeof<LogicModule.EmptyStringException>
because underneath #fsunit uses NUnit's Throws constraint
http://www.nunit.org/index.php?p=throwsConstraint&r=2.5 … which takes a delegate of void, raise returns 'a
If you want to test that a specific exception type is raised by some code, you can add the exception type to the [<ExpectedException>] attribute like so:
[<Test; ExpectedException(typeof<LogicModule.EmptyStringException>)>]
let``check exception`` () : unit =
(getNumber "")
|> ignore
More documentation is available on the NUnit site: http://www.nunit.org/index.php?p=exception&r=2.6.2
I've got function to log to console
Printf.kprintf
(printfn
"[%s][%A] %s"
<| level.ToString()
<| DateTime.Now)
format // fprint to System.Console.Out maybe
but it's using Printf.StringFormat as format and now I want to follow same logic and print it to file.
So I try
Printf.kfprintf
(fun f ->
fprintfn file "[%s][%A] "
<| level.ToString()
<| DateTime.Now
) file (format)
And there are two things I can't understand. Why there is unit -> 'A instead of string -> 'A ? How should I use it? And Can I use my StringFormat here as TextWriterFormat ?
Another trouble with this is that with first snippet I inherit format to string -> 'Result thing but in kfprintf I can't do it because there is unit -> 'Result and format message appears before [x][x] stuff. I guess I can somehow inherit format to f but I can't find good example, the only I found is part of F# compiler:
[<CompiledName("PrintFormatToTextWriter")>]
let fprintf (os: TextWriter) fmt = kfprintf (fun _ -> ()) os fmt
[<CompiledName("PrintFormatLineToTextWriter")>]
let fprintfn (os: TextWriter) fmt = kfprintf (fun _ -> os.WriteLine()) os fmt
But how can I use this unit ? How can I post message after my message?
I don't think you need to use Printf.kfprintf, you can carry on using Printf.kprintf as the inner fprintfn uses the TextWriter.
let logToWriter writer level format =
Printf.kprintf (fprintfn writer "[%s][%A] %s"
<| level.ToString()
<| System.DateTime.Now) format
Also see this for an example of using Printf.kfprintf.
I wrote the following code to execute a SQLServer StoredProc in F#
module SqlUtility =
open System
open System.Data
open System.Data.SqlClient
SqlUtility.GetSqlConnection "MyDB"
|> Option.bind (fun con -> SqlUtility.GetSqlCommand "dbo.usp_MyStordProc" con)
|> Option.bind (fun cmd ->
let param1 = new SqlParameter("#User", SqlDbType.NVarChar, 50)
param1.Value <- user
cmd.Parameters.Add(param1) |> ignore
let param2 = new SqlParameter("#PolicyName", SqlDbType.NVarChar, 10)
param2.Value <- policyName
cmd.Parameters.Add(param2) |> ignore
Some(cmd)
)
|> Option.bind (fun cmd -> SqlUtility.ExecuteReader cmd)
|> Option.bind (fun rdr -> ExtractValue rdr)
let GetSqlConnection (conName : string) =
let conStr = ConfigHandler.GetConnectionString conName
try
let con = new SqlConnection(conStr)
con.Open()
Some(con)
with
| :? System.Exception as ex -> printfn "Failed to connect to DB %s with Error %s " conName ex.Message; None
| _ -> printfn "Failed to connect to DB %s" conName; None
let GetSqlCommand (spName : string) (con : SqlConnection) =
let cmd = new SqlCommand()
cmd.Connection <- con
cmd.CommandText <- spName
cmd.CommandType <- CommandType.StoredProcedure
Some(cmd)
let AddParameters (cmd : SqlCommand) (paramList : SqlParameter list) =
paramList |> List.iter (fun p -> cmd.Parameters.Add p |> ignore)
let ExecuteReader (cmd : SqlCommand ) =
try
Some(cmd.ExecuteReader())
with
| :? System.Exception as ex -> printfn "Failed to execute reader with error %s" ex.Message; None
I have multiple problems with this code
First and foremost the repeated use of Option.bind is very irritating... and is adding noise. I need a more clearer way to check if the output was None and if not then proceed.
At the end there should be a cleanupfunction where I should be able to close + dispose the reader, command and connection. But currently at the end of the pipeline all I have is the reader.
The function which is adding parameters... it looks like it is modifying the "state" of the command parameter because the return type is still the same command which was sent it... with some added state. I wonder how a more experienced functional programmer would have done this.
Visual Studio gives me a warning at each of the place where i do exception handling. what's wrong with that" it says
This type test or downcast will always hold
The way I want this code to look is this
let x : MyRecord seq = GetConnection "con" |> GetCommand "cmd" |> AddParameter "#name" SqlDbType.NVarchar 50 |> AddParameter "#policyname" SqlDbType.NVarchar 50 |> ExecuteReader |> FunctionToReadAndGenerateSeq |> CleanEverything
Can you recommend how can I take my code to the desired level and also any other improvement?
I think that using options to represent failed computations is more suitable to purely functional langauges. In F#, it is perfectly fine to use exceptions to denote that a computation has failed.
Your code simply turns exceptions into None values, but it does not really handle this situation - this is left to the caller of your code (who will need to decide what to do with None). You may as well just let them handle the exception. If you want to add more information to the exception, you can define your own exception type and throw that instead of leaving the standard exceptions.
The following defines a new exception type and a simple function to throw it:
exception SqlUtilException of string
// This supports the 'printf' formatting style
let raiseSql fmt =
Printf.kprintf (SqlUtilException >> raise) fmt
Using plain .NET style with a few simplifications using F# features, the code looks a lot simpler:
// Using 'use' the 'Dispose' method is called automatically
let connName = ConfigHandler.GetConnectionString "MyDB"
use conn = new SqlConnection(connName)
// Handle exceptions that happen when opening the connection
try conn.Open()
with ex -> raiseSql "Failed to connect to DB %s with Error %s " connName ex.Message
// Using object initializer, we can nicely set the properties
use cmd =
new SqlCommand( Connection = conn, CommandText = "dbo.usp_MyStordProc",
CommandType = CommandType.StoredProcedure )
// Add parameters
// (BTW: I do not think you need to set the type - this will be infered)
let param1 = new SqlParameter("#User", SqlDbType.NVarChar, 50, Value = user)
let param2 = new SqlParameter("#PolicyName", SqlDbType.NVarChar, 10, Value = policyName)
cmd.Parameters.AddRange [| param1; param2 |]
use reader =
try cmd.ExecuteReader()
with ex -> raiseSql "Failed to execute reader with error %s" ex.Message
// Do more with the reader
()
It looks more like .NET code, but that is perfectly fine. Dealing with databases in F# is going to use imperative style and trying to hide that will only make the code confusing. Now, there is a number of other neat F# features you could use - especially the support for dynamic operators ?, which would give you something like:
let connName = ConfigHandler.GetConnectionString "MyDB"
// A wrapper that provides dynamic access to database
use db = new DynamicDatabase(connName)
// You can call stored procedures using method call syntax
// and pass SQL parameters as standard arguments
let rows = db.Query?usp_MyStordProc(user, policy)
// You can access columns using the '?' syntax again
[ for row in rows -> row?Column1, row?Column2 ]
For more information about this, see the following MSDN series:
How to: Dynamically Invoke a Stored Procedure
Step 1: Create a Database and Show the Poll Options
Step 2: Implement Voting for an Option
I just finish writing my first F# program. Functionality wise the code works the way I wanted, but not sure if the code is efficient. I would much appreciate if someone could review the code for me and point out the areas where the code can be improved.
Thanks
Sudaly
open System
open System.IO
open System.IO.Pipes
open System.Text
open System.Collections.Generic
open System.Runtime.Serialization
[<DataContract>]
type Quote = {
[<field: DataMember(Name="securityIdentifier") >]
RicCode:string
[<field: DataMember(Name="madeOn") >]
MadeOn:DateTime
[<field: DataMember(Name="closePrice") >]
Price:float
}
let m_cache = new Dictionary<string, Quote>()
let ParseQuoteString (quoteString:string) =
let data = Encoding.Unicode.GetBytes(quoteString)
let stream = new MemoryStream()
stream.Write(data, 0, data.Length);
stream.Position <- 0L
let ser = Json.DataContractJsonSerializer(typeof<Quote array>)
let results:Quote array = ser.ReadObject(stream) :?> Quote array
results
let RefreshCache quoteList =
m_cache.Clear()
quoteList |> Array.iter(fun result->m_cache.Add(result.RicCode, result))
let EstablishConnection() =
let pipeServer = new NamedPipeServerStream("testpipe", PipeDirection.InOut, 4)
let mutable sr = null
printfn "[F#] NamedPipeServerStream thread created, Wait for a client to connect"
pipeServer.WaitForConnection()
printfn "[F#] Client connected."
try
// Stream for the request.
sr <- new StreamReader(pipeServer)
with
| _ as e -> printfn "[F#]ERROR: %s" e.Message
sr
while true do
let sr = EstablishConnection()
// Read request from the stream.
printfn "[F#] Ready to Receive data"
sr.ReadLine()
|> ParseQuoteString
|> RefreshCache
printfn "[F#]Quot Size, %d" m_cache.Count
let quot = m_cache.["MSFT.OQ"]
printfn "[F#]RIC: %s" quot.RicCode
printfn "[F#]MadeOn: %s" (String.Format("{0:T}",quot.MadeOn))
printfn "[F#]Price: %f" quot.Price
In general, you should try using immutable data types and avoid imperative constructs such as global variables and imperative loops - although using them in F# is fine in many cases, they should be used only when there is a good reason for doing so. Here are a couple of examples where you could use functional approach:
First of all, to make the code more functional, you should avoid using global mutable cache. Instead, your RefreshCache function should return the data as the result (preferably using some functional data structure, such as F# Map type):
let PopulateCache quoteList =
quoteList
// Generate a sequence of tuples containing key and value
|> Seq.map (fun result -> result.RicCode, result)
// Turn the sequence into an F# immutable map (replacement for hashtable)
|> Map.ofSeq
The code that uses it would be changed like this:
let cache =
sr.ReadLine()
|> ParseQuoteString
|> PopulateCache
printfn "[F#]Quot Size, %d" m_cache.Count
let quot = m_cache.["MSFT.OQ"]
// The rest of the sample stays the same
In the EstablishConnection function, you definitely don't need to declare a mutable variable sr, because in case of an exception, the function will return null. I would instead use option type to make sure that this case is handled:
let EstablishConnection() =
let pipeServer =
new NamedPipeServerStream("testpipe", PipeDirection.InOut, 4)
printfn "[F#] NamedPipeServerStream thread created..."
pipeServer.WaitForConnection()
printfn "[F#] Client connected."
try // Wrap the result in 'Some' to denote success
Some(new StreamReader(pipeServer))
with e ->
printfn "[F#]ERROR: %s" e.Message
// Return 'None' to denote a failure
None
The main loop can be written using a recursive function that stops when EstablishConnection fails:
let rec loop() =
match EstablishConnection() with
| Some(conn) ->
printfn "[F#] Ready to Receive data"
// rest of the code
loop() // continue looping
| _ -> () // Quit
Just a couple thoughts...
You probably want a 'use' rather than a 'let' in a few places, as I think some of the objects in the program are IDisposable.
You may consider wrapping the EstablishConnection method and the final while loop in async blocks (and make other minor changes), so that e.g. you can wait asynchronously for connections without blocking a thread.
At first glance it is written in imperative style rather than functional style, which does make sense given that most of the program involves side effects (i.e. I/O). Line for line, it almost looks like a C# program.
Given the amount of I/O that is taking place, I don't know that there is much you can do to this particular program to make it more of a functional style of coding.