我正在自学F#(为了娱乐和利润!),尽管取得了长足的进步,但我却遇到了使用代数类型的绊脚石。以下是我编码的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的人都知道,应该将JSON对象的键/值对键入字符串,而不仅仅是任何JSON元素/值。有没有办法进一步限制Map的first type参数?这些当然不起作用:
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 >
谢谢。
答案 0 :(得分:4)
不可能从已区分的联合中引用另一个案例标识符。从Discriminated Unions,
语法
[属性]
类型[可访问性修饰符]类型名称=
case-identifier1 [of [fieldname1:] type1 [* [fieldname2:] type2 ...]
case-identifier2 [of [fieldname3:] type3 [* [fieldname4:] type4 ...]
[[成员列表]
这意味着每个案例标识符必须是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>
在这种情况下可以使用,但这并不是太可扩展/有限制性。