So what would be the benefit of Novacore? It would solve
some of the bottlenecks in Prolog systems. Somebody wrote:
"Surely if SWI-Prolog is the outlier I’m happy to change that,
considering that the impact is most likely low."
How can you do **widescale change** and semantic confluence
without waiting for every Prolog system, even without waiting
for SWI-Prolog? Thats what Polyfills do in JavaScript. But I don’t
know how popular they are right now. In the past there
was Babel, and it is still there:
Babel is a JavaScript compiler
Use next generation JavaScript, today.
https://babeljs.io/
Logtalk is kind of the Prolog Babel, with the difference that
Logtalk doesn’t do existing Prolog system module system
injection. But the Bable toolchain consists of more than only
Polyfills, some are similar to Logtalk again (codemods):
- Transform syntax
- Polyfill features that are missing in your target environment
(through a third-party polyfill such as core-js)
^- Source code transformations (codemods)
- And more!
To some extend there is also a third option besides the alternative
of if-then-else conditional Prolog compilation. You could also
use goal_expansion/2 to do some things.
Mostowski Collapse schrieb am Mittwoch, 17. August 2022 um 14:18:25 UTC+2:
Now I have an idea for a novell Prolog core, called Novacore.
What should it be able to do? A few features from JavaScript:
a) Renaming Module Import
The have even renaming module import. For example one
can do in JavaScript:
const emitter = require('events');
But is there a Prolog system that has a renaming module import?
Like for example this here:
:- use_module(events as emitter).
b) Polyfills
In JavaScript new functionality can be added to old versions
via so called polyfills. On the module/class level, this requires
that you can do code injections, later mixins.
Although Prolog would be in a good position here, since for
example it has multifile predicates. But does this work for a
module system of a Prolog system? And how would a polyfill
exactly be done?
I tried this in Ciao Prolog playground, but there seems a couple
of other problems:
:- use_module(library(lists)).
lists:subtract([], _, R) :- !,
R = [].
lists:subtract([E|T], D, R) :- !,
( memberchk(E, D)
lists:subtract(T, D, R); R = [E|R1],
lists:subtract(T, D, R1)
).
I get quite a funny bouquet of errors:
ERROR: (lns 6-11) Predicate memberchk/2 undefined in source
ERROR: (lns 6-11) Bad module qualification of subtract/3,
predicate not imported from module lists
ERROR: (lns 6-11) Bad module qualification of subtract/3,
predicate not imported from module lists
ERROR: Aborted module compilation
If I am not totally mistaken the subtract/3 mixing works in SWI-Prolog
and in Jekejeke Prolog. Have to try again. But there could be issues
with reconsult, in general it makes a few things more complicated
concerning the Prolog system tooling. But frankly I didn’t use it so often, there were a few use cases though.
Now I have an idea for a novell Prolog core, called Novacore.
What should it be able to do? A few features from JavaScript:
a) Renaming Module Import
The have even renaming module import. For example one
can do in JavaScript:
const emitter = require('events');
But is there a Prolog system that has a renaming module import?
Like for example this here:
:- use_module(events as emitter).
b) Polyfills
In JavaScript new functionality can be added to old versions
via so called polyfills. On the module/class level, this requires
that you can do code injections, later mixins.
Although Prolog would be in a good position here, since for
example it has multifile predicates. But does this work for a
module system of a Prolog system? And how would a polyfill
exactly be done?
I tried this in Ciao Prolog playground, but there seems a couple
of other problems:
:- use_module(library(lists)).
lists:subtract([], _, R) :- !,
R = [].
lists:subtract([E|T], D, R) :- !,
( memberchk(E, D)
lists:subtract(T, D, R); R = [E|R1],
lists:subtract(T, D, R1)
).
I get quite a funny bouquet of errors:
ERROR: (lns 6-11) Predicate memberchk/2 undefined in source
ERROR: (lns 6-11) Bad module qualification of subtract/3,
predicate not imported from module lists
ERROR: (lns 6-11) Bad module qualification of subtract/3,
predicate not imported from module lists
ERROR: Aborted module compilation
If I am not totally mistaken the subtract/3 mixing works in SWI-Prolog
and in Jekejeke Prolog. Have to try again. But there could be issues
with reconsult, in general it makes a few things more complicated
concerning the Prolog system tooling. But frankly I didn’t use it so often, there were a few use cases though.
The implementation goes:
bb_put(Key, Value) :-
( atom(Key) ->
'$store_global_var'(Key, Value)
; type_error(atom, Key, bb_put/2)
).
And then bb_put/2 is used in Scryer Prologs call_nth/2.
How do you want to use this in a multi-threaded Prolog system?
LMAO!
Mostowski Collapse schrieb am Mittwoch, 31. August 2022 um 11:59:37 UTC+2:
Was refactoring Dogelog Player. It has now a couple
of libraries, making use of the new system_url to fetch them.
- library(compat): New idea of a library, has things like tab/1.
- library(sequence): Reduced library(adanced/sequence) from formerly Jekejeke Prolog.
- library(aggregate): Reduced library(adanced/aggregate) from formerly Jekejeke Prolog.
- library(lists): Like library(basic/lists) from formerly Jekejeke Prolog.
library(sequence) and library(aggregate) are implemented via
the new change_arg/3 in Dogelog Player, unlike Jekejeke Prolog
which uses some Java pivot datastructure.
The new system_url to fetch does not change Dogelog Player
performance. Dogelog Player is still not an extremly fast
Prolog system, it might beat Tau Prolog but otherwise it has
rather modest performance. But change_arg/3 gives nice
performance. Its the better solution than bb_put/2 and bb_get/2,
which is module local, and BTW will not work in multi-threading,
if it has SICStus semantics it is for "blackboard" communication
between multiple threads. The bb_ stands for "blackboard". So its
not thread-local. What is Scryer Prolog thinking?
Anyway, here some performance:
/* Scryer Prolog "v0.9.0-175-g6b8e6204" */
?- use_module(library(iso_ext)).
true.
?- time((call_nth((between(1,1000,_),call_nth(between(1,1000,_),_)),_), fail; true)).
% CPU time: 4.719s
true.
/* Dogelog Player 1.0.2, nodejs */
?- ensure_loaded(library(sequence)).
true.
?- time((call_nth((between(1,1000,_),call_nth(between(1,1000,_),_)),_), fail; true)).
% Wall 1763 ms, gc 4 ms, 6250971 lips
true.
/* SWI-Prolog, 8.5.14 */
?- time((call_nth((between(1,1000,_),call_nth(between(1,1000,_),_)),_), fail; true)).
% 5,002,004 inferences, 0.484 CPU in 0.473 seconds (102% CPU, 10326718 Lips)
true.
Was refactoring Dogelog Player. It has now a couple
of libraries, making use of the new system_url to fetch them.
- library(compat): New idea of a library, has things like tab/1.
- library(sequence): Reduced library(adanced/sequence) from formerly Jekejeke Prolog.
- library(aggregate): Reduced library(adanced/aggregate) from formerly Jekejeke Prolog.
- library(lists): Like library(basic/lists) from formerly Jekejeke Prolog.
library(sequence) and library(aggregate) are implemented via
the new change_arg/3 in Dogelog Player, unlike Jekejeke Prolog
which uses some Java pivot datastructure.
The new system_url to fetch does not change Dogelog Player
performance. Dogelog Player is still not an extremly fast
Prolog system, it might beat Tau Prolog but otherwise it has
rather modest performance. But change_arg/3 gives nice
performance. Its the better solution than bb_put/2 and bb_get/2,
which is module local, and BTW will not work in multi-threading,
if it has SICStus semantics it is for "blackboard" communication
between multiple threads. The bb_ stands for "blackboard". So its
not thread-local. What is Scryer Prolog thinking?
Anyway, here some performance:
/* Scryer Prolog "v0.9.0-175-g6b8e6204" */
?- use_module(library(iso_ext)).
true.
?- time((call_nth((between(1,1000,_),call_nth(between(1,1000,_),_)),_), fail; true)).
% CPU time: 4.719s
true.
/* Dogelog Player 1.0.2, nodejs */
?- ensure_loaded(library(sequence)).
true.
?- time((call_nth((between(1,1000,_),call_nth(between(1,1000,_),_)),_), fail; true)).
% Wall 1763 ms, gc 4 ms, 6250971 lips
true.
/* SWI-Prolog, 8.5.14 */
?- time((call_nth((between(1,1000,_),call_nth(between(1,1000,_),_)),_), fail; true)).
% 5,002,004 inferences, 0.484 CPU in 0.473 seconds (102% CPU, 10326718 Lips) true.
End-users have really problems with embeding
Prolog into HTML pages by themself. For example
an simple advice as follows from Tau Prolog:
get_by_id(writeme, WriteMe),
open(WriteMe, write, Stream),
set_output(Stream),
...
write(Formula)
doesn't work, when the stream does a
innerHTML += for the writeme element. What
would then be needed is:
get_by_id(writeme, WriteMe),
open(WriteMe, write, Stream),
set_output(Stream),
...
term_atom(Formula, FormulaAtom),
xml_ecape(FormulaAtom, FormulaEscaped),
write(FormulaEscaped)
I guess xml_escape/2 exists already since
1990's when the world's first web server, was
developed and implemented by Berners-Lee
on a NeXTcube computer. But its even
worse, does Tau Prolog have a term_atom/2?
I would like to have nice test cases for Novacore.
For sure floats would be part of Novacore. SWI-Prolog
publish its new release 8.5.18. And it says it does now
do to_nearest rouding more consequently. What does
that mean? Wiki tells me there are at least two “to nearest”:
- Round to nearest, ties to even – rounds to the nearest value;
if the number falls midway, it is rounded to the nearest value
with an even least significant digit.
- Round to nearest, ties away from zero (or ties to away) – rounds
to the nearest value; if the number falls midway, it is rounded to
the nearest value above (for positive numbers) or below (for negative numbers).
https://en.wikipedia.org/wiki/IEEE_754#Roundings_to_nearest 1
But then in Java BigDecimal there are even 3 “to nearest”,
HALF_EVEN, HALF_UP and HALF_DOWN.
I guess the to_nearest should be HALF_EVEN. Was
lazy making manual test cases, so used a fuzzer.
The fuzzer test cases might not be that good, especially
for HALF_EVEN, since they might not pick up the border
cases, but nevertheless its a first start. Might do more
test cases. The test cases are simply:
fuzzer :-
L is -(1<<100),
H is (1<<100)+1,
between(1, 100, N),
random(L, H, X),
Y is float(X),
write(case(N, X, Y)), write('.'), nl,
fail.
fuzzer.
My Prolog systems, formerly Jekejeke Prolog and
Dogelog Player on its various target platforms, all
pass these test cases.
Mostowski Collapse schrieb am Montag, 10. Oktober 2022 um 01:06:09 UTC+2:
I would like to have nice test cases for Novacore.
For sure floats would be part of Novacore. SWI-Prolog
publish its new release 8.5.18. And it says it does now
do to_nearest rouding more consequently. What does
that mean? Wiki tells me there are at least two “to nearest”:
- Round to nearest, ties to even – rounds to the nearest value;
if the number falls midway, it is rounded to the nearest value
with an even least significant digit.
- Round to nearest, ties away from zero (or ties to away) – rounds
to the nearest value; if the number falls midway, it is rounded to
the nearest value above (for positive numbers) or below (for negative numbers).
https://en.wikipedia.org/wiki/IEEE_754#Roundings_to_nearest 1
But then in Java BigDecimal there are even 3 “to nearest”,
HALF_EVEN, HALF_UP and HALF_DOWN.
Here are some very first results for other Prolog systems:
/* SWI-Prolog 8.5.18 (Windows and WASM) Ok */
?- case(N, X, Y), Y =\= float(X).
false.
/* Trealla Prolog 2.4.3 Ok */
?- case(N, X, Y), Y =\= float(X).
false.
/* Scryer Prolog 0.9.0 Nok */
?- case(N, X, Y), Y =\= float(X).
N = 1, X = -572504891324561953821040518484, Y = -5.7250489132456196e29
; N = 3, X = 884996183305110611102854483978, Y = 8.849961833051106e29
; N = 4, X = 1251939306673717603656775488197, Y = 1.2519393066737177e30 Etc..
So somehow these test cases already do their job!
Mostowski Collapse schrieb am Montag, 10. Oktober 2022 um 01:12:39 UTC+2:
I guess the to_nearest should be HALF_EVEN. Was
lazy making manual test cases, so used a fuzzer.
The fuzzer test cases might not be that good, especially
for HALF_EVEN, since they might not pick up the border
cases, but nevertheless its a first start. Might do more
test cases. The test cases are simply:
fuzzer :-
L is -(1<<100),
H is (1<<100)+1,
between(1, 100, N),
random(L, H, X),
Y is float(X),
write(case(N, X, Y)), write('.'), nl,
fail.
fuzzer.
My Prolog systems, formerly Jekejeke Prolog and
Dogelog Player on its various target platforms, all
pass these test cases.
Mostowski Collapse schrieb am Montag, 10. Oktober 2022 um 01:06:09 UTC+2:
I would like to have nice test cases for Novacore.
For sure floats would be part of Novacore. SWI-Prolog
publish its new release 8.5.18. And it says it does now
do to_nearest rouding more consequently. What does
that mean? Wiki tells me there are at least two “to nearest”:
- Round to nearest, ties to even – rounds to the nearest value;
if the number falls midway, it is rounded to the nearest value
with an even least significant digit.
- Round to nearest, ties away from zero (or ties to away) – rounds
to the nearest value; if the number falls midway, it is rounded to
the nearest value above (for positive numbers) or below (for negative numbers).
https://en.wikipedia.org/wiki/IEEE_754#Roundings_to_nearest 1
But then in Java BigDecimal there are even 3 “to nearest”, HALF_EVEN, HALF_UP and HALF_DOWN.
Here is a better fuzzer, it has a higher mean lsb/1 value,
and therefore mostlikely tests more HALF_EVEN:
?- aggregate_all(sum(N), (case(_,X,_), N is lsb(abs(X))), S), A is S/100.
S = 94,
A = 0.94.
?- aggregate_all(sum(N), (case2(_,X,_), N is lsb(abs(X))), S), A is S/100.
S = 5050,
A = 50.5.
The fiuzzer reads as follows:
fuzzer2 :-
between(1, 100, N),
L is -(1<<N),
H is (1<<N)+1,
random(L, H, X),
Z is X<<(100-N),
Y is float(Z),
write(case2(N, Z, Y)), write('.'), nl,
fail.
fuzzer2.
It indeed trip wires the newest SWI-Prolog:
/* SWI-Prolog 8.5.18 Nok */
?- case2(N, X, Y), Z is float(X), Y =\= Z.
N = 56,
X = -1190359501396335678156200476672,
Y = -1.1903595013963357e+30,
Z = -1.1903595013963356e+30 ;
false.
The test case is ok in all my systems, like formerly
Jekejeke Prolog and Dogelog Player. The test
case is also ok here:
/* Trealla Prolog 2.4.3 Ok */
?- case2(N, X, Y), Y =\= float(X).
false.
/* Ciao Prolog 1.22.0 Ok */
?- case2(N, X, Y), Y =\= float(X).
no
Mostowski Collapse schrieb am Montag, 10. Oktober 2022 um 08:57:43 UTC+2:
Here is a better fuzzer, it has a higher mean lsb/1 value,
and therefore mostlikely tests more HALF_EVEN:
?- aggregate_all(sum(N), (case(_,X,_), N is lsb(abs(X))), S), A is S/100.
S = 94,
A = 0.94.
?- aggregate_all(sum(N), (case2(_,X,_), N is lsb(abs(X))), S), A is S/100. S = 5050,
A = 50.5.
The fiuzzer reads as follows:
fuzzer2 :-
between(1, 100, N),
L is -(1<<N),
H is (1<<N)+1,
random(L, H, X),
Z is X<<(100-N),
Y is float(Z),
write(case2(N, Z, Y)), write('.'), nl,
fail.
fuzzer2.
Some suspicion about the random number skewing.
Mostlikely #define MAX_URANDOMM_ITER is too low. See
here what GMP does. And it then falls back to low quality result:
if (count == 0)
/* Too many iterations; return result mod n == result - n */
mpn_sub_n (rp, rp, np, size); https://salsa.debian.org/science-team/gmp/-/blob/master/mpz/urandomm.c
Note that the example interval [0…2^n+1) is a very nasty non power
of 2 interval. That I was using a nasty interval was rather not planned, when writing the fuzzer, just some accident.
In my implementation I have no MAX_URANDOMM_ITER limit.
My suspiciion, therefore it generates a better probability shaping
on my side, which can be even measured,
already for 100 bits? Is this the reason? Or its some other bug
in mpz_urandomm() caused by the last limb cmp juggling?
I didn’t test the random/1 evaluable function in Trealla and Cliao yet. Cannot run the same test case in Trealla Prolog or Ciao Prolog,
too much stuff missing:
- Trealla Prolog: Didn’t find bigint random.
- Ciao Prolog: Found some random, with an incompatible
protocol to SWI-Prolog, different meaning of the parameters,
but there is so much other stuff missing, like
msb/1, aggregate_all/3, etc…
Mostowski Collapse schrieb am Mittwoch, 12. Oktober 2022 um 19:46:58 UTC+2:
Some suspicion about the random number skewing.
Mostlikely #define MAX_URANDOMM_ITER is too low. See
here what GMP does. And it then falls back to low quality result:
if (count == 0)
/* Too many iterations; return result mod n == result - n */
mpn_sub_n (rp, rp, np, size); https://salsa.debian.org/science-team/gmp/-/blob/master/mpz/urandomm.c
Note that the example interval [0…2^n+1) is a very nasty non power
of 2 interval. That I was using a nasty interval was rather not planned, when writing the fuzzer, just some accident.
In my implementation I have no MAX_URANDOMM_ITER limit.
My suspiciion, therefore it generates a better probability shaping
on my side, which can be even measured,
already for 100 bits? Is this the reason? Or its some other bug
in mpz_urandomm() caused by the last limb cmp juggling?
On Ciao Prolog its:
random(0,M,...)
On SWI-Prolog its:
random(0,M+1,...)
Should there be a discussion about random/3, its protocoll?
I don’t know, what are the plans of Ciao Prolog and SWI-Prolog?
I don’t know? Is there a road map? I don’t know.
Recently SWI-Prolog deprecated random/3 and introduced
random_between/3 with a different protocoll. Maybe this can
point the way forward? I am more interested about the
skewness here, and not the protcoll of some random API.
Skewed shouldn’t mean screwed. Its quite possible that a
skewed random generator can be fixed.
Mostowski Collapse schrieb am Mittwoch, 12. Oktober 2022 um 19:48:05 UTC+2:
I didn’t test the random/1 evaluable function in Trealla and Cliao yet. Cannot run the same test case in Trealla Prolog or Ciao Prolog,
too much stuff missing:
- Trealla Prolog: Didn’t find bigint random.
- Ciao Prolog: Found some random, with an incompatible
protocol to SWI-Prolog, different meaning of the parameters,
but there is so much other stuff missing, like
msb/1, aggregate_all/3, etc…
Mostowski Collapse schrieb am Mittwoch, 12. Oktober 2022 um 19:46:58 UTC+2:
Some suspicion about the random number skewing.
Mostlikely #define MAX_URANDOMM_ITER is too low. See
here what GMP does. And it then falls back to low quality result:
if (count == 0)
/* Too many iterations; return result mod n == result - n */
mpn_sub_n (rp, rp, np, size); https://salsa.debian.org/science-team/gmp/-/blob/master/mpz/urandomm.c
Note that the example interval [0…2^n+1) is a very nasty non power
of 2 interval. That I was using a nasty interval was rather not planned, when writing the fuzzer, just some accident.
In my implementation I have no MAX_URANDOMM_ITER limit.
My suspiciion, therefore it generates a better probability shaping
on my side, which can be even measured,
already for 100 bits? Is this the reason? Or its some other bug
in mpz_urandomm() caused by the last limb cmp juggling?
So maybe the requirement is eclectic? A non skewed
random ganerator for bigints. Often power of 2 is only
required or smallint is enough.
Ha Ha, a Casino website could be interested that their
random generators are not skewed. Or maybe they even
go for a particular skewed random generator?
What if the users of the Casino website figure out the
skewing and play accordingly? I am thinking of games
like roulette or card games. So its in their own interest?
Legitimacy & Regulation of the RNG
There are a number of independent companies which specialise in iGaming testing.
https://www.casinoguardian.co.uk/casino-guide/random-number-generator/
Does the C floating point API have HALF_EVEN default?
Maybe, maybe not. Is there a test case to figure out
what rounding a system, Prolog or not, does, for its
floating point numbers, independent of some bigint
conversion issues? Was trying the folllowing test and got
some surprise. There was first quite some agreement:
/* SWI-Prolog 8.5.18 */
?- X is 665723957508124892951320985600.0+26388279066624.0.
X = 6.65723957508125e+29.
/* Ciao Prolog 1.22.0 */
?- X is 665723957508124892951320985600.0+26388279066624.0.
X = 6.65723957508125e29 ?
/* Trealla Prolog 2.4.3 */
?- X is 665723957508124892951320985600.0+26388279066624.0.
X = 6.65723957508125e+29.
/* Jekejeke Prolog 1.5.5 */
?- X is 665723957508124892951320985600.0+26388279066624.0.
X = 6.65723957508125E29.
But then suddently:
/* Scryer Prolog 0.9.0 */
?- X is 665723957508124892951320985600.0+26388279066624.0.
X = 6.657239575081248e29.
/* ECLiPSe Prolog 7.0.61 */
?- X is 665723957508124892951320985600.0+26388279066624.0.
X = 6.6572395750812482e+29
?- X is 6.6572395750812482e+29-6.65723957508125E29.
X = -140737488355328.0.
Maybe this also explains the divergence in bigint to float conversion.
Maybe they have by accident a misconfigured FPU? Some init call
to the runtime system missing? Or a cheap floating point library?
But one is WSL2 and the other Windows!
Mostowski Collapse schrieb am Donnerstag, 13. Oktober 2022 um 20:16:02 UTC+2:
Does the C floating point API have HALF_EVEN default?
Maybe, maybe not. Is there a test case to figure out
what rounding a system, Prolog or not, does, for its
floating point numbers, independent of some bigint
conversion issues? Was trying the folllowing test and got
some surprise. There was first quite some agreement:
/* SWI-Prolog 8.5.18 */
?- X is 665723957508124892951320985600.0+26388279066624.0.
X = 6.65723957508125e+29.
/* Ciao Prolog 1.22.0 */
?- X is 665723957508124892951320985600.0+26388279066624.0.
X = 6.65723957508125e29 ?
/* Trealla Prolog 2.4.3 */
?- X is 665723957508124892951320985600.0+26388279066624.0.
X = 6.65723957508125e+29.
/* Jekejeke Prolog 1.5.5 */
?- X is 665723957508124892951320985600.0+26388279066624.0.
X = 6.65723957508125E29.
Have to dig deeper, could be a parsing problem.
For the first summand, I get:
/* SWI-Prolog */
?- X is 665723957508124892951320985600.0.
X = 6.65723957508125e+29.
/* ECLiPSe Prolog */
X is 665723957508124892951320985600.0.
X = 6.6572395750812482e+29
I am not yet at a test case, which would give me
some information about rounding.
Mostowski Collapse schrieb am Donnerstag, 13. Oktober 2022 um 20:18:04 UTC+2:
But then suddently:
/* Scryer Prolog 0.9.0 */
?- X is 665723957508124892951320985600.0+26388279066624.0.
X = 6.657239575081248e29.
/* ECLiPSe Prolog 7.0.61 */
?- X is 665723957508124892951320985600.0+26388279066624.0.
X = 6.6572395750812482e+29
?- X is 6.6572395750812482e+29-6.65723957508125E29.
X = -140737488355328.0.
Maybe this also explains the divergence in bigint to float conversion. Maybe they have by accident a misconfigured FPU? Some init call
to the runtime system missing? Or a cheap floating point library?
But one is WSL2 and the other Windows!
Mostowski Collapse schrieb am Donnerstag, 13. Oktober 2022 um 20:16:02 UTC+2:
Does the C floating point API have HALF_EVEN default?
Maybe, maybe not. Is there a test case to figure out
what rounding a system, Prolog or not, does, for its
floating point numbers, independent of some bigint
conversion issues? Was trying the folllowing test and got
some surprise. There was first quite some agreement:
/* SWI-Prolog 8.5.18 */
?- X is 665723957508124892951320985600.0+26388279066624.0.
X = 6.65723957508125e+29.
/* Ciao Prolog 1.22.0 */
?- X is 665723957508124892951320985600.0+26388279066624.0.
X = 6.65723957508125e29 ?
/* Trealla Prolog 2.4.3 */
?- X is 665723957508124892951320985600.0+26388279066624.0.
X = 6.65723957508125e+29.
/* Jekejeke Prolog 1.5.5 */
?- X is 665723957508124892951320985600.0+26388279066624.0.
X = 6.65723957508125E29.
Now turning my attention to (/)/2. SWI-Prolog wants me
to use set_prolog_flag(iso, true), to get the semantics
of the ISO core standard, page 116.
But the fuzzer still finds the needle in the haystack.
Using more bits to the numerator, and I find:
/* Jekejeke Prolog 1.5.5 */
?- X is -15915286457581575168 / -238455449856.
X = 6.674322800000001E7.
/* SWI-Prolog 8.5.18 */
?- current_prolog_flag(iso, X).
X = true.
?- X is -15915286457581575168 / -238455449856.
X = 66743227.99999999.
0.002525252525252525396** -1
0.00252525252525252551/396
Now I have test cases so nasty, even Ciao Prolog and
SICStus Prolog are stumbling. Interestingly Ciao Prolog
and SICStus Prolog stumble at the exact same test cases.
/* Ciao Prolog 1.22.0 */
?- case4(N, P, Q, Y), Z is P/Q, Y =\= Z.
N = 3,
P = -138732791312937296168,
Q = 70775453,
Y = -1960182315087.9346,
Z = -1960182315087.9343 ? ;
no
/* SICStus Prolog 4.7.1 */
?- case4(N, P, Q, Y), Z is P/Q, Y =\= Z.
N = 3,
P = -138732791312937296168,
Q = 70775453,
Y = -1960182315087.9346,
Z = -1960182315087.9343 ? ;
no
Mostowski Collapse schrieb am Freitag, 14. Oktober 2022 um 10:41:45 UTC+2:
Now turning my attention to (/)/2. SWI-Prolog wants me
to use set_prolog_flag(iso, true), to get the semantics
of the ISO core standard, page 116.
But the fuzzer still finds the needle in the haystack.
Using more bits to the numerator, and I find:
/* Jekejeke Prolog 1.5.5 */
?- X is -15915286457581575168 / -238455449856.
X = 6.674322800000001E7.
/* SWI-Prolog 8.5.18 */
?- current_prolog_flag(iso, X).
X = true.
?- X is -15915286457581575168 / -238455449856.
X = 66743227.99999999.
Whats the Novacore bigint versus float mantra? The difference
between the two notions “less precise and more speed” versus
“more precise and less speed” can be maybe capture as follows:
- Less Precise and More Speed: Even if the arguments are bigint, and
have potentially more than 52 bit mantissa, they are always first
converted to float. So that the operation is only performed with 52
bit arguments, allow for faster processing.
- More Precise and Less Speed: If the arguments are bigint or if
the arguments are rational numbers or arbitrary floats, all bits are
taken into account. So that the operation is performed with more bits
than only 52 bits, resulting in slower processing.
Whats the Novacore bigint versus float mantra? The difference
between the two notions “less precise and more speed” versus
“more precise and less speed” can be maybe capture as follows:
- Less Precise and More Speed: Even if the arguments are bigint, and
have potentially more than 52 bit mantissa, they are always first
converted to float. So that the operation is only performed with 52
bit arguments, allow for faster processing.
- More Precise and Less Speed: If the arguments are bigint or if
the arguments are rational numbers or arbitrary floats, all bits are
taken into account. So that the operation is performed with more bits
than only 52 bits, resulting in slower processing.
Ok did some performance measurement. Basically used this harness.
Was reusing the test data, that was used for accuracy testing, so its
based on the same cases.p file:
% swi_perf
swi_perf :-
write('case, swi: '), time((between(1,20000,_),
case(_, X, Y), Y =\= float(X), fail; true)),
write('case2, swi: '), time((between(1,20000,_),
case2(_, X, Y), Y =\= float(X), fail; true)),
write('case3, swi: '), time((between(1,20000,_),
case3(_, X, Y), Y =\= float(X), fail; true)),
write('case4, swi: '), time((between(1,20000,_),
case4(_, P, Q, Y), Y =\= P/Q, fail; true)),
write('case5, swi: '), time((between(1,20000,_),
case5(_, P, Q, Y), Y =\= P/Q, fail; true)),
write('case6, swi: '), time((between(1,20000,_),
case6(_, P, Q, Y), Y =\= P**Q, fail; true)).
Mostowski Collapse schrieb am Mittwoch, 19. Oktober 2022 um 16:42:37 UTC+2:
Whats the Novacore bigint versus float mantra? The difference
between the two notions “less precise and more speed” versus
“more precise and less speed” can be maybe capture as follows:
- Less Precise and More Speed: Even if the arguments are bigint, and
have potentially more than 52 bit mantissa, they are always first converted to float. So that the operation is only performed with 52
bit arguments, allow for faster processing.
- More Precise and Less Speed: If the arguments are bigint or if
the arguments are rational numbers or arbitrary floats, all bits are
taken into account. So that the operation is performed with more bits
than only 52 bits, resulting in slower processing.
Code inspection sometimes helps find defects:
/* Jekejeke Prolog Ok */
?- X is (2^53+1)*2^64+1, Y is float(X).
X = 166153499473114502559719956244594689, Y = 1.6615349947311452E35.
/* SWI-Prolog 8.5.20 Ok */
?- X is float(166153499473114502559719956244594689).
X = 1.6615349947311452e+35.
/* Ciao Prolog Nok */
?- X is float(166153499473114502559719956244594689).
X = 1.661534994731145e35 ?
Pitty my fuzzer didn't find it. But I guess I will manually add it.
Mostowski Collapse schrieb am Mittwoch, 19. Oktober 2022 um 16:52:41 UTC+2:
Woa! My Jekejeke Prolog (was using JDK 1.8) is quite a number cruncher,
on the other hand Dogelog Player (was using nodeJS) is lacking behind. Inbetween the result of Prolog systems that I tested, results in milliseconds.
System case case2 case3 case4 case5 case6
jekejeke 270 266 255 305 361 539
ciao 344 343 343 386 386 544
eclipse 414 388 396 518 541 939
trealla 553 547 549 569 565 605
swi 989 941 979 528 532 861
scryer 889 857 872 1177 1188 1147
sicstus 1087 1083 1104 1221 1218 1226
dogelog 1171 1163 1171 1241 1300 1330
Mostowski Collapse schrieb am Mittwoch, 19. Oktober 2022 um 16:51:45 UTC+2:
Ok did some performance measurement. Basically used this harness.
Was reusing the test data, that was used for accuracy testing, so its based on the same cases.p file:
% swi_perf
swi_perf :-
write('case, swi: '), time((between(1,20000,_),
case(_, X, Y), Y =\= float(X), fail; true)),
write('case2, swi: '), time((between(1,20000,_),
case2(_, X, Y), Y =\= float(X), fail; true)),
write('case3, swi: '), time((between(1,20000,_),
case3(_, X, Y), Y =\= float(X), fail; true)),
write('case4, swi: '), time((between(1,20000,_),
case4(_, P, Q, Y), Y =\= P/Q, fail; true)),
write('case5, swi: '), time((between(1,20000,_),
case5(_, P, Q, Y), Y =\= P/Q, fail; true)),
write('case6, swi: '), time((between(1,20000,_),
case6(_, P, Q, Y), Y =\= P**Q, fail; true)).
Mostowski Collapse schrieb am Mittwoch, 19. Oktober 2022 um 16:42:37 UTC+2:
Whats the Novacore bigint versus float mantra? The difference
between the two notions “less precise and more speed” versus “more precise and less speed” can be maybe capture as follows:
- Less Precise and More Speed: Even if the arguments are bigint, and have potentially more than 52 bit mantissa, they are always first converted to float. So that the operation is only performed with 52 bit arguments, allow for faster processing.
- More Precise and Less Speed: If the arguments are bigint or if
the arguments are rational numbers or arbitrary floats, all bits are taken into account. So that the operation is performed with more bits than only 52 bits, resulting in slower processing.
Woa! My Jekejeke Prolog (was using JDK 1.8) is quite a number cruncher,
on the other hand Dogelog Player (was using nodeJS) is lacking behind. Inbetween the result of Prolog systems that I tested, results in milliseconds.
System case case2 case3 case4 case5 case6
jekejeke 270 266 255 305 361 539
ciao 344 343 343 386 386 544
eclipse 414 388 396 518 541 939
trealla 553 547 549 569 565 605
swi 989 941 979 528 532 861
scryer 889 857 872 1177 1188 1147
sicstus 1087 1083 1104 1221 1218 1226
dogelog 1171 1163 1171 1241 1300 1330
Mostowski Collapse schrieb am Mittwoch, 19. Oktober 2022 um 16:51:45 UTC+2:
Ok did some performance measurement. Basically used this harness.
Was reusing the test data, that was used for accuracy testing, so its based on the same cases.p file:
% swi_perf
swi_perf :-
write('case, swi: '), time((between(1,20000,_),
case(_, X, Y), Y =\= float(X), fail; true)),
write('case2, swi: '), time((between(1,20000,_),
case2(_, X, Y), Y =\= float(X), fail; true)),
write('case3, swi: '), time((between(1,20000,_),
case3(_, X, Y), Y =\= float(X), fail; true)),
write('case4, swi: '), time((between(1,20000,_),
case4(_, P, Q, Y), Y =\= P/Q, fail; true)),
write('case5, swi: '), time((between(1,20000,_),
case5(_, P, Q, Y), Y =\= P/Q, fail; true)),
write('case6, swi: '), time((between(1,20000,_),
case6(_, P, Q, Y), Y =\= P**Q, fail; true)).
Mostowski Collapse schrieb am Mittwoch, 19. Oktober 2022 um 16:42:37 UTC+2:
Whats the Novacore bigint versus float mantra? The difference
between the two notions “less precise and more speed” versus
“more precise and less speed” can be maybe capture as follows:
- Less Precise and More Speed: Even if the arguments are bigint, and have potentially more than 52 bit mantissa, they are always first converted to float. So that the operation is only performed with 52
bit arguments, allow for faster processing.
- More Precise and Less Speed: If the arguments are bigint or if
the arguments are rational numbers or arbitrary floats, all bits are taken into account. So that the operation is performed with more bits than only 52 bits, resulting in slower processing.
Explanation of the defect:
Mostly based on code from Torbjorn Granlund, Johan Andersson,
and Mats Carlsson (Ref: Knuth vol. 2 sec. 4.3.1)
flt64_t bn_to_float(bignum_t *bn) {
while (i > 1) {
f = f*norm2 + u;
https://github.com/ciao -lang/ciao/blob/master/core/engine/eng_bignum.c#L1043
I think a defect could be if HALF_EVEN prematurely
rounds down the value of f, and then later a value of u,
would indicate that this rounding down was not legit,
but float addition cannot help it.
Mostowski Collapse schrieb am Donnerstag, 20. Oktober 2022 um 03:55:47 UTC+2:
Code inspection sometimes helps find defects:
/* Jekejeke Prolog Ok */
?- X is (2^53+1)*2^64+1, Y is float(X).
X = 166153499473114502559719956244594689, Y = 1.6615349947311452E35.
/* SWI-Prolog 8.5.20 Ok */
?- X is float(166153499473114502559719956244594689).
X = 1.6615349947311452e+35.
/* Ciao Prolog Nok */
?- X is float(166153499473114502559719956244594689).
X = 1.661534994731145e35 ?
Pitty my fuzzer didn't find it. But I guess I will manually add it. Mostowski Collapse schrieb am Mittwoch, 19. Oktober 2022 um 16:52:41 UTC+2:
Woa! My Jekejeke Prolog (was using JDK 1.8) is quite a number cruncher, on the other hand Dogelog Player (was using nodeJS) is lacking behind. Inbetween the result of Prolog systems that I tested, results in milliseconds.
System case case2 case3 case4 case5 case6
jekejeke 270 266 255 305 361 539
ciao 344 343 343 386 386 544
eclipse 414 388 396 518 541 939
trealla 553 547 549 569 565 605
swi 989 941 979 528 532 861
scryer 889 857 872 1177 1188 1147
sicstus 1087 1083 1104 1221 1218 1226
dogelog 1171 1163 1171 1241 1300 1330
Mostowski Collapse schrieb am Mittwoch, 19. Oktober 2022 um 16:51:45 UTC+2:
Ok did some performance measurement. Basically used this harness.
Was reusing the test data, that was used for accuracy testing, so its based on the same cases.p file:
% swi_perf
swi_perf :-
write('case, swi: '), time((between(1,20000,_),
case(_, X, Y), Y =\= float(X), fail; true)),
write('case2, swi: '), time((between(1,20000,_),
case2(_, X, Y), Y =\= float(X), fail; true)),
write('case3, swi: '), time((between(1,20000,_),
case3(_, X, Y), Y =\= float(X), fail; true)),
write('case4, swi: '), time((between(1,20000,_),
case4(_, P, Q, Y), Y =\= P/Q, fail; true)),
write('case5, swi: '), time((between(1,20000,_),
case5(_, P, Q, Y), Y =\= P/Q, fail; true)),
write('case6, swi: '), time((between(1,20000,_),
case6(_, P, Q, Y), Y =\= P**Q, fail; true)).
Mostowski Collapse schrieb am Mittwoch, 19. Oktober 2022 um 16:42:37 UTC+2:
Whats the Novacore bigint versus float mantra? The difference between the two notions “less precise and more speed” versus “more precise and less speed” can be maybe capture as follows:
- Less Precise and More Speed: Even if the arguments are bigint, and have potentially more than 52 bit mantissa, they are always first converted to float. So that the operation is only performed with 52 bit arguments, allow for faster processing.
- More Precise and Less Speed: If the arguments are bigint or if
the arguments are rational numbers or arbitrary floats, all bits are taken into account. So that the operation is performed with more bits
than only 52 bits, resulting in slower processing.
Woa! My testing campaign has not yet ended. Now I
found a test case, where the Unix platform also fails.
Was increasing the bits in the case6/3 fuzzer:
pow2(B, E, X) :- E < 0, integer(E), !, X is 1/(B^(-E)).
pow2(B, E, X) :- X is B^E.
?- current_prolog_flag(iso, X).
X = false.
/* SWI-Prolog 8.5.20 WLS2 Nok */
?- repeat, L is -(1<<15), H is (1<<15)+1,
M is -(1<<3), J is (1<<3)+1, random(L, H, P),
random(M, J, Q), pow2(P, Q, A), B is P**Q, A=\=B.
P = -18573,
Q = -7,
A = -1.3116730299820406e-30,
B = -1.3116730299820408e-30 .
I have SWIPL on Unix platform only since weekend,
could not do this testing beforehand, so I guess I
need to update case6/3 test cases,
because they showed Unix platform completely
passing, which seems to be wrong. I used in the old
fuzzer 12 bits + 6 bits, the above uses 15 bits + 3 bits,
and new nasty test cases pop up. Its a test case where
JDK 19 non-strict also fails. Cool! I couldn’t believe that
it is that good. It was too good to be true.
So its also not that good!
/* Jekejeke Prolog 1.5.5, JDK 19 non-strict Nok */
?- X is -18573** -7.
X = -1.3116730299820408E-30.
Mostowski Collapse schrieb am Dienstag, 25. Oktober 2022 um 14:56:11 UTC+2:
Woa! My testing campaign has not yet ended. Now I
found a test case, where the Unix platform also fails.
Was increasing the bits in the case6/3 fuzzer:
pow2(B, E, X) :- E < 0, integer(E), !, X is 1/(B^(-E)).
pow2(B, E, X) :- X is B^E.
?- current_prolog_flag(iso, X).
X = false.
/* SWI-Prolog 8.5.20 WLS2 Nok */
?- repeat, L is -(1<<15), H is (1<<15)+1,
M is -(1<<3), J is (1<<3)+1, random(L, H, P),
random(M, J, Q), pow2(P, Q, A), B is P**Q, A=\=B.
P = -18573,
Q = -7,
A = -1.3116730299820406e-30,
B = -1.3116730299820408e-30 .
Nope the JDK 19 is correct here. Bug in long division?
/* SWI-Prolog 8.5.20 WSL2 */
?- current_prolog_flag(iso, X).
X = false.
?- X is 1/(18573^7).
X = 1.3116730299820406e-30.
?- X is float(1 rdiv (18573^7)).
X = 1.3116730299820408e-30.
the Mostowski Collapse schrieb am Dienstag, 25. Oktober 2022 um 14:59:11 UTC+2:
I have SWIPL on Unix platform only since weekend,
could not do this testing beforehand, so I guess I
need to update case6/3 test cases,
because they showed Unix platform completely
passing, which seems to be wrong. I used in the old
fuzzer 12 bits + 6 bits, the above uses 15 bits + 3 bits,
and new nasty test cases pop up. Its a test case where
JDK 19 non-strict also fails. Cool! I couldn’t believe that
it is that good. It was too good to be true.
So its also not that good!
/* Jekejeke Prolog 1.5.5, JDK 19 non-strict Nok */
?- X is -18573** -7.
X = -1.3116730299820408E-30.
Mostowski Collapse schrieb am Dienstag, 25. Oktober 2022 um 14:56:11 UTC+2:
Woa! My testing campaign has not yet ended. Now I
found a test case, where the Unix platform also fails.
Was increasing the bits in the case6/3 fuzzer:
pow2(B, E, X) :- E < 0, integer(E), !, X is 1/(B^(-E)).
pow2(B, E, X) :- X is B^E.
?- current_prolog_flag(iso, X).
X = false.
/* SWI-Prolog 8.5.20 WLS2 Nok */
?- repeat, L is -(1<<15), H is (1<<15)+1,
M is -(1<<3), J is (1<<3)+1, random(L, H, P),
random(M, J, Q), pow2(P, Q, A), B is P**Q, A=\=B.
P = -18573,
Q = -7,
A = -1.3116730299820406e-30,
B = -1.3116730299820408e-30 .
Here is a trick to force Python compatibility mode, I guess
works for SWI-Prolog 8.5.20 which has improved rational
number rounding? Thats the incorrectly rounded result:
/* incorrectly rounded of the real number quotient */
?- X is 1267650600228229401496703205376 /
762385119722780192080867194597.
X = 1.662743103759914.
Now do switch on Python compatibility mode, and
watch the result getting correctly rounded:
/* Use this combo for Python compatibility:
prefer_rationals=true,
max_rational_size=0,
max_rational_size_action=float */
?- set_prolog_flag(prefer_rationals, true).
true.
?- set_prolog_flag(max_rational_size, 0).
true.
?- set_prolog_flag(max_rational_size_action, float).
true.
/* Correctly rounded of the real number quotient, via
detour over rational numbers, which are correctly
rounded since release 8.5.20 of SWI-Prolog? */
?- X is 1267650600228229401496703205376 /
762385119722780192080867194597.
X = 1.6627431037599143.
My latest take on msb/2, in case a Prolog system
doesn't have this built-in. Some hi lo search:
% msb(+Integer, -Integer)
msb(0, N) :- !, N = -1.
msb(X, N) :-
msb(X, 1, N).
% msb(+Integer, +Integer, -Integer)
msb(X, H, N) :-
X < (1<<H), !,
L is H>>1,
msb(X, L, H, N).
msb(X, H, N) :-
J is H<<1,
msb(X, J, N).
% msb(+Integer, +Integer, +Integer, -Integer)
msb(_, L, H, N) :- L+1 =:= H, !,
N = L.
msb(X, L, H, N) :- M is (L+H) >> 1,
X < (1<<M), !,
msb(X, L, M, N).
msb(X, L, H, N) :- M is (L+H) >> 1,
msb(X, M, H, N).
But it is not a replacement for the real thing.
An msb/1 evaluable function still performs better.
Scryer Prolog doesn't have these built-ins. So emulating
them makes it utterly slow. Using the hi lo approach for msb:
/* Scryer Prolog 0.9.1 WSL2 */
?- member((N,K),[(53,2600),(1024,150),(8192,9)]),
time((between(1,K,_), mp_pi(N, _), fail; true)), fail; true.
% CPU time: 4.240s
% CPU time: 5.621s
% CPU time: 7.718s
% true.
The best I can do for Dogelog Player, concerning
msb/1 and the JavaScript platform. Just imagine
the speed if JavaScript would have a real msb/1
or bitlength/1, so that we don't need this nonsense?
function bigint_msb(alpha) {
alpha = alpha.toString(16);
return 31 - Math.clz32(hexvalue(alpha.codePointAt(0))) + (alpha.length - 1)*4;
}
This is a known problem that bigint has no msb/1 or
bitlength/1 in JavaScript as of now. Nevertheless I now
easily beat Scryer Prolog even with Dogelog Player.
Comparing to the hi lo search msb I get these results:
/* Dogelog Player 1.0.3 Windows nodeJS */
?- member((N,K),[(53,2600),(1024,150),(8192,9)]),
time((between(1,K,_), mp_pi(N, _), fail; true)), fail; true.
% Wall 4136 ms, gc 10 ms, 2539128 lips
% Wall 4346 ms, gc 104 ms, 2219962 lips
% Wall 3876 ms, gc 88 ms, 1180529 lips
true.
Also unlike Scryer Prolog, for larger bigints, i.e. 8192
the timing doesn't go up, it rather goes down. Probably some
alternative bigint multiplication methods etc.. kick in for
larger bigints, and they are missing in Scryer Prolog for
the Rust based bigints? They are still in its fancy? Also
Scryer Prolog would fare better if it had a native msb/1.
Mostowski Collapse schrieb am Montag, 14. November 2022 um 23:31:28 UTC+1:
Scryer Prolog doesn't have these built-ins. So emulating
them makes it utterly slow. Using the hi lo approach for msb:
/* Scryer Prolog 0.9.1 WSL2 */
?- member((N,K),[(53,2600),(1024,150),(8192,9)]),
time((between(1,K,_), mp_pi(N, _), fail; true)), fail; true.
% CPU time: 4.240s
% CPU time: 5.621s
% CPU time: 7.718s
% true.
I guess its time to talk about DOM APIs inside Prolog,
and making them mature and portable. I am planning
to adopt the Tau Prolog DOM API.
But I have two DOM APIs in mind:
- Stateless DOM API: Basically Tau Prolog is a stateless
DOM API. All API calls have all the necessary parameters,
there is nothing implicit right?
- Statefull DOM API: On the other hand so far, in Dogelog
Player, I have used a statefull DOM API. With a notion
of cursor, you can imagine Turtle graphics, only the Turtle
is not a Turtle with location and orientation, but a node
somewhere in a DOM tree.
My plan is to give the Statefull DOM API a new try, but
bootstrapping it from the a Stateless DOM API. Eh voila
I am already banging my head:
DOM API: How get last child efficiently? https://github.com/tau-prolog/tau-prolog/issues/340
An other can of worms format/2, related to DOM generation.
The Prolog community could standardisize it voluntarily
without the need of the ISO body. Just have a common place,
where somebody puts up a kind of specification document.
Something like a format/2 Prolog Enhancement Proposals (PEP).
Why look into format/2 ? Because Tau Prolog suggests funny stuff:
add_item(Item, Count) :-
...
format(Stream, "~a (~d)", [Item, Count]),
...
Web development with Tau Prolog - Riazaa https://biblioteca.sistedes.es/submissions/descargas/2022/PROLE/2022-PROLE-006.pdf
Mostlikely the above is not a very optimal idea, since
there are so many other issues in DOM text generation,
like XML Escape or URL Encoding. But nevertheless
worth investigating as a means for DOM text generation.
A middle ground could be to standardisized only float formatting.
So that there are some primitives that do float formatting,
and various string interpolations and portraying could be
bootstrapped from it. I find some rudimentaries here from ROK:
float_codes(Float, Codes, Format) :-
% like number_codes/2 but only for floats http://www.cs.otago.ac.nz/staffpriv/ok/pllib.htm
So the standardisation would takle what the ‘%’ operator can
do in Python, when the left argument is a string and the right
argument is a float. But there is much to be demanded, what
if the right argument is an integer, especially a bigint and not
a smallint, a bigint that cannot be converted to float. So ROKs
take is a little outdated, since is not bigint aware.
Mostowski Collapse schrieb am Freitag, 13. Januar 2023 um 10:56:46 UTC+1:
An other can of worms format/2, related to DOM generation.
The Prolog community could standardisize it voluntarily
without the need of the ISO body. Just have a common place,
where somebody puts up a kind of specification document.
Something like a format/2 Prolog Enhancement Proposals (PEP).
Why look into format/2 ? Because Tau Prolog suggests funny stuff:
add_item(Item, Count) :-
...
format(Stream, "~a (~d)", [Item, Count]),
...
Web development with Tau Prolog - Riazaa https://biblioteca.sistedes.es/submissions/descargas/2022/PROLE/2022-PROLE-006.pdf
Mostlikely the above is not a very optimal idea, since
there are so many other issues in DOM text generation,
like XML Escape or URL Encoding. But nevertheless
worth investigating as a means for DOM text generation.
A middle ground could be to standardisized only float formatting.
So that there are some primitives that do float formatting,
and various string interpolations and portraying could be
bootstrapped from it. I find some rudimentaries here from ROK:
float_codes(Float, Codes, Format) :-
% like number_codes/2 but only for floats http://www.cs.otago.ac.nz/staffpriv/ok/pllib.htm
So the standardisation would takle what the ‘%’ operator can
do in Python, when the left argument is a string and the right
argument is a float. But there is much to be demanded, what
if the right argument is an integer, especially a bigint and not
a smallint, a bigint that cannot be converted to float. So ROKs
take is a little outdated, since is not bigint aware.
Mostowski Collapse schrieb am Freitag, 13. Januar 2023 um 10:56:46 UTC+1:
An other can of worms format/2, related to DOM generation.
The Prolog community could standardisize it voluntarily
without the need of the ISO body. Just have a common place,
where somebody puts up a kind of specification document.
Something like a format/2 Prolog Enhancement Proposals (PEP).
Why look into format/2 ? Because Tau Prolog suggests funny stuff:
add_item(Item, Count) :-
...
format(Stream, "~a (~d)", [Item, Count]),
...
Web development with Tau Prolog - Riazaa https://biblioteca.sistedes.es/submissions/descargas/2022/PROLE/2022-PROLE-006.pdf
Mostlikely the above is not a very optimal idea, since
there are so many other issues in DOM text generation,
like XML Escape or URL Encoding. But nevertheless
worth investigating as a means for DOM text generation.
My conclusion, to reach the level of SWI-Prolog,
a number_codes with a format parameters is needed, and
not a float_codes that is restricted to floats.
With a number_codes that also accepts integers, it will go
smooth to also format integers, as SWI-Prolog does.
On my side I started defining a new built-in:
atom_number(-Atom, +Atom, +Integer, +Number)
The above built-in takes a slightly different turn, not codes
but atom is the currency for number conversion. The
input atom is ‘f’ or ‘e’, and the input integer is the requested
precision. But it is currently too stupid for bigint, working on it. Mostowski Collapse schrieb am Freitag, 13. Januar 2023 um 10:58:12 UTC+1:
A middle ground could be to standardisized only float formatting.
So that there are some primitives that do float formatting,
and various string interpolations and portraying could be
bootstrapped from it. I find some rudimentaries here from ROK:
float_codes(Float, Codes, Format) :-
% like number_codes/2 but only for floats http://www.cs.otago.ac.nz/staffpriv/ok/pllib.htm
So the standardisation would takle what the ‘%’ operator can
do in Python, when the left argument is a string and the right
argument is a float. But there is much to be demanded, what
if the right argument is an integer, especially a bigint and not
a smallint, a bigint that cannot be converted to float. So ROKs
take is a little outdated, since is not bigint aware.
Mostowski Collapse schrieb am Freitag, 13. Januar 2023 um 10:56:46 UTC+1:
An other can of worms format/2, related to DOM generation.
The Prolog community could standardisize it voluntarily
without the need of the ISO body. Just have a common place,
where somebody puts up a kind of specification document.
Something like a format/2 Prolog Enhancement Proposals (PEP).
Why look into format/2 ? Because Tau Prolog suggests funny stuff:
add_item(Item, Count) :-
...
format(Stream, "~a (~d)", [Item, Count]),
...
Web development with Tau Prolog - Riazaa https://biblioteca.sistedes.es/submissions/descargas/2022/PROLE/2022-PROLE-006.pdf
Mostlikely the above is not a very optimal idea, since
there are so many other issues in DOM text generation,
like XML Escape or URL Encoding. But nevertheless
worth investigating as a means for DOM text generation.
PEPs are probably anyway a dead end. Best would be if Prolog
systems were designed around a Novacore. So atom_number/4
would probably fit into a **Novacore**, but format/[2,3] not.
format/[2,3] would have a pure Prolog implementation, that
can be shared across Prolog systems. Same for locales, a pure
Prolog implementation, or some Semantic Net from ChatGPT.
LoL
Mostowski Collapse schrieb am Freitag, 13. Januar 2023 um 11:01:03 UTC+1:
My conclusion, to reach the level of SWI-Prolog,
a number_codes with a format parameters is needed, and
not a float_codes that is restricted to floats.
With a number_codes that also accepts integers, it will go
smooth to also format integers, as SWI-Prolog does.
On my side I started defining a new built-in:
atom_number(-Atom, +Atom, +Integer, +Number)
The above built-in takes a slightly different turn, not codes
but atom is the currency for number conversion. The
input atom is ‘f’ or ‘e’, and the input integer is the requested
precision. But it is currently too stupid for bigint, working on it. Mostowski Collapse schrieb am Freitag, 13. Januar 2023 um 10:58:12 UTC+1:
A middle ground could be to standardisized only float formatting.
So that there are some primitives that do float formatting,
and various string interpolations and portraying could be
bootstrapped from it. I find some rudimentaries here from ROK:
float_codes(Float, Codes, Format) :-
% like number_codes/2 but only for floats http://www.cs.otago.ac.nz/staffpriv/ok/pllib.htm
So the standardisation would takle what the ‘%’ operator can
do in Python, when the left argument is a string and the right
argument is a float. But there is much to be demanded, what
if the right argument is an integer, especially a bigint and not
a smallint, a bigint that cannot be converted to float. So ROKs
take is a little outdated, since is not bigint aware.
Mostowski Collapse schrieb am Freitag, 13. Januar 2023 um 10:56:46 UTC+1:
An other can of worms format/2, related to DOM generation.
The Prolog community could standardisize it voluntarily
without the need of the ISO body. Just have a common place,
where somebody puts up a kind of specification document.
Something like a format/2 Prolog Enhancement Proposals (PEP).
Why look into format/2 ? Because Tau Prolog suggests funny stuff:
add_item(Item, Count) :-
...
format(Stream, "~a (~d)", [Item, Count]),
...
Web development with Tau Prolog - Riazaa https://biblioteca.sistedes.es/submissions/descargas/2022/PROLE/2022-PROLE-006.pdf
Mostlikely the above is not a very optimal idea, since
there are so many other issues in DOM text generation,
like XML Escape or URL Encoding. But nevertheless
worth investigating as a means for DOM text generation.
Thats a defect of Markus Triskas library(format).
The defect is found in Scryer Prolog:
?- format("foo ~w bar ~w baz", [p(A,B),q(B,C)]), nl.
foo p(A,B) bar q(B,C) baz
?- format("foo ~w bar", [p(A,B)]), format(" ~w baz", [q(B,C)]), nl.
foo p(A,B) bar q(A,B) baz
One should be able to chop up format/[2,3]. Other
Prolog systems are able to do that. Either with small
variable gensym or with large variable gensym:
/* Trealla, Jekejeke etc.. --> small variable gensym */
?- format('foo ~w bar ~w baz', [p(A,B),q(B,C)]), nl.
foo p(_0, _1) bar q(_1, _2) baz
?- format('foo ~w bar', [p(A,B)]), format(' ~w baz', [q(B,C)]), nl.
foo p(_0, _1) bar q(_1, _2) baz
/* SWI-Prolog, Dogelog etc.. --> large variable gensym */
?- format('foo ~w bar ~w baz', [p(A,B),q(B,C)]), nl.
foo p(_23041, _23042) bar q(_23042, _23043) baz
?- format('foo ~w bar', [p(A,B)]), format(' ~w baz', [q(B,C)]), nl.
foo p(_25699, _25700) bar q(_25700, _25701) baz
I don't know whether Trealla and Jekejeke use the
same gensym strategy, but phenotype is similar.
Same for the genotype of SWI-Prolog and Dogelog,
mostlikely not the same realization under the hood,
but similar outcome again.
It seems that Tau Prolog has a hickup somewhere:
?- format("foo ~w bar ~w baz", [p(A,B),q(B,C)]), nl.
uncaught exception: error(instantiation_error,fabricate_var_name/3)
https://github.com/tau-prolog/tau-prolog/issues/342
Mostowski Collapse schrieb am Dienstag, 17. Januar 2023 um 00:03:10 UTC+1:
Thats a defect of Markus Triskas library(format).
The defect is found in Scryer Prolog:
?- format("foo ~w bar ~w baz", [p(A,B),q(B,C)]), nl.
foo p(A,B) bar q(B,C) baz
?- format("foo ~w bar", [p(A,B)]), format(" ~w baz", [q(B,C)]), nl.
foo p(A,B) bar q(A,B) baz
One should be able to chop up format/[2,3]. Other
Prolog systems are able to do that. Either with small
variable gensym or with large variable gensym:
/* Trealla, Jekejeke etc.. --> small variable gensym */
?- format('foo ~w bar ~w baz', [p(A,B),q(B,C)]), nl.
foo p(_0, _1) bar q(_1, _2) baz
?- format('foo ~w bar', [p(A,B)]), format(' ~w baz', [q(B,C)]), nl.
foo p(_0, _1) bar q(_1, _2) baz
/* SWI-Prolog, Dogelog etc.. --> large variable gensym */
?- format('foo ~w bar ~w baz', [p(A,B),q(B,C)]), nl.
foo p(_23041, _23042) bar q(_23042, _23043) baz
?- format('foo ~w bar', [p(A,B)]), format(' ~w baz', [q(B,C)]), nl.
foo p(_25699, _25700) bar q(_25700, _25701) baz
I don't know whether Trealla and Jekejeke use the
same gensym strategy, but phenotype is similar.
Same for the genotype of SWI-Prolog and Dogelog,
mostlikely not the same realization under the hood,
but similar outcome again.
Interesting find, hooking into Java printf gives:
?- catch(printf('abc %s def', []), error(E,_), true).
Error: Unknown template: representation_error('Format specifier ''%s''')
/* java.util.MissingFormatArgumentException extends IllegalFormatException */
?- catch(printf('abc %s def', [p,q]), error(E,_), true).
abc p def
So too many arguments are tolerated. On the other
hand SWI-Prolog does check too many arguments:
?- catch(format('abc ~w def', []), error(E,_), true).
E = format('not enough arguments').
?- catch(format('abc ~w def', [p,q]), error(E,_), true).
E = format('too many arguments').
Oki Doki
Mostowski Collapse schrieb am Dienstag, 17. Januar 2023 um 00:07:06 UTC+1:
It seems that Tau Prolog has a hickup somewhere:
?- format("foo ~w bar ~w baz", [p(A,B),q(B,C)]), nl.
uncaught exception: error(instantiation_error,fabricate_var_name/3)
https://github.com/tau-prolog/tau-prolog/issues/342
Mostowski Collapse schrieb am Dienstag, 17. Januar 2023 um 00:03:10 UTC+1:
Thats a defect of Markus Triskas library(format).
The defect is found in Scryer Prolog:
?- format("foo ~w bar ~w baz", [p(A,B),q(B,C)]), nl.
foo p(A,B) bar q(B,C) baz
?- format("foo ~w bar", [p(A,B)]), format(" ~w baz", [q(B,C)]), nl.
foo p(A,B) bar q(A,B) baz
One should be able to chop up format/[2,3]. Other
Prolog systems are able to do that. Either with small
variable gensym or with large variable gensym:
/* Trealla, Jekejeke etc.. --> small variable gensym */
?- format('foo ~w bar ~w baz', [p(A,B),q(B,C)]), nl.
foo p(_0, _1) bar q(_1, _2) baz
?- format('foo ~w bar', [p(A,B)]), format(' ~w baz', [q(B,C)]), nl.
foo p(_0, _1) bar q(_1, _2) baz
/* SWI-Prolog, Dogelog etc.. --> large variable gensym */
?- format('foo ~w bar ~w baz', [p(A,B),q(B,C)]), nl.
foo p(_23041, _23042) bar q(_23042, _23043) baz
?- format('foo ~w bar', [p(A,B)]), format(' ~w baz', [q(B,C)]), nl.
foo p(_25699, _25700) bar q(_25700, _25701) baz
I don't know whether Trealla and Jekejeke use the
same gensym strategy, but phenotype is similar.
Same for the genotype of SWI-Prolog and Dogelog,
mostlikely not the same realization under the hood,
but similar outcome again.
Could a Novacore also be a Worker Prolog? One coul
imagine a Prolog system where all predicates are by
default thread local, and where threads can only
share information through message passing. You would
more or less get JavaScript Workers. Such a Prolog system
would not anymore need synchronization of predicates,
neither needed for static nor dynamic predicates. I am
currently wondering whether I can build such a Prolog variant
and what the performance would be? But a special form of
thread local would be needed, since the predicate needs not be
visible among multiple workers.
If you have fibers, you also don’t need any locking. At least not for basic operations like assert/1, retract/1, etc… since they still run single
threaded if you don’t yield during these operations. But on
the other hand try this in the current SWI-Prolog WASM shell:
?- call_with_time_limit(0.5, (repeat, fail)).
ERROR: Unhandled exception: toplevel: Unknown procedure: call_with_time_limit/2 (DWIM could not correct goal) https://dev.swi-prolog.org/wasm/shell
Surprisingly, since this weekend, I can do the following
in Dogelog Player for Python, the predicate time_out/2 accepts
milliseconds and has the arguments in different order:
?- time_out((repeat, fail), 500).
Error: system_error(timelimit_exceeded)
user:1
And I didn’t use threads, so how did I do it? BTW: I don’t know
whether Tau Prolog can demonstrate it. They have demonstrated
something else, multiple Prolog threads trying to reach a finish line.
I am not yet there at the Tau Prolog calibre of fibers, step by step wondering currently how signal handling can be done.
Mostowski Collapse schrieb am Montag, 27. Februar 2023 um 12:09:01 UTC+1:
A bonus would be if the Prolog system had event
loops in its Workers by default, so that kind of fibers
would be available, and one would still have some benefits
of a multi-threaded Prolog system, just do it async
in the same thread. Maybe a couple service threads besides
the workers threads nevertheless, so that for example
things like call_with_time_limit/2 still work. Maybe such a
Worker Prolog could be bootstrapped from a multi-threaded
Prolog system by changing some defaults, for example
that predicates are by default this new form of thread local.
Mostowski Collapse schrieb am Montag, 27. Februar 2023 um 12:08:12 UTC+1:
Could a Novacore also be a Worker Prolog? One coul
imagine a Prolog system where all predicates are by
default thread local, and where threads can only
share information through message passing. You would
more or less get JavaScript Workers. Such a Prolog system
would not anymore need synchronization of predicates,
neither needed for static nor dynamic predicates. I am
currently wondering whether I can build such a Prolog variant
and what the performance would be? But a special form of
thread local would be needed, since the predicate needs not be
visible among multiple workers.
A bonus would be if the Prolog system had event
loops in its Workers by default, so that kind of fibers
would be available, and one would still have some benefits
of a multi-threaded Prolog system, just do it async
in the same thread. Maybe a couple service threads besides
the workers threads nevertheless, so that for example
things like call_with_time_limit/2 still work. Maybe such a
Worker Prolog could be bootstrapped from a multi-threaded
Prolog system by changing some defaults, for example
that predicates are by default this new form of thread local.
Mostowski Collapse schrieb am Montag, 27. Februar 2023 um 12:08:12 UTC+1:
Could a Novacore also be a Worker Prolog? One coul
imagine a Prolog system where all predicates are by
default thread local, and where threads can only
share information through message passing. You would
more or less get JavaScript Workers. Such a Prolog system
would not anymore need synchronization of predicates,
neither needed for static nor dynamic predicates. I am
currently wondering whether I can build such a Prolog variant
and what the performance would be? But a special form of
thread local would be needed, since the predicate needs not be
visible among multiple workers.
Relatively straight forward using Python fibers. Only they are not
called fibers, the are call coroutines. For example the new
built-in to schedule an alarm is implemented as follows in Python:
loop = asyncio.get_running_loop()
res = loop.call_later(delay/1000.0, alarm_abort)
The same works also for JavaScript now, using an API with some
different names, but also based on an event loop I guess. Thats
the magic of fibers, they are inbetween single threaded and
multi-threaded. The alarm_abort callback in the above posts a
signal, which gets taken note by the auto-yielding interpreter. The behaviour is very similar to a multi-threaded Prolog alarm,
or a single-threaded Prolog alarm that uses some operating
system service for the signalling.
Mostowski Collapse schrieb am Montag, 27. Februar 2023 um 19:31:36 UTC+1:
If you have fibers, you also don’t need any locking. At least not for basic operations like assert/1, retract/1, etc… since they still run single
threaded if you don’t yield during these operations. But on
the other hand try this in the current SWI-Prolog WASM shell:
?- call_with_time_limit(0.5, (repeat, fail)).
ERROR: Unhandled exception: toplevel: Unknown procedure: call_with_time_limit/2 (DWIM could not correct goal) https://dev.swi-prolog.org/wasm/shell
Surprisingly, since this weekend, I can do the following
in Dogelog Player for Python, the predicate time_out/2 accepts milliseconds and has the arguments in different order:
?- time_out((repeat, fail), 500).
Error: system_error(timelimit_exceeded)
user:1
And I didn’t use threads, so how did I do it? BTW: I don’t know whether Tau Prolog can demonstrate it. They have demonstrated
something else, multiple Prolog threads trying to reach a finish line.
I am not yet there at the Tau Prolog calibre of fibers, step by step wondering currently how signal handling can be done.
Mostowski Collapse schrieb am Montag, 27. Februar 2023 um 12:09:01 UTC+1:
A bonus would be if the Prolog system had event
loops in its Workers by default, so that kind of fibers
would be available, and one would still have some benefits
of a multi-threaded Prolog system, just do it async
in the same thread. Maybe a couple service threads besides
the workers threads nevertheless, so that for example
things like call_with_time_limit/2 still work. Maybe such a
Worker Prolog could be bootstrapped from a multi-threaded
Prolog system by changing some defaults, for example
that predicates are by default this new form of thread local.
Mostowski Collapse schrieb am Montag, 27. Februar 2023 um 12:08:12 UTC+1:
Could a Novacore also be a Worker Prolog? One coul
imagine a Prolog system where all predicates are by
default thread local, and where threads can only
share information through message passing. You would
more or less get JavaScript Workers. Such a Prolog system
would not anymore need synchronization of predicates,
neither needed for static nor dynamic predicates. I am
currently wondering whether I can build such a Prolog variant
and what the performance would be? But a special form of
thread local would be needed, since the predicate needs not be
visible among multiple workers.
Are lazy streams fibers? Not really.
If you preform engine yield, you just land in the parent
context of the current execution. You don’t temporarily
suspend the current execution. You need an event loop
for that. But all the papers by Tarau, Wielemaker and
Schrijvers never discuss that. You could also imagine a
new Prolog language, with two new operators async/1
and await/1. For example one could write a bankteller
web worker only with fibers, and one could
produce code as follows:
:- async withdraw/2.
withdraw(Amount) :-
await do_something,
retact(account(Current)),
Current2 is Curent-Amount,
assertz(account(Current2)),
await do_something2.
Since the retract/assertz combo is not interleaved with an
await statement, we know that noting will yield while executing
the combo, and the effect is as if it were a critical region. But
locks or atomics are not needed. You have to switch off auto-
yielding for such a code as above, no auto-yielding is also the
default execution mode of JavaScript, and you could apply
the same reasoning to JavaScript.
Multi-Threaded Prolog ----\
Single-Threaded Prolog ---+-----> Novacore
Worker Fiber Prolog ------/
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 ------/
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 ------/
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 ------/
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.
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.
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
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.
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?
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.
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.
Sysop: | Keyop |
---|---|
Location: | Huddersfield, West Yorkshire, UK |
Users: | 302 |
Nodes: | 16 (2 / 14) |
Uptime: | 78:55:34 |
Calls: | 6,762 |
Files: | 12,289 |
Messages: | 5,378,140 |