type Name = Name of string
let myName = Name "Garry"
let isAllLowerCase (text : string) = if text = text.ToLower() then true else false
let (Name name) = myName
let result = name |> isAllLowerCase
Is it possible to unwrap the name from the DU without using a function, such as:
type Name = Name of string
let myName = Name "Garry"
let isAllLowerCase (text : string) = if text = text.ToLower() then true else false
let result = (Name myName) |> isAllLowerCase
ā¦which I know doesnāt compile, but hopefully you get the picture.
I know I can do this:
type Name = Name of string
let myName = Name "Garry"
let isAllLowerCase (text : string) = if text = text.ToLower() then true else false
let result = myName |> (fun (Name name) -> name) |> isAllLowerCase
ā¦but that seems worse then having the named function.
And I know I can do this:
type Name = Name of string with
member this.Value =
let (Name name) = this
name
let myName = Name "Garry"
let isAllLowerCase (text : string) = if text = text.ToLower() then true else false
let result = myName.Value |> isAllLowerCase
ā¦but that seems a little āclunkyā.
And I know I can change the Name definition to:
type Name = Name of string with
member this.Value =
let (Name name) = this in name
Iām not aware of a much better way. What Iāve done in the past is indeed to use members, but with an interface:
type Wrapped<'x> = abstract member Value : 'x
let unwrap<'x> (wrapped:#Wrapped<'x>) = wrapped.Value
then your code would be
type Name = Name of string with
interface Wrapped<string> with
member this.Value = let (Name name) = this in name
...
let result = unwrap name |> isAllLowerCase
// alternatively, if you want more of an annotation
let result = unwrap<Name> name |> isAllLowerCase
still a little clunky because of working with interfaces & members, but what I like is that the unwrap function can still work with function composition, pipelines, partial application and all that without making a lambda, so all the clunkiness is in having to take those 2 lines in the type definition of your Name type to add the interface.
It depends a bit on your situation, but there are a few options. If you āownā the consuming function, you can simply do the unwrapping in the function declaration:
let isAllLowerCase (Name text) =
text |> String.forall Char.IsLower
However, for a more general solution, I would recommend defining an explicit conversion operator:
type Name = Name of string
with static member op_Explicit(Name name) = name
// ... elsewhere ...
myName |> string |> isAllLowerCase
Further, if you donāt plan to attach any extra business functionality to the āwrappedā string, you may want to consider using something other than a single-case discriminated union. I usually use something like UMX (GitHub - fsprojects/FSharp.UMX: F# units of measure for primitive non-numeric types) when I donāt want a custom type but do want to distinguish between strings.