• Re: Request for comments, Novacore the sequel to ISO modules

    From Mostowski Collapse@bursejan@gmail.com to comp.lang.prolog on Wed Mar 8 01:45:14 2023
    From Newsgroup: comp.lang.prolog

    Now I made a new version of my non-fibers and fibers API.
    I removed the name “engine” from the API, so as to avoid
    confusion. Engines are more lower level than the Python
    idea of callbacks and tasks. The API now reads:
    Part 1: Callbacks (non-fibers) (Changed)
    They are Stackless and run in the <strike>main Engine</strike>
    current Task of the Current Thread. In my current take, they run
    without Auto-Yield and without Yield-Allowed.
    os_call_later(G, D, T):
    The predicate succeeds in T with a new timer. As a side effect
    it schedules the goal G to be executed after D milliseconds.
    os_call_cancel(T):
    The predicate succeeds. As a side effect it cancels the timer T.
    Part 2: Tasks (1:N fibers) (Changed)
    They are Stackful and create their own <strike>Engine</strike> Task
    in the Current Thread. In my current take, they run with
    Auto-Yield and with Yield-Allowed.
    os_task_current(E):
    The predicate succeeds in E with the current <strike>engine</strike> task. os_task_abort(E, M):
    The predicate succeeds. As a side effect the <strike>engine</strike>
    task E gets the message M signalled.
    os_task_create(G, E):
    The predicate succeeds in E with a new <strike>engine</strike> task
    for the goal G. The task gets immediately scheduled to be executed.
    Mostowski Collapse schrieb am Donnerstag, 2. März 2023 um 13:08:48 UTC+1:
    I don’t know yet how to bake it. In formerly Jekejeke
    Prolog I have ultra static but no yield or auto-yield yet,
    despite that it has engines!

    In Dogelog Player I have yield or auto-yield, but no
    cooperative task spawning yet. One Prolog system that
    has already fibers is Tau Prolog, but their system

    doesn’t perform very well otherwise. SWI-Prolog is
    in a good position in that it has already engines with
    yield and recently auto-yield!
    Mostowski Collapse schrieb am Donnerstag, 2. März 2023 um 13:06:55 UTC+1:
    So is there really a 3rd category Worker Fiber? Thanks for
    challenging me explaning a concept over and over again.

    Q: You shouldn’t, but you need between fibers on the same worker?

    A: Do you mean shared atom and clause garbage collection is needed?
    Yes. But only a single threaded version of it. Its not doing anything between fibers inside the same worker. You don’t need locking or

    atomics for fibers as I see it. With the JavaScript Worker model,
    you land in single threaded Prolog system, although you are multi-threaded. I don’t know how difficult it would be to build a

    SWI-Prolog system that has Workers running single-threaded,
    but nevertheless supports many of them over threads? You possibly
    have to separate the Workers from a Workers monitor. Make the

    Workers monitor a separately compiled component, where multi-threading
    is enable. And compile the SWI-Prolog Workers Prolog runtime system single-threaded, i.e. with multi-threading disabled.
    Mostowski Collapse schrieb am Dienstag, 28. Februar 2023 um 01:05:12 UTC+1:
    Multi-Threaded Prolog ----\
    Single-Threaded Prolog ---+-----> Novacore
    Worker Fiber Prolog ------/
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Mostowski Collapse@bursejan@gmail.com to comp.lang.prolog on Wed Mar 8 01:47:33 2023
    From Newsgroup: comp.lang.prolog

    Nice addition to the current API spec and already
    implemented for JavaScript and Python. A callback
    has now the context available of the task that scheduled it.
    ?- os_task_current(T), write('task='), write(T), nl.
    task=main
    ?- call_later((os_task_current(T), write('task='),
    write(T), nl), 100), sleep(500).
    task=main
    ?- create_task((os_task_current(T), write('task='), write(T), nl)), sleep(500). task=[object Object]
    ?- create_task(call_later((os_task_current(T), write('task='),
    write(T), nl), 100)), sleep(500).
    task=[object Object]
    And there is a new Prolog flag allow_yield, which can
    be illustrated, you can query what the current API says:
    ?- current_prolog_flag(allow_yield, A), write('allow_yield='), write(A), nl. allow_yield=on
    ?- call_later((current_prolog_flag(allow_yield, A), write('allow_yield='), write(A), nl), 100), sleep(500).
    allow_yield=off
    Mostowski Collapse schrieb am Mittwoch, 8. März 2023 um 10:45:15 UTC+1:
    Now I made a new version of my non-fibers and fibers API.
    I removed the name “engine” from the API, so as to avoid
    confusion. Engines are more lower level than the Python

    idea of callbacks and tasks. The API now reads:

    Part 1: Callbacks (non-fibers) (Changed)
    They are Stackless and run in the <strike>main Engine</strike>
    current Task of the Current Thread. In my current take, they run
    without Auto-Yield and without Yield-Allowed.

    os_call_later(G, D, T):
    The predicate succeeds in T with a new timer. As a side effect
    it schedules the goal G to be executed after D milliseconds.

    os_call_cancel(T):
    The predicate succeeds. As a side effect it cancels the timer T.

    Part 2: Tasks (1:N fibers) (Changed)
    They are Stackful and create their own <strike>Engine</strike> Task
    in the Current Thread. In my current take, they run with
    Auto-Yield and with Yield-Allowed.

    os_task_current(E):
    The predicate succeeds in E with the current <strike>engine</strike> task.

    os_task_abort(E, M):
    The predicate succeeds. As a side effect the <strike>engine</strike>
    task E gets the message M signalled.

    os_task_create(G, E):
    The predicate succeeds in E with a new <strike>engine</strike> task
    for the goal G. The task gets immediately scheduled to be executed.
    Mostowski Collapse schrieb am Donnerstag, 2. März 2023 um 13:08:48 UTC+1:
    I don’t know yet how to bake it. In formerly Jekejeke
    Prolog I have ultra static but no yield or auto-yield yet,
    despite that it has engines!

    In Dogelog Player I have yield or auto-yield, but no
    cooperative task spawning yet. One Prolog system that
    has already fibers is Tau Prolog, but their system

    doesn’t perform very well otherwise. SWI-Prolog is
    in a good position in that it has already engines with
    yield and recently auto-yield!
    Mostowski Collapse schrieb am Donnerstag, 2. März 2023 um 13:06:55 UTC+1:
    So is there really a 3rd category Worker Fiber? Thanks for
    challenging me explaning a concept over and over again.

    Q: You shouldn’t, but you need between fibers on the same worker?

    A: Do you mean shared atom and clause garbage collection is needed?
    Yes. But only a single threaded version of it. Its not doing anything between fibers inside the same worker. You don’t need locking or

    atomics for fibers as I see it. With the JavaScript Worker model,
    you land in single threaded Prolog system, although you are multi-threaded. I don’t know how difficult it would be to build a

    SWI-Prolog system that has Workers running single-threaded,
    but nevertheless supports many of them over threads? You possibly
    have to separate the Workers from a Workers monitor. Make the

    Workers monitor a separately compiled component, where multi-threading is enable. And compile the SWI-Prolog Workers Prolog runtime system single-threaded, i.e. with multi-threading disabled.
    Mostowski Collapse schrieb am Dienstag, 28. Februar 2023 um 01:05:12 UTC+1:
    Multi-Threaded Prolog ----\
    Single-Threaded Prolog ---+-----> Novacore
    Worker Fiber Prolog ------/
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Mostowski Collapse@bursejan@gmail.com to comp.lang.prolog on Sat Mar 18 05:55:20 2023
    From Newsgroup: comp.lang.prolog

    Inside Novacore we could reinvent Prolog Dicts. JavaScript
    has a primitive data type for Symbols, so you can call
    Symbol.for(“key”), which will internalize the string, so that
    you can use pointer equality on the result:
    Symbol is a built-in object whose constructor returns a symbol primitive https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol
    It wouldn’t match JSON usage, since the keys are not supposed
    to be Symbols, only Strings. But maybe this is only superficially,
    and internally they are Symbols. One could do the same for
    Novacore Prolog Dicts. On the surface Novacore Prolog
    Dicts would use Strings:
    ?- X = {"abc" : 123.45, "def": 67}.
    But under the hood there would be a transition from String to Atom:
    ?- X = {"abc" : 123.45, "def": 67}, X =.. L.
    L = [C'novacore_dict, abc, 123.45, def, 67]
    The rational would be: The keys usually form a limited vocabulary.
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Mostowski Collapse@bursejan@gmail.com to comp.lang.prolog on Sat Mar 18 05:56:22 2023
    From Newsgroup: comp.lang.prolog

    Interestingly with the above trick, a Prolog parser can
    recognize Novacore Prolog Dicts. Since it would see this
    production at the head of a Novacore Prolog Dict:
    novacore_dict :== "{" string ":" term ... "}"
    Which is unlike the ISO core definition of “{}”, since in
    ISO core there are no strings, and even a qualified call in
    ISO module assumes that we have atom “:” term. So
    there would be no collision with this production:
    set :== "{" term "}"
    Mostowski Collapse schrieb am Samstag, 18. März 2023 um 13:55:21 UTC+1:
    Inside Novacore we could reinvent Prolog Dicts. JavaScript
    has a primitive data type for Symbols, so you can call Symbol.for(“key”), which will internalize the string, so that

    you can use pointer equality on the result:

    Symbol is a built-in object whose constructor returns a symbol primitive https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol

    It wouldn’t match JSON usage, since the keys are not supposed
    to be Symbols, only Strings. But maybe this is only superficially,
    and internally they are Symbols. One could do the same for

    Novacore Prolog Dicts. On the surface Novacore Prolog
    Dicts would use Strings:

    ?- X = {"abc" : 123.45, "def": 67}.
    But under the hood there would be a transition from String to Atom:

    ?- X = {"abc" : 123.45, "def": 67}, X =.. L.
    L = [C'novacore_dict, abc, 123.45, def, 67]

    The rational would be: The keys usually form a limited vocabulary.
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Mostowski Collapse@bursejan@gmail.com to comp.lang.prolog on Sat Mar 18 06:01:33 2023
    From Newsgroup: comp.lang.prolog

    Currently I get an error when I use string keys:
    /* SWI-Prolog 9.1.4 */
    ?- current_prolog_flag(double_quotes, X).
    X = string.
    ?- X = _{"abc": 123.46, "def": 67}.
    ERROR: Syntax error: key_expected
    Also there is the annoying need for an underscore functor.
    With string keys I could directly embed JSON?
    In this case null, false and true could be easily an atom.
    Thats kind of solving the constant problem from another angle.
    Mostowski Collapse schrieb am Samstag, 18. März 2023 um 13:56:23 UTC+1:
    Interestingly with the above trick, a Prolog parser can
    recognize Novacore Prolog Dicts. Since it would see this
    production at the head of a Novacore Prolog Dict:

    novacore_dict :== "{" string ":" term ... "}"

    Which is unlike the ISO core definition of “{}”, since in
    ISO core there are no strings, and even a qualified call in
    ISO module assumes that we have atom “:” term. So

    there would be no collision with this production:

    set :== "{" term "}"
    Mostowski Collapse schrieb am Samstag, 18. März 2023 um 13:55:21 UTC+1:
    Inside Novacore we could reinvent Prolog Dicts. JavaScript
    has a primitive data type for Symbols, so you can call Symbol.for(“key”), which will internalize the string, so that

    you can use pointer equality on the result:

    Symbol is a built-in object whose constructor returns a symbol primitive https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol

    It wouldn’t match JSON usage, since the keys are not supposed
    to be Symbols, only Strings. But maybe this is only superficially,
    and internally they are Symbols. One could do the same for

    Novacore Prolog Dicts. On the surface Novacore Prolog
    Dicts would use Strings:

    ?- X = {"abc" : 123.45, "def": 67}.
    But under the hood there would be a transition from String to Atom:

    ?- X = {"abc" : 123.45, "def": 67}, X =.. L.
    L = [C'novacore_dict, abc, 123.45, def, 67]

    The rational would be: The keys usually form a limited vocabulary.
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Mostowski Collapse@bursejan@gmail.com to comp.lang.prolog on Sat Mar 18 06:06:34 2023
    From Newsgroup: comp.lang.prolog

    But this other angle would only work inside JSON.
    There is still a problem with ordinary Prolog code, and
    for example the disabled setter. If we want to avoid some
    bottle neck of translating structures back and forth.
    Mostowski Collapse schrieb am Samstag, 18. März 2023 um 14:01:35 UTC+1:
    Currently I get an error when I use string keys:

    /* SWI-Prolog 9.1.4 */
    ?- current_prolog_flag(double_quotes, X).
    X = string.

    ?- X = _{"abc": 123.46, "def": 67}.
    ERROR: Syntax error: key_expected
    Also there is the annoying need for an underscore functor.

    With string keys I could directly embed JSON?
    In this case null, false and true could be easily an atom.
    Thats kind of solving the constant problem from another angle.
    Mostowski Collapse schrieb am Samstag, 18. März 2023 um 13:56:23 UTC+1:
    Interestingly with the above trick, a Prolog parser can
    recognize Novacore Prolog Dicts. Since it would see this
    production at the head of a Novacore Prolog Dict:

    novacore_dict :== "{" string ":" term ... "}"

    Which is unlike the ISO core definition of “{}”, since in
    ISO core there are no strings, and even a qualified call in
    ISO module assumes that we have atom “:” term. So

    there would be no collision with this production:

    set :== "{" term "}"
    Mostowski Collapse schrieb am Samstag, 18. März 2023 um 13:55:21 UTC+1:
    Inside Novacore we could reinvent Prolog Dicts. JavaScript
    has a primitive data type for Symbols, so you can call Symbol.for(“key”), which will internalize the string, so that

    you can use pointer equality on the result:

    Symbol is a built-in object whose constructor returns a symbol primitive
    https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol

    It wouldn’t match JSON usage, since the keys are not supposed
    to be Symbols, only Strings. But maybe this is only superficially,
    and internally they are Symbols. One could do the same for

    Novacore Prolog Dicts. On the surface Novacore Prolog
    Dicts would use Strings:

    ?- X = {"abc" : 123.45, "def": 67}.
    But under the hood there would be a transition from String to Atom:

    ?- X = {"abc" : 123.45, "def": 67}, X =.. L.
    L = [C'novacore_dict, abc, 123.45, def, 67]

    The rational would be: The keys usually form a limited vocabulary.
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Mostowski Collapse@bursejan@gmail.com to comp.lang.prolog on Sat May 13 06:36:03 2023
    From Newsgroup: comp.lang.prolog

    Now I have already removed the following predicates from
    Novacore, they landed in library(compat):

    - numbervars/2
    - subsumes/2
    - subsumes_term/2

    Now wonder where variant/2 would land? SWI-Prolog wants to tell me
    that variant/2 might need library(compat), because of numbervars/2.
    Assuming A and B have already distinct variables I get the following solution:

    A =@= B :-
    \+ \+ (numbervars(Ac, 0, N),
    numbervars(Bc, 0, N),
    Ac == Bc). https://www.swi-prolog.org/pldoc/doc_for?object=%28%3D@%3D%29/2

    On the other hand this solution gives me also a library(compat)
    dependency, since its based on subsumes_term/2. Again assuming A and
    B have already distinct variables I get the following solution:

    A =@= B :-
    subsumes_term(A, B),
    subsumes_term(B, A). https://www.complang.tuwien.ac.at/ulrich/iso-prolog/built-in_predicates

    Isn't there something simpler?
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Mostowski Collapse@bursejan@gmail.com to comp.lang.prolog on Sat May 13 06:41:09 2023
    From Newsgroup: comp.lang.prolog

    This is cute, has quite some different dependencies,
    inspired by the use of term_variables/3 in bagof/3,
    again assuming that A and B have already disjoint variables:

    A =@= B :-
    term_variables(A, L),
    term_variables(B, R),
    \+ \+ (L=R, A==B).

    Can be bootstrapped from a much smaler Novacore.

    Mostowski Collapse schrieb am Samstag, 13. Mai 2023 um 15:36:04 UTC+2:
    Now I have already removed the following predicates from
    Novacore, they landed in library(compat):

    - numbervars/2
    - subsumes/2
    - subsumes_term/2

    Now wonder where variant/2 would land? SWI-Prolog wants to tell me
    that variant/2 might need library(compat), because of numbervars/2.
    Assuming A and B have already distinct variables I get the following solution:

    A =@= B :-
    \+ \+ (numbervars(A, 0, N),
    numbervars(B, 0, N),
    A == B).
    https://www.swi-prolog.org/pldoc/doc_for?object=%28%3D@%3D%29/2

    On the other hand this solution gives me also a library(compat)
    dependency, since its based on subsumes_term/2. Again assuming A and
    B have already distinct variables I get the following solution:

    A =@= B :-
    subsumes_term(A, B),
    subsumes_term(B, A). https://www.complang.tuwien.ac.at/ulrich/iso-prolog/built-in_predicates

    Isn't there something simpler?
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Mostowski Collapse@bursejan@gmail.com to comp.lang.prolog on Sun May 21 03:54:41 2023
    From Newsgroup: comp.lang.prolog

    Now I have implemented the new open/4 options method/1,
    headers/1 and body/1 also for Dogelog Player. There is a first
    take that works in the browser. More platforms to follow.

    Its such a thin extension, API wise, want to have it as part of
    Novacore. What do other Prolog systems do? Here is what
    SWI-Prolog in their offering.

    - method/1: Accepts the method name in lower case,
    so far I use the option with an upper case value.
    - headers/1: Doesn't use our Key-Value pair format,
    instead the format is Key(Value). Has separate option
    for auth/1 and inside auth/1 for bearer/1.
    - body/1: Not available in SWI-Prolog, must use post/1,
    and post/1 accepts quite a bulk of formats.

    and then I find that Trealla Prolog does something else
    Signature wise:

    - offers some convenience like http_post/4, http_delete/3,
    bootstrapped from http_get/3.
    - http_get/3 has options method/1, post/1 and header/2,
    quite amazing, mostly written in 100% Prolog!
    - might also support HTTPS, depends on client/5.
    - this was checked into GitHub 9 months ago

    and Scryer Prolog does again something else
    Signature wise:

    - http_open/3 had a 100% Prolog solution in 2020,
    but became something else 12 months ago.
    - http_open/3 has options method/1, data/1 and
    request_headers/1, goes into CallHttpOpen instruction,
    which then uses hyper_tls.
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Mild Shock@bursejan@gmail.com to comp.lang.prolog on Sat Jul 29 04:51:11 2023
    From Newsgroup: comp.lang.prolog

    The new multilingual strings are also an exercise in
    Novacore. There were a few issues that needed novel
    Prolog solutions, to make a Novacore solution.

    One problem was I didn't want to use library(format)
    and format/3 to format multilingual strings when
    generating error messages. This addresses more

    the later multilingual strings processing than the
    multilingual strings store itself. So how resolve this
    paradox? Here is my take, a mini format/3 boostraped

    from the Dogelog Player specific atom_split/3:

    % sys_inter_polate(+Stream, +Atom, +List)
    sys_inter_polate(Stream, Template, Args) :-
    atom_split(Template, '~', [Head|Tail]),
    put_atom(Stream, Head),
    sys_zipper_output(Args, Tail, Stream).

    % sys_zipper_output(+List, +List, +Stream)
    sys_zipper_output([Arg|Args], [Head|Tail], Stream) :-
    writeq(Stream, Arg),
    put_atom(Stream, Head),
    sys_zipper_output(Args, Tail, Stream).
    sys_zipper_output([], [], _).

    It only understands format specifier '~', but is sufficient:

    /* German Text */
    strings('syntax_error.singleton_var', de, 'Alleinstehende Variable(n) ~, anonyme Variable(n) (_) benutzen.').

    /* English and Fallback Text */
    strings('syntax_error.singleton_var', '', 'Singleton variable(s) ~, use anonymous variable(s) (_).').

    LoL
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Mild Shock@bursejan@gmail.com to comp.lang.prolog on Thu Sep 7 17:15:20 2023
    From Newsgroup: comp.lang.prolog

    Did you know that Novacore has change_arg/3?
    Works for Dogelog Player and formerly Jekejeke Prolog.
    It is similar like nb_linkarg/3 in SWI-Prolog.

    So we can implement countall/3 in a blink:

    countall(G, N) :-
    functor(Holder, v, 1),
    change_arg(1, Holder, 0),
    (G,
    arg(1, Holder, H),
    J is H+1,
    change_arg(1, Holder, J),
    fail; true),
    arg(1, Holder, N).

    Works find:

    ?- countall(between(10,20,_), N).
    N = 11.
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Mild Shock@bursejan@gmail.com to comp.lang.prolog on Sun Nov 19 10:54:28 2023
    From Newsgroup: comp.lang.prolog

    We are now exploring file systems with novacore.
    And here and then we have a couple of primitives
    and then do some bootstrapping. It currently lands

    in library(random) until we find a better place:

    % directory_member(+Atom, -Atom)
    directory_member(F, N) :-
    directory_files(F, L),
    member(N, L).

    % ensure_directory(+Atom)
    ensure_directory(F) :-
    file_exists(F),
    file_property(F, type(directory)),
    !.
    ensure_directory(F) :-
    make_directory(F).

    Guess what, finding semantic and support of
    directory_files/2, file_exists/1 and file_property/2
    is already non trivial.
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Mild Shock@janburse@fastmail.fm to comp.lang.prolog on Sun Nov 19 19:59:07 2023
    From Newsgroup: comp.lang.prolog


    LogNonsenseTalk with its brainwash is totally
    useless. This here is already wrong:

    file_exists(File) :-
    absolute_file_name(File, ExpandedPath),
    {exists_file(ExpandedPath)}.

    https://github.com/LogtalkDotOrg/logtalk3/blob/master/library/os/os.lgt

    Becaue for example exists_file/1 in SWI-Prolog
    means exists regular file. But file_exists/1

    should mean exists file of any type. Just
    lookup what GNU Prolog provides. In OS lingua

    file means often regular, directory, etc..

    Mild Shock schrieb:
    We are now exploring file systems with novacore.
    And here and then we have a couple of primitives
    and then do some bootstrapping. It currently lands

    in library(random) until we find a better place:

    % directory_member(+Atom, -Atom)
    directory_member(F, N) :-
       directory_files(F, L),
       member(N, L).

    % ensure_directory(+Atom)
    ensure_directory(F) :-
       file_exists(F),
       file_property(F, type(directory)),
       !.
    ensure_directory(F) :-
       make_directory(F).

    Guess what, finding semantic and support of
    directory_files/2, file_exists/1 and file_property/2
    is already non trivial.


    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Mild Shock@janburse@fastmail.fm to comp.lang.prolog on Sun Nov 19 20:01:48 2023
    From Newsgroup: comp.lang.prolog

    You see the OS jargon meaning in directory_member/2
    which is bootstrapped from directory_files/2.

    directory_files/2 should of course list any files
    inside the directory, regular, directory, etc..

    not only regular files. So "files" means any
    file of type regular, directory, etc..

    Mild Shock schrieb:

    LogNonsenseTalk with its brainwash is totally
    useless. This here is already wrong:

    file_exists(File) :-
        absolute_file_name(File, ExpandedPath),
        {exists_file(ExpandedPath)}.

    https://github.com/LogtalkDotOrg/logtalk3/blob/master/library/os/os.lgt

    Becaue for example exists_file/1 in SWI-Prolog
    means exists regular file. But file_exists/1

    should mean exists file of any type. Just
    lookup what GNU Prolog provides. In OS lingua

    file means often regular, directory, etc..

    Mild Shock schrieb:
    We are now exploring file systems with novacore.
    And here and then we have a couple of primitives
    and then do some bootstrapping. It currently lands

    in library(random) until we find a better place:

    % directory_member(+Atom, -Atom)
    directory_member(F, N) :-
        directory_files(F, L),
        member(N, L).

    % ensure_directory(+Atom)
    ensure_directory(F) :-
        file_exists(F),
        file_property(F, type(directory)),
        !.
    ensure_directory(F) :-
        make_directory(F).

    Guess what, finding semantic and support of
    directory_files/2, file_exists/1 and file_property/2
    is already non trivial.



    --- Synchronet 3.20a-Linux NewsLink 1.114