danielwertheim

danielwertheim


notes from a passionate developer

Share


Sections


Tags


Disclaimer

This is a personal blog. The opinions expressed here represent my own and not those of my employer, nor current or previous. All content is published "as is", without warranty of any kind and I don't take any responsibility and can't be liable for any claims, damages or other liabilities that might be caused by the content.

F# - Custom exception with support for formatted strings

I have been coding F# for a while now, and I've managed to stay away from throwing custom exception so chances are I'm doing something wrong now as I'm looking into throwing one, but I thought I would share it, as it also shows how you can accomplish similar constructs as failwithf to allow formatted messages.

Lets start with the simple part, defining the exception

type ProtocolVersion =
    | V1 = 1

exception ProtocolException of ProtocolVersion * string

Raising this exception would be as simple as:

raise (ProtocolException (ProtocolVersion.V1, "Unknown marker found for Encoding."))

And if we want to inject some values in the string:

raise (ProtocolException (ProtocolVersion.V1, sprintf "%i reads '%s'." 42 "fourty two"))

What I want i something like failwith and failwithf:

raise (protocol1Exception "Unknown marker found for Encoding.")

and:

raise (protocol1Exceptionf "%i reads '%s'." 42 "fourty two")

One working solution

I'm honestly not sure if this is THE solution, but it's at least a solution, but feel free to educate me of improvements. As F# is Open Source we can have a look at how e.g. failwithf has been implemented: https://github.com/fsharp/fsharp/blob/cb6cb5c410f537c81cf26825657ef3bb29a7e952/src/fsharp/FSharp.Core/printf.fs#L1645

And looking at this we can put together a working solution like this:

let protocol1Exception message =
    ProtocolException (ProtocolVersion.V1, message)

let protocol1Exceptionf format =
    Printf.ksprintf protocol1Exception format

Trying it out:

let willThrow() =
    raise (protocol1Exception "Unknown marker found for Encoding.")

let willThrow'() =
    raise (protocol1Exceptionf "%i reads '%s'." 42 "fourty two")

try
    willThrow()
with
    | ProtocolException (v,m) -> printfn "Exception message: %s; ProtocolVersion: '%A';" m v

try
    willThrow'()
with
    | ProtocolException (v,m) -> printfn "Exception message: %s; ProtocolVersion: '%A'" m v

Gives:

1: Exception message: Unknown marker found for Encoding.; ProtocolVersion: 'V1';
2: Exception message: 42 reads 'fourty two'.; ProtocolVersion: 'V1'

That's it for this time. Again, feel free to educate me about improvements. Not only about the implementation in itself but also things like: "you should really not throw, but use e.g Result and ROP instead".

Cheers,

//Daniel

View Comments