Commit 035b9839 authored by Siebers, Michael's avatar Siebers, Michael
Browse files

Externalized helper predicates, simplified predicate names.

Moved helper predicates for handle_irrelevant_file and handle_explain into included files.

Renamed explain-helper predicates (ommitting initial explain_).
parent bd014ffc
%! do_handle_explain(++Parameters:dict, --Result:list, --More:atom) is det
%
% Generates textual explanations for the irrelevance of a file. Parameters may
% contain the following keys:
% - abs_path (required, atom): The absolute path of the file which's
% irrelevance shall be explained
% - limit (optional, integer, default: 5): The maximal number of explanations
% to create.
%
% The Result is a list of explanation(AbsPath,Message,Details,References) terms.
% The list might be empty. In these terms AbsPath is bound to the absolute path
% of the item which's irrelevance is explained. Message is a list which's
% elements are either strings or a term reference(DescribingString,ReferenceID).
% Details is a list of Message-type lists. Finally, References is a list of
% ref(ReferenceID,ListOfEntity) terms where each Entity is given as term
% entity(NameOfEntity,NameOfProperty). NameOfEntity and NameOfProperty are
% either atom or a string. If there are more explanations available than there
% are in Result, More is bound to 'true'. Otherwise, More is bound to 'false'.
%
% @throws not_irrelevant(AbsPath) If the provided absolut path denotes a file
% which is not irrelevant. Then, by definition, no explanation may be
% created.
% @error type_error(dict, Parameters) if Parameters is no dict
% @error existence_error(key, Key, Parameters) if Parameters does not contain
% the required key Key
% @error instantiation_error if any used dict value is unbound
% @error uninstantiation_error(Result|More) if Result or More are bound
% @error type_error(Type, KeyName) if the dict value under KeyName is of a wrong
% type, expected Type
% @error unknown_key(KeyName, Dict) if the dict contains an unknown key KeyName
% @error implementation_error(Cause) if generating the explanation itself throws
% an exception
do_handle_explain(Parameters, Result, More) :-
must_be(dict, Parameters), % might throw type_error(dict, Parameters)
% or instantiation_error
AbsPath = Parameters.abs_path, % extract absolute path; or
% existence_error(key, abs_path, Parameters)
must_be(atom, AbsPath), % might throw instantiation_error or
% type_error(atom, AbsPath)
once(( get_dict(limit, Parameters, Limit)
; Limit = 5
)),
must_be(integer, Limit), % might throw instantiation_error or
% type_error(int, Limit)
( get_dict(Key,Parameters,_),
\+ member(Key, [abs_path, limit])
-> throw(error(unknown_key(Key,Parameters),_))
; true
),
( var(Result)
-> true
; uninstantiation_error(Result)
),
( var(More)
-> true
; uninstantiation_error(More)
),
catch(
do_handle_explain_(AbsPath, Limit, Result, More),
E,
( E=fails(irrelevant(Path))
-> throw(not_irrelevant(Path))
; throw(error(implementation_error(E),_))
)
).
do_handle_explain_(AbsPath, Limit, Result, More) :-
findnsols(Limit,
explanation(AbsPath,Message, Details, References),
explanations:explain(irrelevant(AbsPath), Message, Details, References),
Result),
deterministic(LastAnswer),
( LastAnswer==true % other responses are not explicitly documented
-> More=false
; More=true
), !.
%! exception_to_handler_exception(+ExplainException, --HandlerException)
%
% Translates exceptions from do_handle_explain/3 to bad_request/1 and
% server_error/2 terms thrown by handle_explain/1.
%
% @see do_handle_explain/3 for the actual errors thrown
% @see handle_explain/1 for the resulting handler errors
exception_to_handler_exception(not_irrelevant(AbsPath),
bad_request(not_irrelevant(AbsPath))).
exception_to_handler_exception(
error(existence_error(key, Key, _Parameters),_),
bad_request(missing_key(Key))).
exception_to_handler_exception(
error(type_error(dict, Dict), Context),
server_error(type_error(dict, Dict), Context)) :- is_dict(Dict).
exception_to_handler_exception(
error(type_error(Type, KeyName),_),
bad_request(wrong_type(KeyName, Type))).
exception_to_handler_exception(
error(unknown_key(KeyName, _Parameters),_),
bad_request(unknown_key(KeyName))).
exception_to_handler_exception(
error(instantiation_error,Context),
server_error(instantiation_error,Context)).
exception_to_handler_exception(
error(uninstantiation_error(Argument),Context),
server_error(uninstantiation_error(Argument),Context)).
exception_to_handler_exception(
error(implementation_error(Cause),Context),
server_error(implementation_error(Cause),Context)).
exception_to_handler_exception(
error(E,Context),
server_error(unexpected_error(E),Context)).
%! explanation_to_json(++Explanation, --JSON) is det
%
% Transforms an explanation to a term suitable to be transformed into a JSON
% string. The sub-transformations of the explanation message and details are
% carried out by message_to_json/2. The references are transformed by
% reference_to_json/2.
%
% @see do_handle_explain/3 for the expected format of Explanation
% @error instantiation_error if Explanation or any part of it is unbound
% @error type_error(Type,Part) if Explanation or any Part of it has not the
% expected type
% @error uninstantiation_error(Part) if (part of) JSON is instantiated and
% cannot be unified with the correct answer.
explanation_to_json(explanation(AbsPath,Message,Details,References),
JSONDict) :-
text_to_string(AbsPath,AbsPathStr),
message_to_json(Message, MessageJSON),
must_be(list,Details),
maplist(message_to_json,Details, DetailsJSON),
reference_to_json(References, ReferencesJSON),
( JSONDict = explanation{
abs_path: AbsPathStr,
reasoning: MessageJSON,
reasoning_details: DetailsJSON,
references: ReferencesJSON
}
-> true
; uninstantiation_error(JSONDict)
), !.
explanation_to_json(Explanation,_JSON) :-
type_error(explanation(_,_,_,_),Explanation).
/*
reference_to_json_(ref(RefId,ListOfEntities), JSONDict) :-
must_be(integer,RefId),
must_be(list, ListOfEntities),
maplist(entity_to_json, ListOfEntities, JSONEntities),
( JSONDict = reference{id: RefId, referenced_entities: JSONEntities}
-> true
; uninstantiation_error(JSONDict)
),
!.
reference_to_json_(Reference,_JSON) :-
type_error(ref(_,_), Reference).
*/
%! message_to_json(++Message:list, --JSON) is det
%
% Transforms an explanation (detailed) message to a term suitable to be
% transformed into a JSON string.
%
% @see do_handle_explain/3 for the expected format of Message
% @error instantiation_error if Message or any part of it is unbound
% @error type_error(Type,Part) if Message or any Part of it has not the expected
% type
% @error uninstantiation_error(Part) if (part of) JSON is instantiated and
% cannot be unified with the correct answer.
message_to_json(Message, JSON) :-
must_be(list, Message),
maplist(message_to_json_, Message, JSONTrue),
( JSON=JSONTrue
-> true
; uninstantiation_error(JSON)
).
message_to_json_(reference(M,Ref), JSON) :-
text_to_string(M, MStr),
must_be(integer, Ref),
dict_create(J, message, [text=MStr, ref_id=Ref]),
( JSON = J
-> true
; uninstantiation_error(JSON)
),
!.
message_to_json_(M, JSON) :-
text_to_string(M, MStr),
dict_create(J, message, [text=MStr]),
( JSON = J
-> true
; uninstantiation_error(JSON)
).
%! reference_to_json(++References:list, --JSON) is det
%
% Transforms explanation references to a term suitable to be
% transformed into a JSON string.
%
% @see do_handle_explain/3 for the expected format of References
% @error instantiation_error if References or any part of it is unbound
% @error type_error(Type,Part) if References or any Part of it has not the expected type
% @error domain_error(non_empty_list,[]) if any reference has an empty list of entities.
% @error uninstantiation_error(Part) if (part of) JSON is instantiated and cannot be
% unified with the correct answer.
reference_to_json(References, JSON) :-
must_be(list, References),
maplist(reference_to_json_, References, JSONTrue),
( JSON=JSONTrue
-> true
; uninstantiation_error(JSON)
).
reference_to_json_(ref(RefId,ListOfEntities), JSONDict) :-
must_be(integer,RefId),
must_be(list, ListOfEntities),
maplist(entity_to_json, ListOfEntities, JSONEntities),
( JSONDict = reference{id: RefId, referenced_entities: JSONEntities}
-> true
; uninstantiation_error(JSONDict)
),
!.
reference_to_json_(Reference,_JSON) :-
type_error(ref(_,_), Reference).
%! entity_to_json(entity(++Path, ++Property), -JSON:dict) is det
entity_to_json(entity(Path, Property),
entity{abs_path: PathStr, property: PropertyStr}) :-
text_to_string(Path, PathStr),
text_to_string(Property, PropertyStr),
!.
entity_to_json(Entity,JSON) :-
( var(JSON)
-> type_error(entity(_,_), Entity)
; uninstantiation_error(JSON)
).
%! do_handle_irrelevant_file(+Parameters:dict, -AbsPath:atom) is semidet.
%
% True if the item associated with the absolute path given in Parameters
% is irrelevant. The absolute path is accessed using the dict key abs_path.
% An error is thrown if the dict does not contain this key, if its value
% is not an atom or if the dict contains any other key.
%
% @error type_error(dict, Dict) if Dict is no dict
% @error existence_error(key, abs_path, Dict) if Dict does not contain the key abs_path
% @error instantiation_error if the dict value under abs_path is unbound
% @error type_error(atom, AbsPath) if the dict value under abs_path is not an atom
% @error unknown_key(KeyName, Dict) if the dict contains any KeyName which is not abs_path
% @error implementation_error(Cause) if checking the irrelevance itself throws an error (Cause)
do_handle_irrelevant_file(Parameters,AbsPath) :-
must_be(dict,Parameters), % might throw type_error(dict, Parameters)
% or instantiation_error
AbsPath = Parameters.abs_path,
must_be(atom,AbsPath),
( (get_dict(Key,Parameters,_), Key\= abs_path )
-> throw(error(unknown_key(Key,Parameters),_))
; true
),
catch(user:irrelevant(AbsPath),
E,
throw(error(implementation_error(E),_))
).
......@@ -260,7 +260,7 @@ test(explain_message_to_json_happy_simple, % simple
[109,101,115,115,97,103,101] ])),
true([_{text: "message"}] = ActualJSON)
]) :-
web_api:explain_message_to_json([Content], ActualJSON).
web_api:message_to_json([Content], ActualJSON).
test(explain_message_to_json_happy_reference, % reference
[
forall(member(Content,["message", message,
......@@ -268,7 +268,7 @@ test(explain_message_to_json_happy_reference, % reference
[109,101,115,115,97,103,101] ])),
true([_{text: "message", ref_id: 0}] = ActualJSON)
]) :-
web_api:explain_message_to_json(
web_api:message_to_json(
[reference(Content,0)], ActualJSON).
test(explain_message_to_json_happy_complex, % complex
[
......@@ -276,7 +276,7 @@ test(explain_message_to_json_happy_complex, % complex
= ActualJSON)
]) :-
Message = ["this", reference("is a", 0), "test"],
web_api:explain_message_to_json(Message, ActualJSON).
web_api:message_to_json(Message, ActualJSON).
% explain_message_to_json(Message, JSON) throws instantiation_error
% if Message or any part of it is unbound.
......@@ -291,7 +291,7 @@ test(explain_message_to_json_instantiation,
])),
error(instantiation_error)
]) :-
web_api:explain_message_to_json(Message, _ActualJSON).
web_api:message_to_json(Message, _ActualJSON).
% explain_message_to_json(Message, JSON) throws type_error(Type, Part)
......@@ -301,27 +301,27 @@ test(explain_message_to_json_message_type_list, % message no list
forall(member(Message, [1, "test", test, 7.3, some(term)])),
error(type_error(list,Message))
]) :-
web_api:explain_message_to_json(Message, _ActualJSON).
web_api:message_to_json(Message, _ActualJSON).
test(explain_message_to_json_message_type_text, % text no text
[
forall(type_not_text(OtherTypeValue)),
error(type_error(text,OtherTypeValue))
]) :-
web_api:explain_message_to_json([OtherTypeValue], _ActualJSON).
web_api:message_to_json([OtherTypeValue], _ActualJSON).
test(explain_message_to_json_message_type_referencetext, % reference text no text
[
forall(type_not_text(OtherTypeValue)),
error(type_error(text,OtherTypeValue))
]) :-
web_api:explain_message_to_json([reference(OtherTypeValue,0)], _ActualJSON).
web_api:message_to_json([reference(OtherTypeValue,0)], _ActualJSON).
test(explain_message_to_json_message_type_referenceid, % reference id no integer
[
forall(type_not_integer(OtherTypeValue)),
error(type_error(integer,OtherTypeValue))
]) :-
web_api:explain_message_to_json([reference("text",OtherTypeValue)],
web_api:message_to_json([reference("text",OtherTypeValue)],
_ActualJSON).
% explain_message_to_json(Message, JSON) throws uninstantiation_error(JSON)
......@@ -331,7 +331,7 @@ test(explain_message_to_json_uninstantiaton,
forall(member(BoundJSON,[_{un: expected}, tag{text: "some text"}])),
error(uninstantiation_error(BoundJSON))
]) :-
web_api:explain_message_to_json(["some text"], BoundJSON).
web_api:message_to_json(["some text"], BoundJSON).
/********************************
......@@ -358,7 +358,7 @@ test(explain_reference_to_json_happy_simple,
}])
]) :-
References = [ref(RefId,[entity(Path,Type)])],
web_api:explain_reference_to_json(References, ActualJSON).
web_api:reference_to_json(References, ActualJSON).
test(explain_reference_to_json_happy_multiple_entities,
......@@ -376,7 +376,7 @@ test(explain_reference_to_json_happy_multiple_entities,
]) :-
References = [ref(0,[entity("some_path","name"),
entity("another/path","some property")])],
web_api:explain_reference_to_json(References, ActualJSON).
web_api:reference_to_json(References, ActualJSON).
test(explain_reference_to_json_happy_multiple_references,
[
true(ActualJSON = [_{
......@@ -396,7 +396,7 @@ test(explain_reference_to_json_happy_multiple_references,
]) :-
References = [ref(0,[entity("some_path","name")]),
ref(1,[entity("another/path","some property")])],
web_api:explain_reference_to_json(References, ActualJSON).
web_api:reference_to_json(References, ActualJSON).
% explain_reference_to_json(References, JSON) throws instantiation_error
% if References or any part of it is unbound.
......@@ -415,7 +415,7 @@ test(explain_reference_to_json_instantiation,
])),
error(instantiation_error)
]) :-
web_api:explain_reference_to_json(References, _ActualJSON).
web_api:reference_to_json(References, _ActualJSON).
% explain_reference_to_json(References, JSON) throws type_error(Type, Part)
......@@ -425,7 +425,7 @@ test(explain_reference_to_json_message_type_list, % References no list
forall(type_not_list(References)),
error(type_error(list,References))
]) :-
web_api:explain_reference_to_json(References, _ActualJSON).
web_api:reference_to_json(References, _ActualJSON).
test(explain_reference_to_json_message_type_reference, % reference no ref(_,_)
[
......@@ -433,7 +433,7 @@ test(explain_reference_to_json_message_type_reference, % reference no ref(_,_)
error(type_error(ref(_,_),OtherTypeValue))
]) :-
References = [OtherTypeValue],
web_api:explain_reference_to_json(References, _ActualJSON).
web_api:reference_to_json(References, _ActualJSON).
test(explain_reference_to_json_message_type_id, % id no integer
[
......@@ -441,7 +441,7 @@ test(explain_reference_to_json_message_type_id, % id no integer
error(type_error(integer,OtherTypeValue))
]) :-
References = [ref(OtherTypeValue,[entity("some_path","name")])],
web_api:explain_reference_to_json(References, _ActualJSON).
web_api:reference_to_json(References, _ActualJSON).
test(explain_reference_to_json_message_type_entities, % entities no list
[
......@@ -449,21 +449,21 @@ test(explain_reference_to_json_message_type_entities, % entities no list
error(type_error(list,OtherTypeValue))
]) :-
References = [ref(0,OtherTypeValue)],
web_api:explain_reference_to_json(References, _ActualJSON).
web_api:reference_to_json(References, _ActualJSON).
test(explain_reference_to_json_message_type_entity, % entity is not entity(_,_)
[
forall(type_not_term(entity/2,OtherTypeValue)),
error(type_error(entity(_,_),OtherTypeValue))
]) :-
References = [ref(0,[OtherTypeValue])],
web_api:explain_reference_to_json(References, _ActualJSON).
web_api:reference_to_json(References, _ActualJSON).
test(explain_reference_to_json_message_type_entitypath, % entity path not text
[
forall(type_not_text(OtherTypeValue)),
error(type_error(text,OtherTypeValue))
]) :-
References = [ref(0,[entity(OtherTypeValue,"name")])],
web_api:explain_reference_to_json(References, _ActualJSON).
web_api:reference_to_json(References, _ActualJSON).
test(explain_reference_to_json_message_type_entityproperty, % entity property not text
[
......@@ -471,7 +471,7 @@ test(explain_reference_to_json_message_type_entityproperty, % entity property no
error(type_error(text,OtherTypeValue))
]) :-
References = [ref(0,[entity("some_path",OtherTypeValue)])],
web_api:explain_reference_to_json(References, _ActualJSON).
web_api:reference_to_json(References, _ActualJSON).
% explain_reference_to_json(References, JSON) throws uninstantiation_error(JSON)
% if JSON is bound and cannot be unified with result
......@@ -492,7 +492,7 @@ test(explain_reference_to_json_uninstantiaton,
error(uninstantiation_error(BoundJSON))
]) :-
References = [ref(0,[entity("some_path","name")])],
web_api:explain_reference_to_json(References, BoundJSON).
web_api:reference_to_json(References, BoundJSON).
......@@ -541,7 +541,7 @@ test(explain_explanation_to_json_happy,
]}
]
},
web_api:explain_explanation_to_json(
web_api:explanation_to_json(
explanation(AbsPath,Message,Details,References), ActualJSON).
......@@ -549,7 +549,7 @@ test(explain_explanation_to_json_happy,
% instantiation_error if Explanation is unbound.
test(explain_explanation_to_json_instantiation,
[ error(instantiation_error)]) :-
web_api:explain_explanation_to_json(_Explanation, _ActualJSON).
web_api:explanation_to_json(_Explanation, _ActualJSON).
% explain_explanation_to_json(Explanation, JSON) throws
% type_error(explanation(_,_,_,_),Explanation) if Explanation is not of the
......@@ -558,7 +558,7 @@ test(explain_explanation_to_json_type,
[
forall(type_not_term(explanation/4,OtherTypeValue)),
error(type_error(explanation(_,_,_,_),OtherTypeValue)) ]) :-
web_api:explain_explanation_to_json(OtherTypeValue, _ActualJSON).
web_api:explanation_to_json(OtherTypeValue, _ActualJSON).
% explain_explanation_to_json(Explanation, JSON) throws
......@@ -575,7 +575,7 @@ test(explain_explanation_to_json_abspath_instantiation,
References = [ ref(0,[entity("another/path","name")]),
ref(1,[entity("yet/some/other/path", "property")])
],
web_api:explain_explanation_to_json(
web_api:explanation_to_json(
explanation(_AbsPath,Message,Details,References), _ActualJSON).
......@@ -596,7 +596,7 @@ test(explain_explanation_to_json_abspath_type,
References = [ ref(0,[entity("another/path","name")]),
ref(1,[entity("yet/some/other/path", "property")])
],
web_api:explain_explanation_to_json(
web_api:explanation_to_json(
explanation(OtherTypeValue,Message,Details,References), _ActualJSON).
......@@ -612,7 +612,7 @@ test(explain_explanation_to_json_details_type,
References = [ ref(0,[entity("another/path","name")]),
ref(1,[entity("yet/some/other/path", "property")])
],
web_api:explain_explanation_to_json(
web_api:explanation_to_json(
explanation(AbsPath,Message,OtherTypeValue,References), _ActualJSON).
......@@ -732,7 +732,7 @@ test(explain_explanation_to_json_bound,
References = [ ref(0,[entity("another/path","name")]),
ref(1,[entity("yet/some/other/path", "property")])
],
web_api:explain_explanation_to_json(
web_api:explanation_to_json(
explanation(AbsPath,Message,Details,References), BoundJSON).
......
:- module(web_api, [server_main/0]).
:- include(include/common/api_response).
:- include('include/irrelevance.pl').
:- include('include/explanations.pl').
:- use_module(library(http/thread_httpd)).
:- use_module(library(http/http_error)).
......@@ -86,9 +88,11 @@ server_opts(Opts) :-
:- http_handler(root(query), handle_query, []).
/* Closures for handlers */
%! handle_explain(++Request) is det
%! handle_irrelevant_file(++Request) is det
%
% Handles HTTP request to explain the irrelevance of a single absolute path.
% Handles HTTP request querrying the irrelevance of a single absolute path.
%
% @throws bad_request(not_irrelevant(AbsPath)) If the absolut path provided in
% the request's payload denotes a
......@@ -97,276 +101,13 @@ server_opts(Opts) :-
% explanation may be created.
% @throws bad_request(missing_key(Key)) If a required parameter was missing
% in the JSON payload.
% @throws bad_request(wrong_type(KeyName, Type))) If the value associated with
% the key KeyName is not of the
% correct type.
% @throws bad_request(wrong_type(abs_path, atom, Value))) If the Value
% associated with the key
% abs_path is not an atom.
% @throws bad_request(unknown_key(KeyName)) The unknown key KeyName was supplied
% in the request's payload.
% @throw server_error(E,Context) if an exception occured which is not the user's
% error, but our own.
handle_explain(Req) :-
http_read_json(Req, Parameters, [json_object(dict), value_string_as(atom)]),
catch(do_handle_explain(Parameters, Explanations, More),
E,
(
explain_exception_to_handler_exception(E, HandlerException),
throw(HandlerException)
)
),
catch(maplist(explain_explanation_to_json, Explanations, ExplanationsJSON),
error(Err,Context),
throw(server_error(Err,Context))),
reply_json_dict(reply{explanations: ExplanationsJSON,
further_explanations: @(More)},
[true(@(true)), false(@(false))]). % in only default for
% reply_json/1 and
% reply_json/2.
%! explain_exception_to_handler_exception(+ExplainException, --HandlerException)
%
% Translates exceptions from do_handle_explain/3 to bad_request/1 and
% server_error/2 terms thrown by handle_explain/1.
%
% @see do_handle_explain/3 for the actual errors thrown
% @see handle_explain/1 for the resulting handler errors
explain_exception_to_handler_exception(not_irrelevant(AbsPath),
bad_request(not_irrelevant(AbsPath))).
explain_exception_to_handler_exception(
error(existence_error(key, Key, _Parameters),_),
bad_request(missing_key(Key))).
explain_exception_to_handler_exception(
error(type_error(dict, Dict), Context),
server_error(type_error(dict, Dict), Context)) :- is_dict(Dict).
explain_exception_to_handler_exception(
error(type_error(Type, KeyName),_),
bad_request(wrong_type(KeyName, Type))).
explain_exception_to_handler_exception(
error(unknown_key(KeyName, _Parameters),_),
bad_request(unknown_key(KeyName))).
explain_exception_to_handler_exception(
error(instantiation_error,Context),
server_error(instantiation_error,Context)).
explain_exception_to_handler_exception(
error(uninstantiation_error(Argument),Context),
server_error(uninstantiation_error(Argument),Context)).
explain_exception_to_handler_exception(
error(implementation_error(Cause),Context),
server_error(implementation_error(Cause),Context)).
explain_exception_to_handler_exception(
error(E,Context),
server_error(unexpected_error(E),Context)).
%! explain_explanation_to_json(++Explanation, --JSON) is det
%
% Transforms an explanation to a term suitable to be transformed into a JSON
% string. The sub-transformations of the explanation message and details are
% carried out by explain_message_to_json/2. The references are transformed by
% explain_reference_to_json/2.
%
% @see do_handle_explain/3 for the expected format of Explanation
% @error instantiation_error if Explanation or any part of it is unbound
% @error type_error(Type,Part) if Explanation or any Part of it has not the
% expected type
% @error uninstantiation_error(Part) if (part of) JSON is instantiated and
% cannot be unified with the correct answer.
explain_explanation_to_json(explanation(AbsPath,Message,Details,References),
JSONDict) :-
text_to_string(AbsPath,AbsPathStr),
explain_message_to_json(Message, MessageJSON),
must_be(list,Details),
maplist(explain_message_to_json,Details, DetailsJSON),
explain_reference_to_json(References, ReferencesJSON),
( JSONDict = explanation{
abs_path: AbsPathStr,
reasoning: MessageJSON,
reasoning_details: DetailsJSON,
references: ReferencesJSON
}
-> true
; uninstantiation_error(JSONDict)
), !.
explain_explanation_to_json(Explanation,_JSON) :-
type_error(explanation(_,_,_,_),Explanation).
/*
explain_reference_to_json_(ref(RefId,ListOfEntities), JSONDict) :-
must_be(integer,RefId),
must_be(list, ListOfEntities),
maplist(explain_entity_to_json, ListOfEntities, JSONEntities),
( JSONDict = reference{id: RefId, referenced_entities: JSONEntities}
-> true
; uninstantiation_error(JSONDict)
),