如何将 F# 联合类型的使用限制为特定选项

How Can I Restrict the Usage of an F# Union Type to A Particular Option

我正在自学 F# -- For Fun and Profit!--虽然我取得了一些进步,但我 运行 在使用代数类型方面遇到了绊脚石。下面是我编码的 JSON 类型,用于将任意 JSON 结构序列化为字符串。当然,我愿意接受对其设计和效率的主观评论,但我主要关注第 7 行:

type JSON =
    | JString of string
    | JNumber of decimal
    | JBool   of bool
    | JNull
    | JArray  of JSON list
    | JObject of Map< JSON, JSON >
with
    member this.Serialize  =
        let rec serialize ( element : JSON ) =
            match element with
            | JString str ->
                "\"" + str + "\""
            | JNumber num ->
                num.ToString()
            | JBool   bln -> 
                bln.ToString().ToLower()
            | JNull       ->
                "null"
            | JArray  ary ->
                "[" + String.concat "," ( List.map serialize ary ) + "]"
            | JObject obj -> 
                "{" + (
                    Map.fold (
                        fun state key value ->
                            state + ( match state with "" -> "" | _ -> "," )
                                  + ( serialize key ) 
                                  + ":" 
                                  + ( serialize value ) ) "" obj ) + "}"
        serialize( this )

任何熟悉 JSON 的人都知道 key/value 对 JSON 对象应该以字符串为键,而不仅仅是任何 JSON element/value .有没有办法进一步限制Map的第一个类型参数?这些当然不行:

type JSON =
    ... elided ...
    | JObject of Map< JSON.JString, JSON >

...

type JSON =
    ... elided ...
    | JObject of Map< JString, JSON >

...

type JSON =
    ... elided ...
    | JObject of Map< string, JSON >

谢谢。

无法从受歧视的联合中引用另一个案例标识符。来自 Discriminated Unions

Syntax

[ attributes ]

type [accessibility-modifier] type-name =

    | case-identifier1 [of [ fieldname1 : ] type1 [ * [ fieldname2 : ] type2 ...]

    | case-identifier2 [of [fieldname3 : ] type3 [ * [ fieldname4 : ] type4 ...]

    [ member-list ]

这意味着每个案例标识符必须是 of 某种类型。案例标识符本身不是类型。

实现相同功能的一种方法是将可区分的联合分解为多个可区分的联合:

type JSONKey =
| JString of string

type JSONValue =
| JString of string
| JNumber of decimal
| JBool of bool
| JNull
| JArray of JSONValue list
| JObject of Map<JSONKey, JSONValue>

然后将JSON定义为:

type JSON = Map<JSONKey, JSONValue>

那么,serialize需要改为let rec serialize ( element : JSONValue )serialize( this ) 需要更改为 serialize( JObject this )


正如@Ringil提到的,Map<string, JSON>会在这种情况下工作,但这不太extensible/restrictive。