experimenting with FLENG
- https://cdn.kinopio.club/szw5ARj8OnQ-EQfMwP6Bt/cole-freeman.800x.dither.png
- https://cdn.kinopio.club/zMhrZo-ZoE5aGDtN-ezBp/clipboard-20241126-220929.webp
- https://kinopio.club/mQkFBquD90gi3re33v9mU/-TIKeqVHkrr-zecXxDOmn
- https://kinopio.club/mQkFBquD90gi3re33v9mU/O1D8g--eHs9qtpfftC9kO
- https://kinopio.club/mQkFBquD90gi3re33v9mU/DSjC0GEmEDP5KRAN6pR93
- https://kinopio.club/mQkFBquD90gi3re33v9mU/qD4pgbJUEi8N7HvWtk1sK
- https://www.ueda.info.waseda.ac.jp/~ueda/pub/GHCthesis.pdf
"Guarded Horn Clauses" — PhD Thesis, Kazunori Ueda
- https://www.ueda.info.waseda.ac.jp/~ueda/pub/tr103new2.pdf Kazunori Ueda — Guarded Horn Clauses
- ## bounded buffers
- now let's wrap `transformer`, and have it block until the consumer sends a "ready" signal
```
-initialization(main).
router(ready(_), Lines, Msgs, Next) :-
transformer(Lines, Msgs, Next).
transformer([], Out, _) :- Out = [].
transformer([X|Xs], Out, Buf) :-
Buf = [_|Next],
Out = [message(X, Response)|Rest],
router(Response, Xs, Rest, Next).
consumer(_, []).
consumer(I, [message(X,_)|Xs]) :-
fmt:format("consumer: ~q\n", [X]),
J is I + 1,
consumer(J, Xs).
main :-
Buf = [_|Next],
io:read_lines_bounded(stdin, Buf, Lines),
transformer(Lines, Msgs, Next),
consumer(0, Msgs).
```
- implement the message passing, with some forced sequential processing and delays for the sake of illustration
```
-initialization(main).
timestamped(Msg, Args) :-
timer:get_time_of_day(time(D,S,M)),
list:append(["[~q:~q:~q] ", Msg], Fmt),
fmt:format(Fmt, [D,S,M|Args]).
router(ready(Id), Lines, Msgs, Next) :-
timestamped("router: got ready signal from consumer ~d\n", [Id])
& transformer(Lines, Msgs, Next).
transformer([], Out, _) :- Out = [].
transformer([X|Xs], Out, Buf) :-
Buf = [_|Next],
Out = [message(X, Response)|Rest],
router(Response, Xs, Rest, Next).
consumer(_, []).
consumer(I, [message(X,Response)|Xs]) :-
timestamped("consumer ~d: ~q\n", [I, X]),
timer(500, Done, _),
when(Done, Response = ready(I)),
J is I + 1,
consumer(J, Xs).
main :-
Buf = [_|Next],
io:read_lines_bounded(stdin, Buf, Lines),
transformer(Lines, Msgs, Next),
consumer(0, Msgs).
```
- ```
$ N=5; fleng incomplete-messages.ghc -o incomplete-messages
&& ./incomplete-messages < <(for ((i=0;i<N;i++)); do head -c 16 /dev/urandom | base64; done)
[20152:23132:746689] consumer 0: "rt14GLdzzdaO83JmUGLxPA=="
[20152:23133:246795] router: got ready signal from consumer 0
[20152:23133:247047] consumer 1: "I49w6mkIWjsrLP9DfbTtLw=="
[20152:23133:747161] router: got ready signal from consumer 1
[20152:23133:747421] consumer 2: "Qn4sUDlv8IUc8nuVeEpinQ=="
[20152:23134:247621] router: got ready signal from consumer 2
[20152:23134:247874] consumer 3: "3pfwazVw6w3Yad9j8KOOzQ=="
[20152:23134:747960] router: got ready signal from consumer 3
[20152:23134:748189] consumer 4: "+rNJE5UkTydOHMWP02gv9Q=="
[20152:23135:248386] router: got ready signal from consumer 4
```
- ## difference lists
- assemble multiple distinct lists into on
- the key here is that the tail of each distinct sub-list is unified with either the terminal empty list or one of the other sublists
- each forked process receives a variable representing the tail
- so you can process the sub-lists in parallel, and assemble them in constant time
- intersect.ghc — this example's straight from the strand book
```
-initialization(main).
intersect(L, M, Out) :- intersect1(L, M, [Out|[]]).
intersect1([X|Xs], M, Out, End) :-
member_add(X, M, Out, Acc),
intersect1(Xs, M, Acc, End).
intersect1([], _, Out, End) :- Out = End.
member_add(X, [X|_], Out, End) :-
Out = [X|End].
member_add(X, [Y|Ys], Out, End) :-
X =\= Y | member_add(X, Ys, Out, End).
member_add(_, [], Out, End) :- Out = End.
main :-
intersect([a,b,c,d], [c,e,f,d], Intersection),
fmt:format("~q\n", [Intersection]).
```
- ## short-circuits
- https://cdn.kinopio.club/PAxF_7QOJFEdFrSHfW1nR/image.png
experimenting with FLENG
- https://cdn.kinopio.club/szw5ARj8OnQ-EQfMwP6Bt/cole-freeman.800x.dither.png
- https://cdn.kinopio.club/zMhrZo-ZoE5aGDtN-ezBp/clipboard-20241126-220929.webp
- https://kinopio.club/mQkFBquD90gi3re33v9mU/-TIKeqVHkrr-zecXxDOmn
- https://kinopio.club/mQkFBquD90gi3re33v9mU/O1D8g--eHs9qtpfftC9kO
- https://kinopio.club/mQkFBquD90gi3re33v9mU/DSjC0GEmEDP5KRAN6pR93
- https://kinopio.club/mQkFBquD90gi3re33v9mU/qD4pgbJUEi8N7HvWtk1sK
- https://www.ueda.info.waseda.ac.jp/~ueda/pub/GHCthesis.pdf
"Guarded Horn Clauses" — PhD Thesis, Kazunori Ueda
- https://www.ueda.info.waseda.ac.jp/~ueda/pub/tr103new2.pdf Kazunori Ueda — Guarded Horn Clauses
- ## bounded buffers
- now let's wrap `transformer`, and have it block until the consumer sends a "ready" signal
```
-initialization(main).
router(ready(_), Lines, Msgs, Next) :-
transformer(Lines, Msgs, Next).
transformer([], Out, _) :- Out = [].
transformer([X|Xs], Out, Buf) :-
Buf = [_|Next],
Out = [message(X, Response)|Rest],
router(Response, Xs, Rest, Next).
consumer(_, []).
consumer(I, [message(X,_)|Xs]) :-
fmt:format("consumer: ~q\n", [X]),
J is I + 1,
consumer(J, Xs).
main :-
Buf = [_|Next],
io:read_lines_bounded(stdin, Buf, Lines),
transformer(Lines, Msgs, Next),
consumer(0, Msgs).
```
- implement the message passing, with some forced sequential processing and delays for the sake of illustration
```
-initialization(main).
timestamped(Msg, Args) :-
timer:get_time_of_day(time(D,S,M)),
list:append(["[~q:~q:~q] ", Msg], Fmt),
fmt:format(Fmt, [D,S,M|Args]).
router(ready(Id), Lines, Msgs, Next) :-
timestamped("router: got ready signal from consumer ~d\n", [Id])
& transformer(Lines, Msgs, Next).
transformer([], Out, _) :- Out = [].
transformer([X|Xs], Out, Buf) :-
Buf = [_|Next],
Out = [message(X, Response)|Rest],
router(Response, Xs, Rest, Next).
consumer(_, []).
consumer(I, [message(X,Response)|Xs]) :-
timestamped("consumer ~d: ~q\n", [I, X]),
timer(500, Done, _),
when(Done, Response = ready(I)),
J is I + 1,
consumer(J, Xs).
main :-
Buf = [_|Next],
io:read_lines_bounded(stdin, Buf, Lines),
transformer(Lines, Msgs, Next),
consumer(0, Msgs).
```
- ```
$ N=5; fleng incomplete-messages.ghc -o incomplete-messages
&& ./incomplete-messages < <(for ((i=0;i<N;i++)); do head -c 16 /dev/urandom | base64; done)
[20152:23132:746689] consumer 0: "rt14GLdzzdaO83JmUGLxPA=="
[20152:23133:246795] router: got ready signal from consumer 0
[20152:23133:247047] consumer 1: "I49w6mkIWjsrLP9DfbTtLw=="
[20152:23133:747161] router: got ready signal from consumer 1
[20152:23133:747421] consumer 2: "Qn4sUDlv8IUc8nuVeEpinQ=="
[20152:23134:247621] router: got ready signal from consumer 2
[20152:23134:247874] consumer 3: "3pfwazVw6w3Yad9j8KOOzQ=="
[20152:23134:747960] router: got ready signal from consumer 3
[20152:23134:748189] consumer 4: "+rNJE5UkTydOHMWP02gv9Q=="
[20152:23135:248386] router: got ready signal from consumer 4
```
- ## difference lists
- assemble multiple distinct lists into on
- the key here is that the tail of each distinct sub-list is unified with either the terminal empty list or one of the other sublists
- each forked process receives a variable representing the tail
- so you can process the sub-lists in parallel, and assemble them in constant time
- intersect.ghc — this example's straight from the strand book
```
-initialization(main).
intersect(L, M, Out) :- intersect1(L, M, [Out|[]]).
intersect1([X|Xs], M, Out, End) :-
member_add(X, M, Out, Acc),
intersect1(Xs, M, Acc, End).
intersect1([], _, Out, End) :- Out = End.
member_add(X, [X|_], Out, End) :-
Out = [X|End].
member_add(X, [Y|Ys], Out, End) :-
X =\= Y | member_add(X, Ys, Out, End).
member_add(_, [], Out, End) :- Out = End.
main :-
intersect([a,b,c,d], [c,e,f,d], Intersection),
fmt:format("~q\n", [Intersection]).
```
- ## short-circuits
- https://cdn.kinopio.club/PAxF_7QOJFEdFrSHfW1nR/image.png