Fixed defaulty representations of json_members//1 and json_elements//1
This commit is contained in:
@@ -63,8 +63,16 @@ json_value(boolean(true)) --> "true".
|
||||
json_value(boolean(false)) --> "false".
|
||||
json_value(null) --> "null".
|
||||
|
||||
/* Read Bob Kowalski's "Algorithm = Logic + Control":
|
||||
/* Note on variable instantiation checks (`var/1` and `nonvar/1`) used below and in Prolog in general.
|
||||
Instantiation checks should never ever be used to change the logic of your program! Instead, they are one of
|
||||
many tools to adjust the 'control' or 'search strategy' used by Prolog to execute the logic of your program.
|
||||
Control tweaks are used for the following:
|
||||
- Prevent instantiation errors.
|
||||
- Prevent nontermination.
|
||||
- Improve the time complexity of execution (e.g. from superexponential to linear).
|
||||
For a general overview of the idea, read Bob Kowalski's "Algorithm = Logic + Control":
|
||||
https://www.doc.ic.ac.uk/~rak/papers/algorithm%20=%20logic%20+%20control.pdf
|
||||
For an introduction to search strategies in Prolog, read: https://www.metalevel.at/prolog/sorting#searching
|
||||
This DCG definition does two things:
|
||||
1. Logic: Relate an association list to a JSON object serialized in a string.
|
||||
2. Control: Define the exact strategy by which we obtain an association list from a JSON string and vice versa.
|
||||
@@ -87,16 +95,42 @@ json_object(Assoc) -->
|
||||
; true
|
||||
) }.
|
||||
|
||||
json_members([Key-Value]) --> json_member(Key, Value).
|
||||
json_members([Key-Value | Pairs]) --> json_member(Key, Value), ",", json_members(Pairs).
|
||||
|
||||
/* Why have both `json_members//1` and `json_members_//2`? Wouldn't it be less confusing to have just
|
||||
`json_members//1`?
|
||||
In fact in the first version of the code there was just this simple definition of `json_members//1`:
|
||||
```
|
||||
json_members([Key-Value]) --> json_member(Key, Value).
|
||||
json_members([Key-Value | Pairs]) --> json_member(Key, Value), ",", json_members(Pairs).
|
||||
```
|
||||
The problem with this definition was that there's no way for Prolog to distinguish between the two DCG heads,
|
||||
because [Key-Value] unifies with [Key-Value|[]], which unifies with [Key-Value|Pairs].
|
||||
Therefore, such a representation is defaulty, and is actually misleading because when you look at it you think
|
||||
that a list with only one pair would apply to only the first definition, but it actually applies to both!
|
||||
For more info on clean vs defaulty representations, read: https://www.metalevel.at/prolog/data#clean
|
||||
The below definition, while longer, cleanly distinguishes member lists with just one value from member lists
|
||||
with two or more values.
|
||||
*/
|
||||
json_members([Pair|Pairs]) --> json_members_(Pairs, Pair).
|
||||
|
||||
json_members_([], Key-Value) --> json_member(Key, Value).
|
||||
json_members_([NextPair|Pairs], Key-Value) -->
|
||||
json_member(Key, Value),
|
||||
",",
|
||||
json_members_(Pairs, NextPair).
|
||||
|
||||
json_member(Key, Value) --> json_ws, json_string(Key), json_ws, ":", json_element(Value).
|
||||
|
||||
json_array([]) --> "[", json_ws, "]".
|
||||
json_array([Value|Values]) --> "[", json_elements([Value|Values]), "]".
|
||||
|
||||
json_elements([Value]) --> json_element(Value).
|
||||
json_elements([Value|Values]) --> json_element(Value), ",", json_elements(Values).
|
||||
json_elements([Value|Values]) --> json_elements_(Values, Value).
|
||||
|
||||
json_elements_([], Value) --> json_element(Value).
|
||||
json_elements_([NextValue|Values], Value) -->
|
||||
json_element(Value),
|
||||
",",
|
||||
json_elements_(Values, NextValue).
|
||||
|
||||
json_element(Value) --> json_ws, json_value(Value), json_ws.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user