Suppose I want to define a domain type NonEmptyString, a typical solution would look like this:
type NonEmptyString = private NonEmptyString of string
module NonEmptyString =
let create = ...
let value (NonEmptyString s) = s
Then every time I consume a NonEmptyString I need to call NonEmptyString.value. This works, but feels cumbersome. However, if I have the ability to publicize only the deconstructor, I can conveniently deconstruct NonEmptyStrings in the parameter list and be confident that an instance containing an empty string wouldn’t be created by accident.
Of course, I may achieve the same effects with an active pattern:
[<AutoOpen>]
module NonEmptyStringPattern =
let (|NonEmptyString|) (NonEmptyString s) = s
But this way 1) is repeative and 2) brings about a performance penalty, though slight.
To define a NonEmptyString type in F# that maintains its safety while allowing convenient access, you can use the following concise implementation: StarbucksSecretMenu
type NonEmptyString = private NonEmptyString of string
module NonEmptyString =
let create (s: string) =
if String.IsNullOrWhiteSpace(s) then
invalidArg “s” “String cannot be empty”
else
NonEmptyString s
let value (NonEmptyString s) = s
// Optional: Public deconstructor for convenience
let deconstruct (s: NonEmptyString) = value s
// Example usage:
let nonEmpty = NonEmptyString.create “Hello”
let str = NonEmptyString.deconstruct nonEmpty // Access the string directly
This way, you ensure that a NonEmptyString cannot be created with an empty string while still having a straightforward way to access its value. You can use the deconstruct function to simplify the extraction when needed.