💾 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

View Raw

More Information

⬅️ Previous capture (2024-03-21)

-=-=-=-=-=-=-

FLENG as a "Plan B"

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.

KLIC homepage

FLENG

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"

Parallelism

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.

Metaprogramming

-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.

I/O

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, _).

GUI

-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.

Screenshot of this program

FFI

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.

Back to my gemlog