我可以在 F# 类型声明中对泛型参数设置 "is a Discriminated Union" 类型约束吗?

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

我正在为一些通用类型的通用输入创建一个类型系统:

// 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, ""))

您会看到在评论中,我注释掉了另一种类型。我希望使用注释中定义的类型来替换 Validatable 模块中 validate t value condition 函数的功能。

我需要更换什么???允许我说通用参数 't 是区分联合的案例标识符?

联合案例不是类型,它是产生类型值的函数。所以你可以这样写你的 Validator 类型:

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)

并像这样使用它:

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, ""))

真的很难说清楚你到底想做什么。但是如果你想写一些作用于多种类型值的函数,你通常可以使用接口并且不需要泛型类型约束。

例如,如果您想要使用正则表达式验证某些类型的值的通用方法,您可以定义一个 IValidable 接口:

type IValidable = 
  abstract Value : string
  abstract ValidationRegex : string

然后在所有要验证的类型中实现接口:

type Email = 
  | Email of string
  interface IValidable with
    member x.Value = let (Email v) = x in v
    member x.ValidationRegex = "[a-z]+@[a-z]+.[a-z]+"
  
type PhoneNumber = 
  | PhoneNumber of string
  interface IValidable with
    member x.Value = let (PhoneNumber v) = x in v
    member x.ValidationRegex = "[0-9]+"

现在您可以编写一个简单的 validate 函数并将其与您的所有值一起使用:

let validate (v:IValidable) =
  System.Text.RegularExpressions.Regex.IsMatch(v.Value, v.ValidationRegex)
  
validate (Email "no")
validate (Email "no@no.no")
validate (PhoneNumber "no")
validate (PhoneNumber "123")