💾 Archived View for republic.circumlunar.space › users › dbane › gemlog › 2024-02-06.gmi captured on 2024-06-16 at 12:50:15. Gemini links have been rewritten to link to archived content
⬅️ Previous capture (2024-03-21)
-=-=-=-=-=-=-
I like KL1, but KLIC is taking a long time to be put on a software forge; and a lot of the things I think would be a good idea (e.g. kqueue, pthreads) are already in FLENG. So I've been taking a look at the latter again.
FLENG implements a few languages. Flat Guarded Horn Clauses (FGHC) is the one I'm interested in, it's almost identical to KL1. Library is different though. Similar to the difference between Java and C#. I try to explain a few fragments I wrote (for playing around only) in the following.
There are various examples, but the main value I'm getting out of this is exposure to a new programming paradigm. I remember being impressed by some highly parallel algorithms in more conventional languages in the past and wondering how the author came up with them. In a concurrent LP language you have no choice, this is the natural way to write code. But it takes a little reading to figure out, I can recommend the "Strand book" that the FLENG site also links to.
"Strand: New Concepts for Parallel Programming"
Tasks can be distributed over pthreads by just using '@'. The semantics of logic variables ensure thread-safety, and although it's not used here an incomplete list data structure can serve as a queue (this is all similar to other Concurrent Logic Programming languages, and well explained in the Strand book).
-initialization(main). main :- get_1(Value1)@2, % distributed to node 2 get_2(Value2)@3, % and 3 Sum is Value1 + Value2, writeln(Sum). get_1(Res) :- Res = 1. get_2(Res) :- Res = 2.
-initialization(main). -mode(to_zero(?, ^)). to_zero(X, Y) :- X > 0 | Y is X - 1. to_zero(X, Y) :- X < 0 | Y is X + 1. main :- get_module('', Mod), % '' is the default top-level module module_name(Mod, Nm), all_modules(Mods), Pred = {'to_zero', -40, Res}, % A tuple like this is identical to the Prolog functor to_zero(-40, Res) call(Pred), % apply() is also available, with semantics identical to Prolog print_res(Nm, Mods, Res). -mode(print_res(?, ?, ?)). print_res(Nm, Mods, Res) :- writeln(Nm) & % '&' forces sequencing, usually bad style when you can run all child clauses in parallel writeln(Mods) & writeln(Res)
Although not as dynamic as full Prolog, facilities like the above are relatively cheap, comparable to dlsym() in C but I prefer this code.
I haven't used it until now, but you can specify modes (input ? or output ^) for all predicates. I think this turns the language into "moded FGHC" according to Prof. Ueda.
Some thought was put into this. All networking I/O is via kqueue/epoll under the hood, and there are some fast-paths. For example, here's a minimal UNIX cat(1):
-initialization(run). run :- io:transfer(0, 1, _).
-initialization(main). click(Ev) :- send(Ev, click). main :- ezd:init(EZD0), open_port(Events, S), do_ui(Events, S, EZD0, _). do_ui(Events, S)-EZD :- bb:std_events(Events)-EZD, label("0")-EZD, bb:button("b", {300, 10}, {200, 30}, "Count", '':click(Events))-EZD, loop(S, EZD, 0). label(Val)-EZD :- bb:label("t", {10, 10}, Val)-EZD. loop([_|S2], EZD0, Count) :- C is Count + 1, number_to_list(C, CL, []), label(CL, EZD0, EZD), loop(S2, EZD, C).
There's a GUI implemented in PCN (an alternative to FGHC) on top of SDL2. The above is a translation of one of the PCN examples into FGHC, which was a fairly mechanical process. Looks won't win any aesthetic prizes, but it's serviceable and for commercial work a Web-style UI would probably be required anyway.
I decided to try out something that I had to change the KLIC runtime library for, calling the UNIX syslog function. I was able to do it in FLENG in ordinary code, without needing to dive into the compiler or runtime.
-foreign('#include <syslog.h>'). -foreign([openlog(string, integer, integer), syslog(integer, string), closelog(-void)]). -initialization(run). run :- log_pid(LogPid), log_local0(LogLocal0), log_info(LogInfo), openlog('foo', LogPid, LogLocal0, Ok0), when(Ok0, syslog(LogInfo, 'bar', Ok1)), when(Ok1, closelog(_)). -foreign(const(log_pid = 'LOG_PID':integer)). -foreign(const(log_local0 = 'LOG_LOCAL0':integer)). -foreign(const(log_info = 'LOG_INFO':integer)).
Some random observations:
In summary, FLENG is maturing rapidly.