Is it a good idea that a union's deconstructor is publicized while the constructor remains private?

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.