typesf#abstractiondiscriminated-uniongeneric-type-parameters

Can I set a "is a Discriminated Union" type constraint on a generic argument in F# type declaration?


I am creating a type system for some generic types of common input:

// type Validator<'t> when 't :> ??? = Validator of 't
// with member x.validate value condition =
//         if condition then
//             (true, Some ('t value))
//         else
//             (false, None)

module Validatable =
    let validate t value condition =        
        if condition then
            (true, Some (t value))
        else
            (false, None)

type Email = Email of string
with member x.validate(s) = Validatable.validate Email s (Regex.IsMatch(s, ""))

type Name = Name of string
with member x.validate(s) = Validatable.validate Name s (Regex.IsMatch(s, ""))

type PhoneNumber = PhoneNumber of string
with member x.validate(s) = Validatable.validate PhoneNumber s (Regex.IsMatch(s, ""))

You'll see that within the comment, I have another type commented out. I am hoping to use the type defined within the comment to replace the functionality of the validate t value condition function within the Validatable module.

What do I need to replace the ??? with to allow me to say that the generic parameter 't is the case identifier of a Discriminated Union?


Solution

  • A union case isn't a type, it's a function that produces a value of type. So you can write your Validator type like this instead:

    type Validator<'inp, 'out> = Validator of ('inp -> 'out)
    with member x.validate value condition =
            let (Validator f) = x
            if condition then
                (true, Some (f value))
            else
                (false, None)
    

    And use it like this:

    type Email = Email of string
    with member x.validate(s) = (Validator Email).validate s (Regex.IsMatch(s, ""))
    
    type Name = Name of string
    with member x.validate(s) = (Validator Name).validate s (Regex.IsMatch(s, ""))
    
    type PhoneNumber = PhoneNumber of string
    with member x.validate(s) = (Validator PhoneNumber).validate s (Regex.IsMatch(s, ""))