在不使用类的情况下表示受限类型

AK_*_*AK_ 6 .net f# f#-4.0

在F#中,我可以在不定义类的情况下表示受限类型吗?假设我想表示第一个除以第二个数字的所有数字对.

在C#我能做到:

class PairDivides
{
    int a {get;private set;}
    int b {get;private set;}

    PairDivides(int first, int second)
    {
        if (a % b != 0)
        {
            throw new Exception();
        }

        this.a = first;
        this.b = second;
    }
}
Run Code Online (Sandbox Code Playgroud)

现在创建一个不分割的PairDivides实例b是不可能的a......

这可以在F#中仅使用功能构造(记录,区分联合,可能是活动模式等)来完成吗?

我希望能够创建和接收类似这些对的东西,确保它们构造正确.

Ree*_*sey 6

您可以通过将类型设为私有来完成此操作.唯一的缺点是您需要提供访问数据的功能:

module Pairs =
    type PairDivides = private { a: int; b: int }

    let createPairDivides a b =
        match a % b with
        | 0 -> Some { PairDivides.a = a ; b = b }
        | _ -> None

    let print div =
        printfn "{%d %d}" div.a div.b

    let tryPrint div =
        match div with
        | Some a -> print a
        | None -> printfn "None"

let a = Pairs.createPairDivides 2 2

let b = a.Value

// This is inaccessible: b.a

Pairs.createPairDivides 2 2 |> Pairs.tryPrint
Pairs.createPairDivides 2 3 |> Pairs.tryPrint
Run Code Online (Sandbox Code Playgroud)

通过提供创建对的函数,以及在必要时使用或从中提取的函数,您可以完全消除创建无效对的能力(您将None返回而不是使用坏对)而不使用异常.

缺点是您需要提供从成对中提取值的机制,因为在当前模块外部使用时,类型现在无法访问.

话虽如此,通过课程这样做也没有错.如果您愿意,可以通过创建类来获得相同级别的强制执行:

type PairDivides private (a,b) =
    member __.A = a
    member __.B = b

    static member Create a b =
        match a % b with
        | 0 -> Some(PairDivides(a,b))
        | _ -> None


PairDivides.Create 2 2 |> printfn "%A"
PairDivides.Create 2 3 |> printfn "%A"
Run Code Online (Sandbox Code Playgroud)