-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathfunction_op.pl
127 lines (110 loc) · 4.17 KB
/
function_op.pl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
:- module(function_op, [
op(600, xfx, ~),
'~'/3,
op(652, xfx, ?~),
'?~'/3,
op(666, xfx, #~),
'#~'/3,
op(667, xfx, ?#~),
'?#~'/3
]).
:- use_module(function).
:- use_module(function/serde).
% Actual dark magic - https://swi-prolog.discourse.group/t/block-operator-for-matrix-notation/3506/4
%% Operator guide:
% ~ is the base symbol for getting values from functions
% # is the uuid variant of the operator, which works on uuids instead
% ? is the query symbol, and looks up functions with the key/value pair
%% ~(+Func, +Key, -Value)
% Gets the value with the given key from Func.
'$expand':function(~(_,_), _).
~(function(Uuid, _, _, _, _, _), uuid, Uuid).
~(function(_, Name, _, _, _, _), name, Name).
~(function(_, _, Generics, _, _, _), generics, Generics).
~(function(_, _, _, Inputs, _, _), inputs, Inputs).
~(function(_, _, _, _, Outputs, _), outputs, Outputs).
~(function(_, _, _, _, _, Docs), docs, Docs).
:- Uuid = function('01234', _, _, _, _, _) ~ uuid.
%% ?~(+Key, +Value, -Func)
% Find a function which satisfies the key/value constraint.
'$expand':function(?~(_,_), _).
?~(uuid, Value, Func) :- get_function(Value, Func).
?~(name, Value, Func) :- get_function(_, Func), fname(Func, Value).
?~(generics, Value, Func) :- get_function(_, Func), generics(Func, Value).
?~(inputs, Value, Func) :- get_function(_, Func), inputs(Func, Value).
?~(outputs, Value, Func) :- get_function(_, Func), outputs(Func, Value).
?~(docs, Value, Func) :- get_function(_, Func), docs(Func, Value).
:- Func = name ?~ "add".
%% #~(+Key, +Value, -Func)
% Gets the value with the given key from a function with the given UUID.
'$expand':function(#~(_,_), _).
#~(Func, Key, Value) :- func_field(Key, Func, Value).
%% ?#~(+Key, +Value, -Func)
% Find the UUID of a function which satisfies the key/value constraint.
'$expand':function(?#~(_,_), _).
?#~(Key, Value, Func) :- func_field(Key, Func, Value).
%% Example of the value that these operators provide:
% pretty_print_path([]) :- !.
% pretty_print_path([Func]) :-
% fname(Func, Name),
% write(Name), !.
% pretty_print_path([Func|Tail]) :-
% fname(Func, Name),
% format("~w -> ", [Name]), pretty_print_path(Tail), !.
%
% :- fname(Uuid, "add"), pretty_print_path(Uuid).
pretty_print_path([]) :- !.
pretty_print_path([Func]) :-
write(Func #~ name), !.
pretty_print_path([Func|Tail]) :-
format("~w -> ", [Func #~ name]), pretty_print_path(Tail), !.
:- pretty_print_path([name ?#~ "add"]).
%% When refactored, all of the arguments need to be moved, which is an enormous pain, and
% can cause subtle issues which tests might catch, but might also not catch.
% jsonify_fn(
% function(Uuid, Name, Generics, Inputs, Outputs, Docs),
% JSON
% ) :-
% var(JSON),
% jsonify_generics(Generics, JGenerics),
% jsonify_types(Inputs, JInputs),
% jsonify_types(Outputs, JOutputs),
% JSON = _{
% uuid:Uuid,
% name:Name,
% generics:JGenerics,
% inputs:JInputs,
% outputs:JOutputs,
% docs:Docs
% }, !.
%% This ~ notation allows us to forgo these positional arguments entirely, in favour of
% just getting the values we need. This makes it much easier to refactor, as we only
% need to change 1 source of truth, not many dozens scattered throughout the codebase
jsonify_fn(
Fn,
JSON
) :-
var(JSON),
jsonify_generics(Fn ~ generics, JGenerics),
jsonify_types(Fn ~ inputs, JInputs),
jsonify_types(Fn ~ outputs, JOutputs),
JSON = _{
uuid:Fn ~ uuid,
name:Fn ~ name,
generics:JGenerics,
inputs:JInputs,
outputs:JOutputs,
docs:Fn ~ docs
}.
%% Unfortunately, SWIPL does not currently support functions in calls,
% which would have been very nice to have, and would have allowed doing something like
% call(name ?~ "add", Fn) - which would make expressing these dynamic queries rather
% elegant.
% Alternative 1:
% custom_fn(Key, Value, Fn) :- func_field(Uuid, Fn, Value).
% call(custom_fn(name, "add"), Fn).
% Main issue with the alternative is that it's both more verbose, and obscures the intent
% of the operation.
% Alternative 2:
% Prolog's record library - this isn't ideal, because it is extremely verbose, and goes against
% the entire idea behind using this framework.