Skip to content

Commit 40bd887

Browse files
committed
Simplify indented output rendering by using stateful output routines.
This replaces the "pure functional" output style, which took great pains to build a nested list structure which was only sent to the output device at the very end. This required passing around a hidden "tab" parameter in a monadic fashion, the use of a "next" continuation parameter, and semicolons to ensure that the tail of a structure was threaded in properly. Now when you call routines like say, nl, put, and indent, the effects of those calls are going directly into a file handle or other target as they run. You are no longer building up a lazy nested piece of data which is _later_ sent to an output target. All the complexity of the "pure" method vanishes like a bad dream. This drastically simplifies the code supporting the b15 and index_C tests. I also use the technique to great advantage in the demo.ray project: https://github.com/chkoreff/demo.ray This illustrates a principle which I title: "Pure Functions Sometimes Considered Harmful" The pure functional style still has its place in certain aspects of a program, but for input and output it's easier just to cut to the chase and deal with the machine on its own terms. In those cases, "var" is your friend -- and it can even create more opportunities for implementing routines directly in C, as I did with the parsing primitives in stream.c in the Fexl project. I am also finding the var technique useful for "throughput". In one major project I was passing a single immutable state parameter around everywhere, and I greatly simplified the code by keeping that state in a handful of vars. The good thing about "var" is that it is _controlled_ mutability, not the haphazard and dangerous kind where you modify nested data structures in place. In Fexl the data structures are still immutable, but you use a var to _point_ to a new version when you make a state change. I think this strikes the right balance between pure functions and routines with side effects.
1 parent 582c71b commit 40bd887

File tree

8 files changed

+122
-142
lines changed

8 files changed

+122
-142
lines changed

src/VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
27.11.0+1
1+
27.12.0

src/out/b15

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ get: GOOD
208208
{"bccc" "5233" "9999"}
209209
{"bcccxyz" "3364" void}
210210
]
211-
steps 1645232 bytes 577120
211+
steps 1644462 bytes 577120
212212
~
213213
\err=
214214
~

src/test/lib/b15/context.fxl

Lines changed: 13 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -15,29 +15,27 @@ cx_render_list;
1515
is_defined val (fexl_quote val) "void"
1616
)
1717

18-
\render_pairs=
18+
\say_pairs=
1919
(
20-
render_list
21-
(\row\next
20+
say_list
21+
(\row
2222
row \key\val
23-
say ["{" (fexl_quote key) " " (render_val val) "}"];
24-
next
23+
say ["{" (fexl_quote key) " " (render_val val) "}"]
2524
)
2625
)
2726

28-
\render_errors=
27+
\say_errors=
2928
(
30-
render_list
31-
(\row\next
29+
say_list
30+
(\row
3231
row \key\val\val2
33-
say ["{" (fexl_quote key)" " (render_val val)" "(render_val val2)"}"];
34-
next
32+
say ["{" (fexl_quote key)" " (render_val val)" "(render_val val2)"}"]
3533
)
3634
)
3735

3836
\form
39-
def "render_pairs" render_pairs;
40-
def "render_errors" render_errors;
37+
def "say_pairs" say_pairs;
38+
def "say_errors" say_errors;
4139
form
4240
)
4341

@@ -50,29 +48,10 @@ form
5048
use "lib/index/render.fxl"
5149
)
5250

53-
\cx_output=
54-
(
55-
value;
56-
std;
57-
cx_render;
58-
cx_render_index;
51+
\cx_output==
52+
(\form
5953
cx_render_local;
60-
def "_put" put;
61-
\;
62-
63-
\put_render=
64-
(\render\tab\x
65-
_put (render x end tab)
66-
)
67-
68-
\say_pairs=(put_render render_pairs "")
69-
\say_index=(put_render render_index "")
70-
\say_errors=(put_render render_errors "")
71-
72-
\form
73-
def "say_pairs" say_pairs;
74-
def "say_index" say_index;
75-
def "say_errors" say_errors;
54+
cx_render_index;
7655
form
7756
)
7857

src/test/lib/index/render.fxl

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,21 @@
11
#
2-
\render_index=
3-
(@\render_index
4-
render_list
5-
(\row\next
2+
\say_index=
3+
(@\say_index
4+
say_list
5+
(\row
66
row \is_leaf\key\val
77
is_leaf
88
(
9-
say ["{T "(fexl_quote key) " " (fexl_quote val) "}"];
10-
next
9+
say ["{T "(fexl_quote key) " " (fexl_quote val) "}"]
1110
)
1211
(
13-
say ["{F "(fexl_quote key) ];
14-
indent
15-
(
16-
render_index val;
17-
end
18-
);
19-
say "}";
20-
next
12+
say ["{F "(fexl_quote key)]
13+
indent (say_index val)
14+
say "}"
2115
)
2216
)
2317
)
2418

2519
\form
26-
def "render_index" render_index;
20+
def "say_index" say_index;
2721
form

src/test/lib/index_C/context.fxl

Lines changed: 35 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -12,106 +12,79 @@ cx_render_C;
1212

1313
# Process all the rows in an index.
1414
\do_rows=
15-
(@\do_rows \checked_bound\pos\rows\next
16-
rows next \row\rows
15+
(@\do_rows \checked_bound\pos\rows
16+
rows () \row\rows
1717
row \is_leaf\key\val
1818

1919
\check_ch==
20-
(\next
20+
(
2121
\ch=(slice key pos 1)
22-
checked_bound () (say ["if (n <= "(num_str pos)") return 0;"]);
23-
say ["if (x["(num_str pos)"] == '"ch"')"];
24-
next
22+
checked_bound () (say ["if (n <= "(num_str pos)") return 0;"])
23+
say ["if (x["(num_str pos)"] == '"ch"')"]
2524
)
2625

2726
\len=(length key)
2827

2928
is_leaf
3029
(
3130
\do_strncmp==
32-
(\next
31+
(
3332
say ["if (n == "(num_str len)" && strncmp(x,"
34-
(fexl_quote key)",n) == 0)"];
35-
indent
36-
(
37-
say ["return "(fexl_quote val)";"];
38-
end
39-
);
40-
next
33+
(fexl_quote key)",n) == 0)"]
34+
indent (say ["return "(fexl_quote val)";"])
4135
)
4236

4337
lt pos len
4438
(
45-
check_ch;
39+
check_ch
4640
scope
47-
(\next
48-
do_strncmp;
49-
say "return 0;";
50-
next
51-
);
52-
do_rows T pos rows;
53-
next
41+
(
42+
do_strncmp
43+
say "return 0;"
44+
)
45+
do_rows T pos rows
5446
);
5547
eq len 0
5648
(
5749
# Optimize null key.
58-
say ["if (n == "(num_str pos)")"];
59-
indent
60-
(
61-
say ["return "(fexl_quote val)";"];
62-
end
63-
);
64-
do_rows T pos rows;
65-
next
50+
say ["if (n == "(num_str pos)")"]
51+
indent (say ["return "(fexl_quote val)";"])
52+
do_rows T pos rows
6653
)
6754
(
68-
do_strncmp;
69-
do_rows checked_bound pos rows;
70-
next
55+
do_strncmp
56+
do_rows checked_bound pos rows
7157
)
7258
)
7359
(
74-
check_ch;
60+
check_ch
7561
scope
76-
(\next
77-
do_rows F len val;
78-
say "return 0;";
79-
next
80-
);
81-
do_rows T pos rows;
82-
next
62+
(
63+
do_rows F len val
64+
say "return 0;"
65+
)
66+
do_rows T pos rows
8367
)
8468
)
8569

86-
\render_index==
70+
# (compile_index index)
71+
# Return C code which implements the index.
72+
\compile_index==
8773
(\index
88-
say "const char *lookup(const char *x, unsigned long n)";
74+
say "const char *lookup(const char *x, unsigned long n)"
8975
scope
90-
(\next
91-
(\return
76+
(
9277
index
9378
(
9479
# Special case for empty index.
95-
say "(void)x;";
96-
say "(void)n;";
97-
return
80+
say "(void)x;"
81+
say "(void)n;"
9882
)
9983
(\_\_
100-
do_rows F 0 index;
101-
return
84+
do_rows F 0 index
10285
)
103-
);
104-
say "return 0;";
105-
next
106-
);
107-
end
108-
)
109-
110-
# (compile_index index)
111-
# Return C code which implements the index.
112-
\compile_index==
113-
(\index
114-
render_index index ""
86+
say "return 0;"
87+
)
11588
)
11689

11790
# (index_put_pairs pairs index)

src/test/lib/render/C.fxl

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,9 @@
55
(\content
66
indent
77
(
8-
say "{";
9-
content;
10-
say "}";
11-
end
8+
say "{"
9+
content
10+
say "}"
1211
)
1312
)
1413

src/test/lib/render/base.fxl

Lines changed: 54 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,64 @@
1-
# Pure functional output
1+
# Indented output
22

3-
\end==(\tab [])
4-
\say==(\x\next\tab [tab x NL; next tab])
5-
\put==(\x\next\tab [x; next tab])
6-
\nl=(put NL)
3+
\v_put=var_new
4+
\v_nl=var_new
5+
\v_say=var_new
6+
\v_tab=var_new
77

8+
\put==(var_get v_put)
9+
\nl==(var_get v_nl ())
10+
\say==(var_get v_say)
11+
12+
# Indent content with a new tab stop.
813
\indent==
9-
(\content\next\tab
10-
[(eval (. tab TAB) content); next tab]
14+
(\content
15+
\tab=(var_get v_tab)
16+
var_put v_tab (. tab TAB)
17+
content
18+
var_put v_tab tab
19+
)
20+
21+
# Slide over to the current tab stop.
22+
\tab==(put (var_get v_tab))
23+
24+
# Redirect output functions to a file handle.
25+
\set_output_fh==
26+
(\fh
27+
var_put v_tab ""
28+
var_put v_put (fput fh)
29+
var_put v_nl (yield; fnl fh)
30+
var_put v_say
31+
(\x
32+
fput fh (var_get v_tab)
33+
fsay fh x
34+
)
35+
)
36+
37+
# Run content with output functions sent to a file handle.
38+
\use_output_fh==
39+
(\fh\content
40+
41+
\save_tab=(var_get v_tab)
42+
\save_put=(var_get v_put)
43+
\save_nl=(var_get v_nl)
44+
\save_say=(var_get v_say)
45+
46+
set_output_fh fh
47+
content
48+
49+
var_put v_tab save_tab
50+
var_put v_put save_put
51+
var_put v_nl save_nl
52+
var_put v_say save_say
1153
)
1254

55+
set_output_fh stdout
56+
1357
\form
14-
def "end" end;
15-
def "say" say;
1658
def "put" put;
1759
def "nl" nl;
60+
def "say" say;
1861
def "indent" indent;
62+
def "tab" tab;
63+
def "use_output_fh" use_output_fh;
1964
form

src/test/lib/render/list.fxl

Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,11 @@
11
#
2-
\render_rows==
3-
(\render_row @\loop \list\next
4-
list next \row\tail
5-
render_row row;
6-
loop tail;
7-
next
8-
)
9-
10-
\render_list==
11-
(\render_row\list\next
12-
say "[";
13-
render_rows render_row list;
14-
say "]";
15-
next
2+
\say_list==
3+
(\say_row\list
4+
say "["
5+
each list say_row
6+
say "]"
167
)
178

189
\form
19-
def "render_rows" render_rows;
20-
def "render_list" render_list;
10+
def "say_list" say_list;
2111
form

0 commit comments

Comments
 (0)