Trealla Prolog ============== A compact, efficient ISO Prolog interpreter. Written in plain old C and using a plain old Makefile. MIT licensed Runs on Linux, Android, MacOS, FreeBSD, OpenBSD, NetBSD, RISC/OS, Haiku, OpenIndiana & Solaris Integers & Rationals are unbounded Atoms and strings are UTF-8 of unlimited length The default double-quoted representation is *chars* list Strings & slices are efficient (especially with mmap'd files) Rational trees ##EXPERIMENTAL## REPL with history Builds for Cosmopolitan, WebAssembly (WASI) & Go API for calling from C (or by using WASM from Go & JS) Foreign function interface (FFI) for calling out to user code Access SQLITE databases using builtin module (uses FFI) FFIs for GNU Scientific Library (GSL) ##EXPERIMENTAL## FFIs for Raylib & Raymath Concurrency via threads / tasks / futures / engines (aka. generators) Definite Clause Grammar (DCGs) Attributed variables with freeze/2, dif/2 & when/2 Constraints: CLP(B) & CLP(Z) Blackboarding primitives Delimited continuations Thread-local variant tabling Socket(s) library ... Windows build (your mileage may vary) ##UNSUPPORTED## Available from: [https://github.com/trealla-prolog/trealla](https://github.com/trealla-prolog/trealla). Runs with [Jupyter Notebooks](https://github.com/LogtalkDotOrg/logtalk-jupyter-kernel#readme). Logo ==== ![Trealla Logo: Trealla](trealla.png) Usage ===== tpl [options] [files] [-- args] where options can be: -O0, --noopt - no optimization -f - *.tplrc* not loaded -l file - load file file - load file -g goal - query goal (only used once) --library path - alt to TPL_LIBRARY_PATH env var -t, --trace - trace -q, --quiet - quiet mode (no banner) -v, --version - version -h, --help - help -d, --daemonize - daemonize -w, --watchdog - create watchdog --autofail - autofail queries at the toplevel --consult - consult from STDIN --nolimit - no memory limit --index-check - verify indexed lookups against a linear scan (debug, slow) For example: tpl -g test2,halt samples/sieve Invocation without any goal presents the REPL. The default path to the library is relative to the executable location. The file *~/.tplrc* is consulted on startup unless the *-f* option is present. When consulting, reconsulting and deconsulting files the *.pl* version of the filename is always preferred (if not specified) when looking for a file. Building ======== Written in plain-old C99. git clone https://github.com/trealla-prolog/trealla.git cd trealla On Debian-like systems, you will need to install (if not alread( the following packages to set up a build environment: sudo apt install build-essential git libedit-dev libffi-dev libssl-dev Then... make To build without FFI: make NOFFI=1 To build without SSL: make NOSSL=1 To build without pre-emptive multi-threading support: make NOTHREADS=1 To build (as a last resort) with the included ISOCLINE sources (default is to use EDITLINE, except with WASI & Windows): make ISOCLINE=1 Older compilers may require: make NOPEDANTIC=1 to avoid issues with newer flags. Finally... make install to install locally. Optionally... make test and there should be no errors. Further to test with `valgrind` (on Linux): make clean && make debug && make valgrind or more thoroughly (on MacOS): make clean && make sanitize && make test Should ideally show no memory out-of-bounds, null-pointer, use after free or memory leaks (there may a few spurious errors). On macOS: brew install libffi openssl coreutils By default `editline` is used on `'nix` systems, however if using GNU readline instead (make READLINE=1) install the BREW version of readline. Building with Cosmopolitan ========================== make cosmo Building with MUSL ================== On Ubuntu: sudo apt install musl-tools make CC=musl-gcc OPT=-static NOFFI=1 NOSSL=1 ISOCLINE=1 WebAssembly (WASI) ================== Trealla has support for WebAssembly System Interface (WASI). For an easy build envrionment, set up [wasi-sdk](https://github.com/WebAssembly/wasi-sdk). [Binaryen](https://github.com/WebAssembly/binaryen) is needed for optimization. To build the WebAssembinary binary, set CC to wasi-sdk's clang: make CC=/opt/wasi-sdk/bin/clang wasm Setting WASI_CC also works as an alternative to CC. Cross-compile for Windows x64 ============================= To cross-compile on Linux and produce a Windows/x86-64 executable... sudo apt install mingw-w64 make WIN=1 NOFFI=1 NOSSL=1 ```console $ file tpl.exe tpl.exe: PE32+ executable (console) x86-64, for MS Windows ``` Some have reported success with a native Windows build using msys2. Cross-compile for Linux x86 =========================== To cross-compile on Linux and produce a Linux/x86-32 executable... sudo apt install gcc-multilib sudo apt install libssl-dev:i386 libffi-dev:i386 libreadline-dev:i386 make OPT=-m32 ```console $ file tpl tpl: ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, BuildID[sha1]=31f643d7a4cfacb0a34e81b7c12c78410493de60, for GNU/Linux 3.2.0, with debug_info, not stripped ``` Contributions ============= Contributions are welcome. Acknowledgements ================ This project (in current incarnation) started in March 2020 and it would not be where it is today without help from these people: - [Xin Wang](https://github.com/dram) - [Paulo Moura](https://github.com/pmoura) - [Markus Triska](https://github.com/triska) - [Jos De Roo](https://github.com/josd) - [Ulrich Neumerkel](https://github.com/uwn) - [Guregu](https://github.com/guregu) Unbounded integers (Bigints) and Rationals ========================================== For unbounded arithmetic Trealla uses a modified fork of the [imath](https://github.com/infradig/imath) library, which is partially included in the source. Note, unbounded integers (aka. bigints) are for arithmetic purposes only and will give a type_error when used in places not expected. The *imath* library has a bug whereby printing large numbers becomes exponentially slower (100K+ digits). Strings ======= Double-quoted strings, when *set_prolog_flag(double_quotes,chars)* is set (which is the default) are stored as packed UTF-8 byte arrays. This is compact and efficient. Such strings emulate a list representation and from the programmer point of view are very much indistinguishable from lists. A good use of such strings is *open(filename,read,Str,[mmap(Ls))* which gives a memory-mapped view of a file as a string *Ls*. List operations on files are now essentially zero-overhead! DCG applications will gain greatly (*phrase_from_file/[2-3]* uses this). Both strings and atoms make use of low-overhead reflist-counted byte slices where appropriate. Non-standard predicates ======================= help/0 help/1 # help(+functor) or help(+PI) help/2 # help(+PI,+atom) where *atom* can be *trealla*, *swi* or *tau* module_help/1 # help(+module) module_help/2 # help(+module,+functor) or help(+module,+PI) module_help/3 # help(+module,+PI,+atom) where *atom* can bw *trrealla*, *swi* or *tau* source_info/2 # source_info(+PI, -list) module_info/2 # module_info(+atom, -list) module/1 # module(?atom) modules/1 # modules(-list) load_text/2 # load_text(+atom,+opts) listing/0 listing/1 # listing(+PI) abolish/2 # abolish(+pi,+list) pretty/1 # pretty-print version of listing/1 between/3 msort/2 # version of sort/3 with duplicates samsort/2 # same as msort/2 merge/3 format/[1-3] portray_clause/[1-2] predicate_property/2 evaluable_property/2 numbervars/[1,3-4] e/0 name/2 tab/[1,2] get_unbuffered_code/1 # read a single unbuffered code get_unbuffered_char/1 # read a single unbuffered character read_from_atom/2 # read_from_atom(+atom,?term) read_from_chars/2 # read_from_chars(+chars,?term) read_term_from_atom/3 # read_term_from_atom(+atom,?term,+optlist) read_term_from_chars/3 # read_term_from_chars(+chars,?term,+optlist) read_from_chars_/3 # read_from_chars+(?term,+chars,-rest) read_term_from_chars_/4 # read_term_from_chars+(?term,+optlist,+chars,-rest) write_term_to_atom/3 # write_term_to_atom(?atom,?term,+oplist) write_canonical_to_atom/3 # write_canonical_to_atom(?atom,?term,+oplist) term_to_atom/2 # term_to_atom(?atom,?term) setrand/1 # set_seed(+integer) set random number seed srandom/1 # set_seed(+integer) set random number seed set_seed/1 # set_seed(+integer) set random number seed get_seed/1 # get_seed(-integer) get random number seed rand/1 # rand(-integer) integer [0,RAND_MAX] random/1 # random(-float) float [0.0,<1.0] random_between/3 # random_between(+int,+int,-int) integer [arg1,)/2 # soft-cut call_det/2 # call_det(+call,?boolean) copy_term_nat/2 # doesn't copy attrs (same as copy_term/2) copy_term_with_attributes/2 # does copy attrs (opposite to copy_term/2) unifiable/3 # unifiable(+term1,+term2,-Goals) ?=/2 # ?=(+term1,+term2) term_expansion/2 goal_expansion/2 cyclic_term/1 term_singletons/2 findall/4 sort/4 ignore/1 is_list/1 is_partial_list/1 is_list_or_partial_list/1 is_stream/1 term_hash/2 term_hash/3 # ignores arg2 (options) time/1 inf/0 nan/0 \uXXXX and \UXXXXXXXX # Unicode escapes (for JSON) gcd/2 uuid/1 # uuid(-string) load_files/[1,2] module/1 line_count/2 atom_number/2 # *SWI-Prolog* compatible cfor/3 # cfor(+evaluable,+evaluable,-var) repeat/1 # repeat(+integer) make/0 argv/1 # argv(-list) raw_argv/1 # raw_argv(-list) rdiv/2 # evaluable numerator/1 # evaluable denominator/1 # evaluable rational/1 with_output_to(chars(Cs), Goal) # *SWI-Prolog* compatible with_output_to(string(Cs), Goal) # *SWI-Prolog* compatible with_output_to(atom(Atom), Goal) # *SWI-Prolog* compatible divmod/4 # *SWI-Prolog* compatible read_line_to_codes/2 # *SWI-Prolog* compatible read_line_to_codes/3 # *SWI-Prolog* compatible read_line_to_string/2 # *SWI-Prolog* compatible read_file_to_string/3 # *SWI-Prolog* compatible split_string/4 # *SWI-Prolog* compatible option/2-3 # *SWI-Prolog* compatible (see library(option)) findnsols/4 # *SWI-Prolog* compatible nb_setarg/3 # *SWI-Prolog* compatible (only with small integer values) writeln/1 # *SWI-Prolog* compatible writeln/2 # *SWI-Prolog* compatible call_nth/2 # *SWI-Prolog* compatible offset/2 # *SWI-Prolog* compatible limit/2 # *SWI-Prolog* compatible call_with_time_limit/2 # *SWI-Prolog* compatible time_out/3 # *SICStus Prolog* compatible getenv/2 setenv/2 unsetenv/1 directory_files/2 delete_file/1 exists_file/1 rename_file/2 copy_file/2 time_file/2 size_file/2 exists_directory/1 make_directory/1 make_directory_path/1 working_directory/2 chdir/1 absolute_file_name/[2,3] # expand(Bool) & relative_to(file) options is_absolute_file_name/1 access_file/2 set_stream/2 # only supports alias/1 & type/1 property alias/2 # alias(?integer,+atom) string_upper/2 string_lower/2 atom_upper/2 atom_lower/2 popcount/1 # function returning number of 1 bits lsb/1 # function returning the least significant bit of a positive integer (count from zero) msb/1 # function returning the most significant bit of a positive integer (count from zero) log10/1 # function returning log10 of arg now/0 # function returning Unix epoch in whole secs now/1 # now(-integer) Unix epoch in whole secs get_time/1 # get_time(-float) Unix epoch in secs wall_time/1 # wall_time(-float) elapsed clock time in secs cpu_time/1 # cpu_time(-float) elapsed CPU time in secs posix_strftime/3 # posix_strftime(+format,-string,+tm(NNN,...)) posix_strptime/3 # posix_strptime(+format,+string,-tm(NNN,...)) posix_mktime/2 # posix_mktime(+tm(NNN,...),-seconds) posix_gmtime/2 # posix_gmtime(+seconds,-tm(NNN,...)) posix_localtime/2 # posix_localtime(+seconds,-tm(NNN,...)) posix_ctime/2 # posix_time(+seconds,-atom) posix_time/1 # posix_time(-seconds) posix_getpid/1 # posix_pid(-pid) posix_getppid/1 # posix_ppid(-pid) posix_fork/1 # posix_fork(-pid) current_key/1 string_concat/3 # string_concat(+string,+string,?string) string_length/2 sleep/1 # sleep time in secs split/4 # split(+string,+sep,?left,?right) shell/1 shell/2 date_time/6 date_time/7 loadfile/2 # loadfile(+filename,-string) savefile/2 # savefile(+filename,+string) getfile/2 # getfile(+filename,-strings) getfile/3 # getfile(+filename,-strings,+opts) getline/1 # getline(-string) getline/2 # getline(+stream,-string) getline/3 # getline(+stream,-string,+opts) getlines/1 # getlines(-strings) getlines/2 # getlines(+stream,-strings) getlines/3 # getlines(+stream,-strings,+opts) open(stream(Str),...) # with open/4 reopen a stream open(F,M,S,[mmap(Ls)]) # with open/4 mmap() the file to Ls reset/3 # parser_reset(:goal,?ball,-cont) shift/1 # shift(+ball) term_variables/3 replace/4 # replace(+string,+old,+new,-string) Where `getlines/3` supports `terminator(+Bool)` to keep the line terminator or not (default). Also `empty(+Bool)` to end with the first empty line or not (default), this can be useful for loading a list of headers in an HTTP response. Note: consult/1 and load_files/2 support lists of files as args. Also support loading into modules eg. *consult(MOD:FILE-SPEC)*. Use these *POSIX* system calls for interprocess creation and communication... popen/3 # popen(+cmd,+mode,--stream) popen/4 # popen(+cmd,+mode,--stream,+opts) pclose/1 # pclose(+stream) For example... ```console tpl -g "popen('ps -a',read,S,[]),getlines(S,Ls),pclose(S),maplist(println,Ls),halt" PID TTY TIME CMD 2806 tty2 00:00:00 gnome-session-b 31645 pts/0 00:00:00 tpl 31646 pts/0 00:00:00 sh 31647 pts/0 00:00:00 ps ``` For general *POSIX* process creation use these *SWI-Prolog* compatible calls... process_create/3 # process_create(+cmd,+args,+opts) process_wait/3 # process_wait(+pid,-status,+opts) process_wait/2 # process_wait(+pid,-status) process_kill/2 # process_kill(+pid,+signal) process_kill/1 # process_kill(+pid) For example... ```console ?- process_create('ls',['-l'],[process(Pid)]),process_wait(Pid,_). total 2552 4 -rw-rw-r-- 1 andrew andrew 1813 Aug 25 10:18 ATTRIBUTION 4 -rw-rw-r-- 1 andrew andrew 1093 Aug 25 10:18 LICENSE 8 -rw-rw-r-- 1 andrew andrew 7259 Sep 18 18:27 Makefile 24 -rw-rw-r-- 1 andrew andrew 23709 Sep 19 08:56 README.md 4 -rw-rw-r-- 1 andrew andrew 28 Aug 25 10:18 _config.yml 4 drwxrwxr-x 2 andrew andrew 4096 Sep 17 10:41 docs 4 drwxrwxr-x 2 andrew andrew 4096 Sep 18 21:29 library 4 drwxrwxr-x 2 andrew andrew 4096 Sep 3 13:02 samples 4 drwxrwxr-x 6 andrew andrew 4096 Sep 19 09:38 src 4 drwxrwxr-x 5 andrew andrew 4096 Sep 14 20:49 tests 1448 -rwxrwxr-x 1 andrew andrew 1478712 Sep 19 09:38 tpl 8 -rw-rw-r-- 1 andrew andrew 7671 Aug 25 10:18 tpl.c 16 -rw-rw-r-- 1 andrew andrew 13928 Sep 18 18:28 tpl.o 36 -rw-rw-r-- 1 andrew andrew 33862 Aug 25 10:18 trealla.png Pid = 735602. ?- ``` Note: read_term/[2,3] supports the positions(Start,End) and the line_counts(Start,End) property options to report file information. This is analogous to stream_property/2 use of position(Pos) and line_count(Line) options. Note: read_term, write_term & friends support the *json(Boolean)* option to make more sympathetic support for JSON using the builtin parsing and printing mechanisms. Predicate reference =================== 594 predicates — 183 ISO, 62 evaluable. Generated by `util/gen_reference.py` from `help/0` in the built binary, so it cannot drift from the build. Regenerate on release rather than editing by hand. Jump to: [Core & terms](#core--terms) · [Control](#control) · [Arithmetic](#arithmetic) · [Streams & I/O](#streams--io) · [Formatting](#formatting) · [Database](#database) · [Maps](#maps) · [Attributed variables](#attributed-variables) · [Threads](#threads) · [Coroutining](#coroutining) · [Operating system](#operating-system) · [POSIX time](#posix-time) · [Regular expressions](#regular-expressions) · [CSV](#csv) · [Foreign function interface](#foreign-function-interface) · [library(arithmetic)](#libraryarithmetic) · [library(builtins)](#librarybuiltins) · [library(concurrent)](#libraryconcurrent) · [library(freeze)](#libraryfreeze) · [library(iso_ext)](#libraryisoext) · [library(lists)](#librarylists) · [library(sqlite3)](#librarysqlite3) · [library(tty)](#librarytty) · [Other](#other) ### Core & terms
99 predicates | Predicate | Template | | |---|---|---| | `=/2` | `=(+term,+term)` | ISO | | `=../2` | `=..(+term,?list)` | ISO | | `acyclic_term/1` | `acyclic_term(+term)` | ISO | | `arg/3` | `arg(+integer,+term,?term)` | ISO | | `atom/1` | `atom(+term)` | ISO | | `atom_chars/2` | `atom_chars(?atom,?list)` | ISO | | `atom_codes/2` | `atom_codes(?atom,?list)` | ISO | | `atom_concat/3` | `atom_concat(+atom,+atom,?atom)` | ISO | | `atom_length/2` | `atom_length(?list,?integer)` | ISO | | `atom_lower/2` | `atom_lower(?atom,?atom)` | | | `atom_upper/2` | `atom_upper(?atom,?atom)` | | | `atomic/1` | `atomic(+term)` | ISO | | `atomic_concat/3` | `atomic_concat(+atomic,+atomic,?atomic)` | | | `atomic_list_concat/2` | `atomic_list_concat(+list,?atom)` | | | `atomic_list_concat/3` | `atomic_list_concat(?list,+atomic,?atom)` | | | `base64/3` | `base64(?string,?string,+list)` | | | `between/3` | `between(+integer,+integer,?integer)` | | | `call_nth/2` | `call_nth(:callable,+integer)` | | | `callable/1` | `callable(+term)` | ISO | | `can_be/2` | `can_be(+atom,+term,)` | | | `can_be/4` | `can_be(+term,+atom,+term,?any)` | | | `char_code/2` | `char_code(?atom,?integer)` | ISO | | `compare/3` | `compare(+atom,+term,+term)` | ISO | | `compound/1` | `compound(+term)` | ISO | | `copy_term/2` | `copy_term(+term,?term)` | ISO | | `copy_term_nat/2` | `copy_term_nat(+term,?term)` | | | `copy_term_with_attributes/2` | `copy_term_with_attributes(+term,?term)` | | | `crypto_data_hash/3` | `crypto_data_hash(?string,?string,?list)` | | | `crypto_n_random_bytes/2` | `crypto_n_random_bytes(+integer,-codes)` | | | `current_module/1` | `current_module(-atom)` | | | `current_predicate/1` | `current_predicate(+predicate_indicator)` | ISO | | `current_rule/1` | `current_rule(-term)` | ISO | | `cyclic_term/1` | `cyclic_term(+term)` | | | `duplicate_term/2` | `duplicate_term(+term,?term)` | | | `end_of_file/0` | `end_of_file` | ISO | | `findall/3` | `findall(+term,:callable,-list)` | ISO | | `functor/3` | `functor(?term,?atom,?integer)` | ISO | | `ground/1` | `ground(+term)` | ISO | | `halt/0` | `halt` | ISO | | `halt/1` | `halt(+integer)` | ISO | | `help/0` | `help` | | | `help/1` | `help(+predicate_indicator)` | | | `help/2` | `help(+predicate_indicator,+atom)` | | | `hex_bytes/2` | `hex_bytes(?string,?list)` | | | `hex_chars/2` | `hex_chars(?integer,?string)` | | | `is_bigint/1` | `is_bigint(+term)` | | | `is_list/1` | `is_list(+term)` | | | `is_list_or_partial_list/1` | `is_list_or_partial_list(+term)` | | | `is_partial_list/1` | `is_partial_list(+term)` | | | `limit/2` | `limit(+integer,:callable)` | | | `list/1` | `list(+term)` | | | `load_text/2` | `load_text(+string,+list)` | | | `meta_predicate/1` | `meta_predicate(+term)` | | | `module_help/1` | `module_help(+atom)` | | | `module_help/2` | `module_help(+atom,+predicate_indicator)` | | | `module_help/3` | `module_help(+atom,+predicate_indicator,+atom)` | | | `module_info/2` | `module_info(+atom,-list)` | | | `multifile/1` | `multifile(+term)` | | | `must_be/2` | `must_be(+atom,+term)` | | | `must_be/4` | `must_be(+term,+atom,+term,?any)` | | | `nb_setarg/3` | `nb_setarg(+integer,+term,+integer)` | | | `nonvar/1` | `nonvar(+term)` | ISO | | `number/1` | `number(+term)` | ISO | | `number_chars/2` | `number_chars(?number,?list)` | ISO | | `number_codes/2` | `number_codes(?number,?list)` | ISO | | `numlist/3` | `numlist(+integer,+integer,-list)` | | | `octal_chars/2` | `octal_chars(?integer,?string)` | | | `offset/2` | `offset(+integer,+callable)` | | | `op/3` | `op(?integer,?atom,+atom)` | ISO | | `prolog_load_context/2` | `prolog_load_context(+atom,?term)` | | | `repeat/0` | `repeat` | ISO | | `replace/4` | `replace(+string,+integer,+integer,-string)` | | | `set_prolog_flag/2` | `set_prolog_flag(+atom,+term)` | ISO | | `source_info/2` | `source_info(+predicate_indicator,-list)` | | | `split/4` | `split(+string,+string,?string,?string)` | | | `split_string/4` | `split_string(+string,+atom,+atom,-list)` | | | `statistics/0` | `statistics` | | | `statistics/2` | `statistics(+atom,-term)` | | | `string/1` | `string(+term)` | | | `string_codes/2` | `string_codes(+string,-list)` | | | `string_concat/3` | `string_concat(+string,+string,?string)` | | | `string_length/2` | `string_length(+string,?integer)` | | | `string_lower/2` | `string_lower(?string,?string)` | | | `string_upper/2` | `string_upper(?string,?string)` | | | `strip_module/3` | `strip_module(+callable,?atom,?callable)` | | | `sub_atom/5` | `sub_atom(+atom,?before,?length,?after,?atom)` | ISO | | `sub_string/5` | `sub_string(+character_list,?before,?length,?after,?character_list)` | ISO | | `term_hash/2` | `term_hash(+term,?integer)` | | | `term_singletons/2` | `term_singletons(+term,-list)` | | | `term_variables/2` | `term_variables(+term,-list)` | ISO | | `trace/0` | `trace` | | | `unifiable/3` | `unifiable(+term,+term,-list)` | | | `unify_with_occurs_check/2` | `unify_with_occurs_check(+term,+term)` | ISO | | `urlenc/3` | `urlenc(?string,?string,+list)` | | | `use_module/1` | `use_module(+term)` | | | `use_module/2` | `use_module(+term,+list)` | | | `using/0` | `using` | | | `uuid/1` | `uuid(-string)` | | | `var/1` | `var(+term)` | ISO |
### Control
24 predicates | Predicate | Template | | |---|---|---| | `!/0` | `!` | ISO | | `*->/2` | `*->(:callable,:callable)` | | | `,/2` | `,(:callable,:callable)` | ISO | | `->/2` | `->(:callable,:callable)` | ISO | | `;/2` | `;(:callable,:callable)` | ISO | | `abort/0` | `abort` | | | `call/1` | `call(:callable)` | ISO | | `call/2` | `call(:callable,?term)` | ISO | | `call/3` | `call(:callable,?term,term)` | ISO | | `call/4` | `call(:callable,?term,?term,?term)` | ISO | | `call/5` | `call(:callable,?term,?term,?term,?term)` | ISO | | `call/6` | `call(:callable,?term,?term,?term,?term,?term)` | ISO | | `call/7` | `call(:callable,?term,?term,?term,?term,?term,?term)` | ISO | | `call/8` | `call(:callable,?term,?term,?term,?term,?term,?term,?term)` | ISO | | `catch/3` | `catch(:callable,?term,:callable)` | ISO | | `fail/0` | `fail` | ISO | | `false/0` | `false` | ISO | | `if/3` | `if(:callable,:callable,:callable)` | | | `ignore/1` | `ignore(:callable)` | | | `once/1` | `once(:callable)` | ISO | | `reset/3` | `reset(:callable,?term,-term)` | | | `shift/1` | `shift(+term)` | | | `throw/1` | `throw(+term)` | ISO | | `true/0` | `true` | ISO |
### Arithmetic
80 predicates | Predicate | Template | | |---|---|---| | `///2` | `//(+integer,+integer,-integer)` | ISO evaluable | | `//2` | `/(+number,+number,-float)` | ISO evaluable | | `*/2` | `*(+number,+number,-number)` | ISO evaluable | | `**/2` | `**(+number,+number,-float)` | ISO evaluable | | `+/1` | `+(+number,-number)` | ISO evaluable | | `+/2` | `+(+number,+number,-number)` | ISO evaluable | | `-/1` | `-(+number,-number)` | ISO evaluable | | `-/2` | `-(+number,+number,-number)` | ISO evaluable | | `/2` | `>(+number,+number)` | ISO | | `>=/2` | `>=(+number,+number)` | ISO | | `>>/2` | `>>(+integer,-integer)` | ISO evaluable | | `@/2` | `@>(+term,+term)` | ISO | | `@>=/2` | `@>=(+term,+term)` | ISO | | `^/2` | `^(+number,+number,-integer)` | ISO evaluable | | `abs/1` | `abs(+number,-number)` | ISO evaluable | | `acos/1` | `acos(+number,-float)` | ISO evaluable | | `acosh/1` | `acosh(+number,-float)` | evaluable | | `asin/1` | `asin(+number,-float)` | ISO evaluable | | `asinh/1` | `asinh(+number,-float)` | evaluable | | `atan/1` | `atan(+number,-float)` | ISO evaluable | | `atan2/2` | `atan2(+number,+number,-float)` | ISO evaluable | | `atanh/1` | `atanh(+number,-float)` | evaluable | | `ceiling/1` | `ceiling(+float,-integer)` | ISO evaluable | | `copysign/2` | `copysign(+number,-number)` | evaluable | | `cos/1` | `cos(+number,-float)` | ISO evaluable | | `cosh/1` | `cosh(+number,-float)` | evaluable | | `denominator/1` | `denominator(+rational,-integer)` | evaluable | | `div/2` | `div(+integer,+integer,-integer)` | ISO evaluable | | `divmod/4` | `divmod(+integer,+integer,?integer,?integer)` | | | `e/0` | `e` | ISO evaluable | | `epsilon/0` | `epsilon` | ISO evaluable | | `erf/1` | `erf(+number,-float)` | evaluable | | `erfc/1` | `erfc(+number,-float)` | evaluable | | `exp/1` | `exp(+number,-float)` | ISO evaluable | | `float/1` | `float(+number)` | ISO | | `float_fractional_part/1` | `float_fractional_part(+float,-float)` | ISO evaluable | | `float_integer_part/1` | `float_integer_part(+float,-integer)` | ISO evaluable | | `floor/1` | `floor(+float,-integer)` | ISO evaluable | | `gcd/2` | `gcd(+integer,+integer,-integer)` | evaluable | | `get_seed/1` | `get_seed(-integer)` | | | `integer/1` | `integer(+number)` | ISO | | `is/2` | `is(?number,+number)` | ISO | | `log/1` | `log(+number,-float)` | ISO evaluable | | `log/2` | `log(+number,+number,-float)` | evaluable | | `log10/1` | `log10(+number,-float)` | evaluable | | `lsb/1` | `lsb(+integer,-integer)` | evaluable | | `max/2` | `max(+number,+number,-number)` | ISO evaluable | | `min/2` | `min(+number,+number,-number)` | ISO evaluable | | `mod/2` | `mod(+integer,+integer,-integer)` | ISO evaluable | | `msb/1` | `msb(+integer,-integer)` | evaluable | | `numerator/1` | `numerator(+rational,-integer)` | evaluable | | `pi/0` | `pi` | ISO evaluable | | `popcount/1` | `popcount(+integer,-integer)` | evaluable | | `rand/0` | `rand` | evaluable | | `rand/1` | `rand(?integer)` | | | `random/1` | `random(?integer)` | | | `random_between/3` | `random_between(?integer,?integer,-integer)` | | | `random_float/0` | `random_float` | evaluable | | `random_integer/0` | `random_integer` | evaluable | | `rational/1` | `rational(+term)` | | | `rdiv/2` | `rdiv(+integer,+integer,-rational)` | evaluable | | `rem/2` | `rem(+integer,+integer,-integer)` | ISO evaluable | | `round/1` | `round(+float,-integer)` | ISO evaluable | | `set_seed/1` | `set_seed(+integer)` | | | `setrand/1` | `setrand(+integer)` | | | `sign/1` | `sign(+number,-number)` | ISO evaluable | | `sin/1` | `sin(+number,-float)` | ISO evaluable | | `sinh/1` | `sinh(+number,-float)` | evaluable | | `sqrt/1` | `sqrt(+number,-float)` | ISO evaluable | | `srandom/1` | `srandom(+integer)` | | | `tan/1` | `tan(+number,-float)` | ISO evaluable | | `tanh/1` | `tanh(+number,-float)` | evaluable | | `truncate/1` | `truncate(+float,-integer)` | ISO evaluable | | `xor/2` | `xor(+integer,+integer,-integer)` | ISO evaluable |
### Streams & I/O
103 predicates | Predicate | Template | | |---|---|---| | `absolute_file_name/3` | `absolute_file_name(+source_sink,-atom,+list)` | | | `access_file/2` | `access_file(+source_sink,+atom)` | | | `alias/2` | `alias(+blob,+atom)` | | | `at_end_of_stream/0` | `at_end_of_stream` | ISO | | `at_end_of_stream/1` | `at_end_of_stream(+stream)` | ISO | | `chdir/1` | `chdir(+source_sink)` | | | `close/1` | `close(+stream)` | ISO | | `close/2` | `close(+stream,+opts)` | ISO | | `copy_file/2` | `copy_file(+source_sink,+source_sink)` | | | `current_error/1` | `current_error(--stream)` | ISO | | `current_input/1` | `current_input(--stream)` | ISO | | `current_output/1` | `current_output(--stream)` | ISO | | `delete_file/1` | `delete_file(+source_sink)` | | | `directory_files/2` | `directory_files(+source_sink,-list)` | | | `exists_directory/1` | `exists_directory(+source_sink)` | | | `exists_file/1` | `exists_file(+source_sink)` | | | `flush_output/0` | `flush_output` | ISO | | `flush_output/1` | `flush_output(+stream)` | ISO | | `get_byte/1` | `get_byte(-integer)` | ISO | | `get_byte/2` | `get_byte(+stream,-integer)` | ISO | | `get_char/1` | `get_char(-integer)` | ISO | | `get_char/2` | `get_char(+stream,-integer)` | ISO | | `get_code/1` | `get_code(-integer)` | ISO | | `get_code/2` | `get_code(+stream,-integer)` | ISO | | `getfile/2` | `getfile(+source_sink,-list)` | | | `getfile/3` | `getfile(+source_sink,-list,+list)` | | | `getline/1` | `getline(-atom)` | | | `getline/2` | `getline(+stream,-string)` | | | `getline/3` | `getline(+stream,-string,+list)` | | | `getlines/1` | `getlines(-list)` | | | `getlines/2` | `getlines(+stream,-list)` | | | `getlines/3` | `getlines(+stream,-list,+list)` | | | `is_absolute_file_name/1` | `is_absolute_file_name(+source_sink)` | | | `is_stream/1` | `is_stream(+term)` | | | `load_files/2` | `load_files(+atom,+list)` | | | `loadfile/2` | `loadfile(+source_sink,-atom)` | | | `make/0` | `make` | | | `make_directory/1` | `make_directory(+source_sink)` | | | `make_directory_path/1` | `make_directory_path(+source_sink)` | | | `nl/0` | `nl` | ISO | | `nl/1` | `nl(+stream)` | ISO | | `open/4` | `open(+source_sink,+mode,--stream,+list)` | ISO | | `peek_byte/1` | `peek_byte(-integer)` | ISO | | `peek_byte/2` | `peek_byte(+stream,-integer)` | ISO | | `peek_char/1` | `peek_char(-integer)` | ISO | | `peek_char/2` | `peek_char(+stream,-integer)` | ISO | | `peek_code/1` | `peek_code(-integer)` | ISO | | `peek_code/2` | `peek_code(+stream,-integer)` | ISO | | `portray_clause/1` | `portray_clause(+term)` | | | `portray_clause/2` | `portray_clause(+stream,+term)` | | | `put_byte/1` | `put_byte(+integer)` | ISO | | `put_byte/2` | `put_byte(+stream,+integer)` | ISO | | `put_char/1` | `put_char(+integer)` | ISO | | `put_char/2` | `put_char(+stream,+integer)` | ISO | | `put_code/1` | `put_code(+integer)` | ISO | | `put_code/2` | `put_code(+stream,+integer)` | ISO | | `read/1` | `read(-term)` | ISO | | `read/2` | `read(+stream,-term)` | ISO | | `read_file_to_string/3` | `read_file_to_string(+source_sink,-string,+options)` | | | `read_line_to_codes/2` | `read_line_to_codes(+stream,-list)` | | | `read_line_to_string/2` | `read_line_to_string(+stream,-string)` | | | `read_term/2` | `read_term(+stream,-term)` | ISO | | `read_term/3` | `read_term(+stream,-term,+list)` | ISO | | `read_term_from_atom/3` | `read_term_from_atom(+atom,?term,+list)` | | | `read_term_from_chars/3` | `read_term_from_chars(+string,?term,+list)` | | | `redo/1` | `redo(+integer)` | | | `redo/2` | `redo(+stream,+integer)` | | | `rename_file/2` | `rename_file(+source_sink,+source_sink)` | | | `savefile/2` | `savefile(+source_sink,+source_sink)` | | | `seeing/1` | `seeing(-atom)` | | | `seen/0` | `seen` | | | `set_error/1` | `set_error(+stream)` | ISO | | `set_input/1` | `set_input(+stream)` | ISO | | `set_output/1` | `set_output(+stream)` | ISO | | `set_stream/2` | `set_stream(+stream,+term)` | ISO | | `set_stream_position/2` | `set_stream_position(+stream,+integer)` | ISO | | `size_file/2` | `size_file(+source_sink,-integer)` | | | `stream_property/2` | `stream_property(+stream,-compound)` | ISO | | `tab/1` | `tab(+integer)` | | | `tab/2` | `tab(+stream,+integer)` | | | `telling/1` | `telling(-atom)` | | | `time_file/2` | `time_file(+source_sink,-float)` | | | `told/0` | `told` | | | `unget_byte/1` | `unget_byte(+integer)` | ISO | | `unget_byte/2` | `unget_byte(+stream,+integer)` | ISO | | `unget_char/1` | `unget_char(+integer)` | ISO | | `unget_char/2` | `unget_char(+stream,+integer)` | ISO | | `unget_code/1` | `unget_code(+integer)` | ISO | | `unget_code/2` | `unget_code(+stream,+integer)` | ISO | | `unload_files/1` | `unload_files(+atom)` | | | `working_directory/2` | `working_directory(-atom,+source_sink)` | | | `write/1` | `write(+term)` | ISO | | `write/2` | `write(+stream,+term)` | ISO | | `write_canonical/1` | `write_canonical(+term)` | ISO | | `write_canonical/2` | `write_canonical(+stream,+term)` | ISO | | `write_canonical_to_atom/3` | `write_canonical_to_atom(?atom,?term,+list)` | | | `write_canonical_to_chars/3` | `write_canonical_to_chars(?string,?term,+list)` | | | `write_term/2` | `write_term(+stream,+term)` | ISO | | `write_term/3` | `write_term(+stream,+term,+list)` | ISO | | `write_term_to_atom/3` | `write_term_to_atom(?atom,?term,+list)` | | | `write_term_to_chars/3` | `write_term_to_chars(?term,+list,?string)` | | | `writeq/1` | `writeq(+term)` | ISO | | `writeq/2` | `writeq(+stream,+term)` | ISO |
### Formatting
3 predicates | Predicate | Template | | |---|---|---| | `format/1` | `format(+string)` | | | `format/2` | `format(+string,+list)` | | | `format/3` | `format(+stream,+string,+list)` | |
### Database
14 predicates | Predicate | Template | | |---|---|---| | `abolish/1` | `abolish(+predicate_indicator)` | ISO | | `abolish/2` | `abolish(+term,+list)` | | | `asserta/1` | `asserta(+term)` | ISO | | `asserta/2` | `asserta(+term,-string)` | | | `assertz/1` | `assertz(+term)` | ISO | | `assertz/2` | `assertz(+term,-string)` | | | `clause/2` | `clause(+term,?term)` | ISO | | `clause/3` | `clause(?term,?term,-string)` | | | `erase/1` | `erase(+string)` | | | `instance/2` | `instance(+string,?term)` | | | `listing/0` | `listing` | | | `listing/1` | `listing(+predicate_indicator)` | | | `retract/1` | `retract(+term)` | ISO | | `retractall/1` | `retractall(+term)` | ISO |
### Maps
14 predicates | Predicate | Template | | |---|---|---| | `engine_destroy/1` | `engine_destroy(+stream)` | | | `engine_fetch/1` | `engine_fetch(-term)` | | | `engine_next/2` | `engine_next(+stream,-term)` | | | `engine_post/2` | `engine_post(+stream,+term)` | | | `engine_self/1` | `engine_self(--stream)` | | | `engine_yield/1` | `engine_yield(+term)` | | | `is_engine/1` | `is_engine(+term)` | | | `map_close/1` | `map_close(+stream)` | | | `map_count/2` | `map_count(+stream,-integer)` | | | `map_create/2` | `map_create(--stream,+list)` | | | `map_del/2` | `map_del(+stream,+atomic)` | | | `map_get/3` | `map_get(+stream,+atomic,-atomic)` | | | `map_list/2` | `map_list(+stream,-list)` | | | `map_set/3` | `map_set(+stream,+atomic,+atomic)` | |
### Attributed variables
3 predicates | Predicate | Template | | |---|---|---| | `attribute/3` | `attribute(?atom,+atom,+integer)` | | | `get_atts/2` | `get_atts(@variable,-term)` | | | `put_atts/2` | `put_atts(@variable,+term)` | |
### Threads
29 predicates | Predicate | Template | | |---|---|---| | `is_thread/1` | `is_thread(+term)` | | | `message_queue_create/2` | `message_queue_create(-queue,+list)` | | | `message_queue_destroy/1` | `message_queue_destroy(+queue)` | | | `message_queue_property/2` | `message_queue_property(?queue,?term)` | | | `mutex_create/2` | `mutex_create(-mutex,+list)` | | | `mutex_destroy/1` | `mutex_destroy(+mutex)` | | | `mutex_lock/1` | `mutex_lock(+mutex)` | | | `mutex_property/2` | `mutex_property(?mutex,?term)` | | | `mutex_trylock/1` | `mutex_trylock(+mutex)` | | | `mutex_unlock/1` | `mutex_unlock(+mutex)` | | | `mutex_unlock_all/0` | `mutex_unlock_all` | | | `pl_msg_recv/2` | `pl_msg_recv(-thread,?term)` | | | `pl_msg_send/2` | `pl_msg_send(+thread,+term)` | | | `pl_thread_pin_cpu/2` | `pl_thread_pin_cpu(+thread,+integer)` | | | `pl_thread_set_priority/2` | `pl_thread_set_priority(+thread,+integer)` | | | `thread/3` | `thread(--thread,+atom,+list)` | | | `thread_cancel/1` | `thread_cancel(+thread)` | | | `thread_create/3` | `thread_create(:callable,--thread,+list)` | | | `thread_detach/1` | `thread_detach(+thread)` | | | `thread_exit/1` | `thread_exit(+term)` | | | `thread_get_message/2` | `thread_get_message(+queue,?term)` | | | `thread_get_message/3` | `thread_get_message(+queue,?term,+list)` | | | `thread_peek_message/2` | `thread_peek_message(+queue,?term)` | | | `thread_property/2` | `thread_property(?thread,?term)` | | | `thread_self/1` | `thread_self(-integer)` | | | `thread_send_message/2` | `thread_send_message(+queue,+term)` | | | `thread_signal/2` | `thread_signal(+thread,:callable)` | | | `thread_sleep/1` | `thread_sleep(+integer)` | | | `thread_yield/0` | `thread_yield` | |
### Coroutining
15 predicates | Predicate | Template | | |---|---|---| | `await/0` | `await` | | | `call_task/1` | `call_task(:callable)` | | | `call_task/2` | `call_task(:callable,?term)` | | | `call_task/3` | `call_task(:callable,?term,?term)` | | | `call_task/4` | `call_task(:callable,?term,?term,?term)` | | | `call_task/5` | `call_task(:callable,?term,?term,?term,?term)` | | | `call_task/6` | `call_task(:callable,?term,?term,?term,?term,?term)` | | | `call_task/7` | `call_task(:callable,?term,?term,?term,?term,?term,?term)` | | | `call_task/8` | `call_task(:callable,?term,?term,?term,?term,?term,?term,?term)` | | | `end_wait/0` | `end_wait` | | | `fork/0` | `fork` | | | `recv/1` | `recv(?term)` | | | `send/1` | `send(+term)` | | | `wait/0` | `wait` | | | `yield/0` | `yield` | |
### Operating system
22 predicates | Predicate | Template | | |---|---|---| | `busy/1` | `busy(+integer)` | | | `cpu_time/1` | `cpu_time(-integer)` | | | `date_time/6` | `date_time(-integer,-integer,-integer,-integer,-integer,-integer)` | | | `date_time/7` | `date_time(-integer,-integer,-integer,-integer,-integer,-integer,-integer)` | | | `get_time/1` | `get_time(-float)` | | | `get_unbuffered_char/1` | `get_unbuffered_char(?character)` | | | `get_unbuffered_code/1` | `get_unbuffered_code(?integer)` | | | `getenv/2` | `getenv(+atom,-atom)` | | | `now/0` | `now` | | | `now/1` | `now(-integer)` | | | `pclose/1` | `pclose(+stream)` | | | `popen/4` | `popen(+source_sink,+atom,--stream,+list)` | | | `process_create/3` | `process_create(+atom,+list,+list)` | | | `process_kill/1` | `process_kill(+integer)` | | | `process_kill/2` | `process_kill(+integer,+integer)` | | | `setenv/2` | `setenv(+atom,+atom)` | | | `shell/1` | `shell(+atom)` | | | `shell/2` | `shell(+atom,-integer)` | | | `sleep/1` | `sleep(+number)` | | | `time/1` | `time(:callable)` | | | `unsetenv/1` | `unsetenv(+atom)` | | | `wall_time/1` | `wall_time(-integer)` | |
### POSIX time
22 predicates | Predicate | Template | | |---|---|---| | `pid/1` | `pid(-integer)` | | | `posix_chmod/2` | `posix_chmod(+atom,+integer)` | | | `posix_ctime/2` | `posix_ctime(+integer,-atom)` | | | `posix_file_mode/2` | `posix_file_mode(+atom,-integer)` | | | `posix_file_times/4` | `posix_file_times(+atom,-float,-float,-float)` | | | `posix_file_type/2` | `posix_file_type(+atom,-atom)` | | | `posix_fork/1` | `posix_fork(-integer)` | | | `posix_getpid/1` | `posix_getpid(-integer)` | | | `posix_getppid/1` | `posix_getppid(-integer)` | | | `posix_gmtime/2` | `posix_gmtime(+integer,-compound)` | | | `posix_link/2` | `posix_link(+atom,+atom)` | | | `posix_localtime/2` | `posix_localtime(+integer,-compound)` | | | `posix_mktime/2` | `posix_mktime(+compound,-integer)` | | | `posix_readlink/2` | `posix_readlink(+atom,-atom)` | | | `posix_realpath/2` | `posix_realpath(+atom,-atom)` | | | `posix_rmdir/1` | `posix_rmdir(+atom)` | | | `posix_set_file_times/3` | `posix_set_file_times(+atom,+number,+number)` | | | `posix_strftime/3` | `posix_strftime(+atom,-atom,+compound)` | | | `posix_strptime/3` | `posix_strptime(+atom,+atom,-compound)` | | | `posix_symlink/2` | `posix_symlink(+atom,+atom)` | | | `posix_time/1` | `posix_time(-integer)` | | | `posix_unlink/1` | `posix_unlink(+atom)` | |
### Regular expressions
5 predicates | Predicate | Template | | |---|---|---| | `sre_compile/2` | `sre_compile(+string,-string,)` | | | `sre_match/4` | `sre_match(+string,+string,-string,-string,)` | | | `sre_matchp/4` | `sre_matchp(+string,+string,-string,-string,)` | | | `sre_subst/4` | `sre_subst(+string,+string,-string,-string,)` | | | `sre_substp/4` | `sre_substp(+string,+string,-string,-string,)` | |
### CSV
4 predicates | Predicate | Template | | |---|---|---| | `parse_csv_file/2` | `parse_csv_file(+atom,+list)` | | | `parse_csv_line/2` | `parse_csv_line(+atom,-list)` | | | `parse_csv_line/3` | `parse_csv_line(+atom,-compound,+options)` | | | `write_csv_file/3` | `write_csv_file(+atom,+list,+options)` | |
### Foreign function interface
2 predicates | Predicate | Template | | |---|---|---| | `foreign_struct/2` | `foreign_struct(+atom,+list)` | | | `use_foreign_module/2` | `use_foreign_module(+atom,+list)` | |
### library(arithmetic)
5 predicates | Predicate | Template | | |---|---|---| | `lsb/2` | `lsb(+integer,?integer)` | | | `msb/2` | `msb(+integer,?integer)` | | | `number_to_rational/2` | `number_to_rational(+number,-rational)` | | | `popcount/2` | `popcount(+integer,?integer)` | | | `rational_numerator_denominator/3` | `rational_numerator_denominator(+rational,-integer,-integer)` | |
### library(builtins)
55 predicates | Predicate | Template | | |---|---|---| | `absolute_filename/2` | `absolute_filename(+atom,?atom)` | | | `append/1` | `append(+filename)` | | | `argv/1` | `argv(-list)` | | | `atom_number/2` | `atom_number(?atom,?number)` | | | `bagof/3` | `bagof(+term,:callable,?list)` | ISO | | `call_residue_vars/2` | `call_residue_vars(@goal,-list)` | | | `chars_base64/3` | `chars_base64(+atom,?atom,+list)` | | | `chars_urlenc/3` | `chars_urlenc(+atom,?atom,+list)` | | | `current_op/3` | `current_op(?integer,?atom,?atom)` | ISO | | `current_prolog_flag/2` | `current_prolog_flag(+callable,+term)` | ISO | | `deconsult/1` | `deconsult(+list)` | | | `engine_create/3` | `engine_create(+term,+callable,?stream)` | | | `engine_create/4` | `engine_create(+term,+callable,?stream,+list)` | | | `evaluable_property/2` | `evaluable_property(+callable,+term)` | ISO | | `findnsols/4` | `findnsols(+integer,+term,+callable,?list)` | | | `flatten/2` | `flatten(?list,?list)` | | | `get0/1` | `get0(?integer)` | | | `get0/1` | `get0(+term)` | | | `get0/2` | `get0(+stream,?integer)` | | | `get0/2` | `get0(+stream,+term)` | | | `keysort/2` | `keysort(+term,?term)` | ISO | | `length/2` | `length(?term,?integer)` | | | `load_files/1` | `load_files(+list)` | | | `msort/2` | `msort(+term,?term)` | ISO | | `numbervars/3` | `numbervars(+term,+integer,?integer)` | | | `open/3` | `open(+atom,+atom,--stream)` | ISO | | `predicate_property/2` | `predicate_property(+callable,+term)` | ISO | | `pretty/1` | `pretty(+predicateindicator)` | | | `print/1` | `print(+term)` | | | `print/2` | `print(+stream,+term)` | | | `process_wait/2` | `process_wait(+integer,-term)` | | | `process_wait/3` | `process_wait(+integer,-term,?list)` | | | `put/1` | `put(+integer)` | | | `put/2` | `put(+stream,+integer)` | | | `raw_argv/1` | `raw_argv(-list)` | | | `read_from_atom/2` | `read_from_atom(+atom,?term)` | | | `read_from_chars/2` | `read_from_chars(+chars,?term)` | | | `reconsult/1` | `reconsult(+list)` | | | `see/1` | `see(+filename)` | | | `setof/3` | `setof(+term,+callable,?list)` | ISO | | `sort/2` | `sort(+term,?term)` | ISO | | `sort/4` | `sort(+term,+atom,+list,?term)` | | | `sre_match_all/3` | `sre_match_all(+pattern,+text,-list)` | | | `sre_match_all_in_file/3` | `sre_match_all_in_file(+pattern,+filename,-list)` | | | `sre_match_all_pos/3` | `sre_match_all_pos(+pattern,+subst,-list)` | | | `sre_match_all_pos_in_file/3` | `sre_match_all_pos_in_file(+pattern,+filename,-list)` | | | `sre_subst_all/4` | `sre_subst_all(+pattern,+text,+subst,-text)` | | | `sre_subst_all_in_file/4` | `sre_subst_all_in_file(+pattern,+filename,+subst,-list)` | | | `tell/1` | `tell(+filename)` | | | `term_hash/3` | `term_hash(+term,+list,-integer)` | | | `term_to_atom/2` | `term_to_atom(+term,?atom)` | | | `term_variables/3` | `term_variables(+term,-list,?tail)` | | | `thread_join/2` | `thread_join(+thread,-term)` | | | `writeln/1` | `writeln(+term)` | | | `writeln/2` | `writeln(+stream,+term)` | |
### library(concurrent)
4 predicates | Predicate | Template | | |---|---|---| | `await/2` | `await(+term,?term)` | | | `future/3` | `future(+term,+callable,?list)` | | | `future_all/2` | `future_all(+list,-term)` | | | `future_any/2` | `future_any(+list,-term)` | |
### library(freeze)
3 predicates | Predicate | Template | | |---|---|---| | `freeze/2` | `freeze(-var,+goal)` | | | `frozen/2` | `frozen(@term,-goal)` | | | `list_to_conjunction/2` | `list_to_conjunction(?list,?list)` | |
### library(iso_ext)
13 predicates | Predicate | Template | | |---|---|---| | `call_cleanup/2` | `call_cleanup(:callable,:callable)` | | | `call_det/2` | `call_det(:callable,?boolean)` | | | `call_with_time_limit/2` | `call_with_time_limit(+number,:callable)` | | | `cfor/3` | `cfor(+evaluable,+evaluable,-var)` | | | `countall/2` | `countall(:callable,?integer)` | ISO | | `findall/4` | `findall(+term,:callable,-list,+list)` | | | `forall/2` | `forall(:callable,:callable)` | | | `setup_call_cleanup/3` | `setup_call_cleanup(:callable,:callable,:callable)` | | | `subsumes_term/2` | `subsumes_term(+term,+term)` | ISO | | `succ/2` | `succ(?integer,+integer)` | | | `succ/2` | `succ(+integer,-integer)` | | | `time_out/3` | `time_out(:callable,+integer,?atom)` | | | `variant/2` | `variant(+term,+term)` | |
### library(lists)
44 predicates | Predicate | Template | | |---|---|---| | `append/2` | `append(?list,?list)` | | | `append/3` | `append(?term,?term,?term)` | | | `exclude/2` | `exclude(:callable,?list)` | | | `foldl/4` | `foldl(:callable,+list,+var,-var)` | | | `foldl/5` | `foldl(:callable,+list,+list,+var,-var)` | | | `foldl/6` | `foldl(:callable,+list,+list,+list,+var,-var)` | | | `include/2` | `include(:callable,?list)` | | | `intersection/3` | `intersection(+list,+list,-list)` | | | `is_set/1` | `is_set(+list)` | | | `last/2` | `last(+list,-term)` | | | `list_max/2` | `list_max(+list,?integer)` | | | `list_min/2` | `list_min(+list,?integer)` | | | `list_sum/2` | `list_sum(+list,?integer)` | | | `maplist/2` | `maplist(:callable,+list)` | | | `maplist/3` | `maplist(:callable,+list,+list)` | | | `maplist/4` | `maplist(:callable,+list,+list,+list)` | | | `maplist/5` | `maplist(:callable,+list,+list,+list,+list)` | | | `maplist/6` | `maplist(:callable,+list,+list,+list,+list,+list)` | | | `maplist/7` | `maplist(:callable,+list,+list,+list,+list,+list,+list)` | | | `maplist/8` | `maplist(:callable,+list,+list,+list,+list,+list,+list,+list)` | | | `max_list/2` | `max_list(+list,?integer)` | | | `member/2` | `member(?term,?term)` | | | `memberchk/2` | `memberchk(?term,?term)` | | | `min_list/2` | `min_list(+list,?integer)` | | | `nth0/3` | `nth0(?integer,?term,?term)` | | | `nth0/4` | `nth0(?integer,?term,?term,?term)` | | | `nth1/3` | `nth1(?integer,?term,?term)` | | | `nth1/4` | `nth1(?integer,+term,?term,?term)` | | | `permutation/2` | `permutation(?list,?list)` | | | `reverse/2` | `reverse(?list,?list)` | | | `same_length/2` | `same_length(?list,?list)` | | | `select/3` | `select(+term,+term,?term)` | | | `selectchk/3` | `selectchk(+term,?term,?term)` | | | `subtract/3` | `subtract(+list,+list,-list)` | | | `sum_list/2` | `sum_list(+list,?integer)` | | | `tasklist/2` | `tasklist(:callable,+list)` | | | `tasklist/3` | `tasklist(:callable,+list,+list)` | | | `tasklist/4` | `tasklist(:callable,+list,+list,+list)` | | | `tasklist/5` | `tasklist(:callable,+list,+list,+list,+list)` | | | `tasklist/6` | `tasklist(:callable,+list,+list,+list,+list,+list)` | | | `tasklist/7` | `tasklist(:callable,+list,+list,+list,+list,+list,+list)` | | | `tasklist/8` | `tasklist(:callable,+list,+list,+list,+list,+list,+list,+list)` | | | `transpose/2` | `transpose(?list,?list)` | | | `union/3` | `union(+list,+list,-list)` | |
### library(sqlite3)
14 predicates | Predicate | Template | | |---|---|---| | `sqlite3_close/2` | `sqlite3_close(+stream,-integer)` | | | `sqlite3_column_count/2` | `sqlite3_column_count(+stream,-integer)` | | | `sqlite3_column_double/3` | `sqlite3_column_double(+stream,+integer,-float)` | | | `sqlite3_column_int64/3` | `sqlite3_column_int64(+stream,+integer,-integer)` | | | `sqlite3_column_name/3` | `sqlite3_column_name(+stream,+integer,-atom)` | | | `sqlite3_column_text/3` | `sqlite3_column_text(+stream,+integer,-string)` | | | `sqlite3_column_type/3` | `sqlite3_column_type(+stream,+integer,-integer)` | | | `sqlite3_exec/6` | `sqlite3_exec(+stream,+atom,+integer,+integer,-integer,-integer)` | | | `sqlite3_finalize/2` | `sqlite3_finalize(+stream,-integer)` | | | `sqlite3_open/3` | `sqlite3_open(+atom,--stream,-integer)` | | | `sqlite3_prepare_v2/6` | `sqlite3_prepare_v2(+stream,+atom,+integer,-integer,-integer,-integer)` | | | `sqlite3_query/4` | `sqlite3_query(+stream,+string,-list,-list)` | | | `sqlite3_step/2` | `sqlite3_step(+stream,-integer)` | | | `sqlite_flag/2` | `sqlite_flag(+atom,-integer)` | |
### library(tty)
8 predicates | Predicate | Template | | |---|---|---| | `menu/3` | `menu(+term,+list,-term)` | | | `tty_action/1` | `tty_action(+term)` | | | `tty_clear/0` | `tty_clear` | | | `tty_flash/0` | `tty_flash` | | | `tty_goto/2` | `tty_goto(+integer,+integer)` | | | `tty_nl/1` | `tty_nl(+integer)` | | | `tty_size/2` | `tty_size(-integer,-integer)` | | | `ttyflush/0` | `ttyflush` | |
### Other
9 predicates | Predicate | Template | | |---|---|---| | `/\/2` | `/\(+integer,+integer,-integer)` | ISO evaluable | | `=` | `=/2: =:=(+number,+number)` | ISO | | `=\=/2` | `=\=(+number,+number)` | ISO | | `?=/2` | `?=(+term,+term)` | | | `\//2` | `\/(+integer,+integer,-integer)` | ISO evaluable | | `\/1` | `\(+integer,-integer)` | ISO evaluable | | `\+/1` | `\+(:callable)` | ISO | | `\=/2` | `\=(+term,+term)` | ISO | | `\==/2` | `\==(+term,+term)` | ISO |
Blackboard functions ==================== The blackboard is global in scope and shared among threads. The following are *SICStus Prolog* & *SWI-Prolog* (if `expects_dialect(sicstus)`) compatible: bb_put/2 # bb_put(:atom, +term) bb_get/2 # bb_get(:atom, ?term) bb_update/3 # bb_update(:atom, ?term, ?term) bb_delete/2 # bb_delete(:atom, ?term) The following is undone on backtracking and is a *Scryer Prolog* extension: bb_b_put/2 # bb_b_put(:atom, +term) Note: attributes are preserved across bb_put/bb_get like Scryer and SWI Prologs. But note: *bb_put/2* ensures copies of attributed variables, *bb_b_put/2* ensures live references: ```console ✗ tpl -q ?- freeze(V1,writeln(hello(V1))), bb_put(key,V1), bb_get(key,V2), V1=99, V2=98. hello(99) hello(98) V1 = 99, V2 = 98. ?- freeze(V1,writeln(hello(V1))), bb_b_put(key,V1), bb_get(key,V2), V2=99. hello(99) V1 = 99, V2 = 99. ?- ``` Crypto functions ================ Hash a plain-text data string to a hexadecimal byte string representing the cryptographic strength hashed value. The options are *algorithm(Name)* where *Name* can be *sha256*, *sha384* or *sha512*, and optionally *hmac(Key)* where *Key* is a list of byte values. This predicate is only available when compiled with OpenSSL... crypto_data_hash/3 # crypto_data_hash(+data,-hash,+options) Generate 'N' random bytes. crypto_n_random_bytes(N, Bs) # crypto_n_random_bytes(+integer, -codes) Convert a hexadecimal string to a byte-list. At least one arg must be instantiated... hex_bytes/2 # hex_bytes(?hash,?bytes) Parsing CSV with builtins ========================= Fast, efficient parsing of CSV files. Reading: parse_csv_line/2 # parse_csv_line(+atom,-list) parse_csv_line/3 # parse_csv_line(+atom,-compound,+options) parse_csv_file/2 # parse_csv_file(+filename,+options) Where options can be: trim(Boolean) # default false, trims leading and trailing whitespace numbers(Boolean) # default false, converts integers and floats header(Boolean) # default false, skip first (header) line in file comments(Boolean) # default false, skip lines beginning with comment character in file comment(Char) # default '#', set the comment character strings(Boolean) # default depends on type of input (atom or string) arity(Integer) # default to not checking arity, otherwise throw domain_error assert(Boolean) # default false, assertz to database instead (assumed for files, needs a functor) functor(Atom) # default output is a list, create a structure (mandatory for files and with assert) quote(Char) # default to double-quote sep(Char) # default to comma for .csv or unknown files & TAB for .tsv files Writing: write_csv_file/3 # write_csv_file(+filename,+list,+options) Where options can be: append(Boolean) # default is to truncate file, or append to file strings(Boolean) # default depends on type of input (atom or string) sep(Char) # default to comma for .csv or unknown files & TAB for .tsv files Examples... ```console ? L=[["1 1",12,'1 3'],[],['21','','23']], write_csv_file('x.csv',L,[]). $ cat x.csv "1 1",12,1 3 21,,23 ?- Row=["1 1",12,'1 3'], L=[Row], write_csv_file('x.csv',L,[]). $ cat x.csv "1 1",12,1 3 ?- parse_csv_line('123,2.345,3456789',T). T = ['123','2.345','3456789']. ?- parse_csv_line("123,2.345,3456789",T). T = ["123","2.345","3456789"]. ?- parse_csv_line('123,2.345,3456789',T,[functor(f)]). T = f('123','2.345','3456789'). ?- parse_csv_line('123,2.345,3456789',T,[functor(f),numbers(true)]). T = f(123,2.345,3456789). ?- parse_csv_line('abc, abc, a b c ',T). T = [abc,' abc',' a b c ']. ?- parse_csv_line('abc, abc, a b c ',T,[trim(true)]). T = [abc,abc,'a b c']. ?- parse_csv_line('123,2.345,3456789',T,[functor(f),numbers(true),assert(true)]). true. ?- f(A,B,C). A = 123, B = 2.345, C = 3456789. ?- time(parse_csv_file('../logtalk3/library/csv/test_files/tickers.csv',[functor(f),quote('\'')])). % Parsed 35193 lines % Time elapsed 0.096s, 3 Inferences, 0.000 MLips) true. ?- f(A,B,C,D,E,F). A = '1125:HK', B = 'OTCGREY', C = 'Stock', D = 'USD', E = '1999-06-22', F = '2019-10-22' ; A = '6317:TK', B = 'PINK', C = 'Stock', D = 'USD', E = '2018-06-27', F = '2020-03-02' ; A = 'A', B = 'NYSE', C = 'Stock', D = 'USD', E = '1999-11-18', F = '2021-06-25' ; A = 'AA', B = 'NYSE', C = 'Stock', D = 'USD', E = '2016-11-01', F = '2021-06-25' ; A = 'AA-W', B = 'NYSE', C = 'Stock', D = 'USD', E = '2016-10-18', F = '2016-11-08' ; A = 'AAA', B = 'NYSEARCA', C = 'ETF', D = 'USD', E = '2020-09-09', F = '2021-06-25' ; ``` Application maps (dictionaries) =============================== Maps use atomic key/value pairs only and are represented as pseudo-streams: map_create/2 # map_create(-skiplist,+opts) map_create/1 # map_create(-skiplist) map_set/3 # map_set(+skiplist,+key,+value) map_get/3 # map_get(+skiplist,+key,?value) map_del/2 # map_del(+skiplist,+key) map_count/2 # map_count(+skiplist,-count) map_list/2 # map_list(+skiplist,?list) map_close/1 # map_close(+skiplist) ```console $ tpl ?- map_create(S,[alias(foo)]). S = <$stream>(4). ?- map_set(foo,1,111), map_set(foo,two,222), map_set(foo,3,333). true. ?- map_get(foo,3,V). V = 333. ?- map_del(foo,3). true. ?- map_list(foo,L). L = [1=111,two=222]. ?- map_close(foo). true. ``` Maps can store virtually unlimited amounts of volatile data in an efficient indexed manner. Maps don't require syntactic extensions to Prolog as found in other non-standard systems. A possible future extension would be to load a CSV file directly in a very efficient manner. HTTP 1.1 ======== :- use_module(library(http)). http_get/3 # http_get(Url, Data, Opts) http_post/4 # http_post(Url, Data, Opts) http_patch/4 # http_patch(Url, Data, Opts) http_put/4 # http_put(Url, Data, Opts) http_delete/3 # http_delete(Url, Data, Opts) http_server/2 # http_server(Goal,Opts), http_request/5 # http_request(S, Method, Path, Ver, Hdrs) ```console ?- http_get("https://github.com/trealla-prolog/trealla", Data, [status_code(Code)]). Data = "\n\n\n\n\n\n\n 0 meaning return that many bytes, = 0 meaning return whatever is there (if non-blocking) or a var meaning return all bytes until end end of file, Simple regular expressions ========================== This is meant as a place-holder until a proper regex package is included. sre_compile/2 # sre_compile(+pattern,-reg) sre_matchp/4 # sre_matchp(+reg,+text,-match,-rest) sre_substp/4 # sre_substp(+reg,+text,-prefix,-rest) sre_match/4 # sre_match(+pattern,+text,-match,-rest) sre_match_all/3 # sre_matchall(+pattern,+text,-list) sre_match_all_pos/3 # sre_matchall_pos(+pattern,+text,-pairs) sre_match_all_in_file/3 # sre_matchall_in_file(+pattern,+filename,-list) sre_match_all_pos_in_file/3 # sre_matchall_pos_in_file(+pattern,+filename,-pairs) sre_subst/4 # sre_subst(+pattern,+text,-prefix,-rest) sre_subst_all/4 # sre_subst(+pattern,+text,+subst,-text) sre_subst_all_in_file/4 # sre_subst_in_file(+pattern,+filename,+subst,-text) ``` * Supports: * --------- * '.' Dot, matches any character * '^' Start anchor, matches beginning of string * '$' End anchor, matches end of string * '*' Asterisk, match zero or more (greedy) * '+' Plus, match one or more (greedy) * '?' Question, match zero or one (non-greedy) * '[abc]' Character class, match if one of {'a', 'b', 'c'} * '[^abc]' Inverted class, match if NOT one of {'a', 'b', 'c'} * '[a-zA-Z]' Character ranges, the character set of the ranges { a-z | A-Z } * '\s' Whitespace, \t \f \r \n \v and spaces * '\S' Non-whitespace * '\w' Alphanumeric, [a-zA-Z0-9_] * '\W' Non-alphanumeric * '\d' Digits, [0-9] * '\D' Non-digits ``` For example... ```console ?- sre_compile("d.f", Reg), sre_matchp(Reg, "abcdefghi", M, Rest). Reg = <$blob>(0x6AC5AAF0), M = "def", Rest = "ghi". ?- sre_match("d.f", "abcdefghi", M, Rest). M = "def", Rest = "ghi". ?- sre_match_all("d.f", "xdafydbfzdcf-", L). L = ["daf","dbf","dcf"]. ?- sre_match_all_pos("d.f", "xdafydbfzdcf-", L). L = [1-3,2-3,3-3]. ?- sre_match_all("d[^c]f", "xdafydbfzdcfxddf-", L). L = ["daf","dbf","ddf"]. ?- sre_subst("d.f", "xdafydbfzdcf-", P, L). P = "x", L = "ydbfzdcf-". ?- sre_subst_all("d.f", "xdafydbfzdcf-", "$", L). L = "x$y$z$-". ?- sre_match_all("\\S", "Needle In A Haystack", L). L = ["N","e","e","d","l","e","I","n","A",...]. ?- sre_match_all_pos("\\s", "Needle In A Haystack", L). L = [6-1,9-1,11-1]. ?- time(sre_match_all_in_file("t\\We",'thesaurus.txt',L)), length(L,Len), format("Occurrs: ~w times~n",[Len]), halt. Time elapsed 0.0463s Occurrs: 749 times ``` Note: if no match is found the returned *match*, *text* (and *list*) is *[]* indicating an empty string. Note: if the input *text* arg is a string then the output *text* arg is a no-copy slice of the string. So if the input is a memory-mapped file then regex searches can be performed quickly and efficiently over huge files. Foreign Function Interface (libffi) =================================== Allows the loading of dynamic libraries and calling of foreign functions written in C from within Prolog... '$dlopen'/3 # '$dlopen(+name, +flag, -handle) These predicates register a foreign function as a builtin and use a wrapper to validate arg types at call/runtime... '$register_function'/4 # '$ffi_reg'(+handle,+symbol,+types,+ret_type) '$register_predicate'/4 # '$ffi_reg'(+handle,+symbol,+types,+ret_type) The allowed types are *sint8*, *sint16*, *sint32*, *sint64*, *sint* (native *signed int*), *uint8*, *uint16*, *uint32*, *uint64*, *uint* (native *unsigned int*), *ushort*, *sshort*, *float*, *double*, *bool*, (use integer 0/1 to align with C *bool* pseudo-type) *void* (a return type only), *cstr* (a char pointer), and *ptr* (for arbitrary pointers/handles). Assuming the following C-code in *samples/foo.c*: ```c double foo(double x, int64_t y) { return pow(x, (double)y); } int bar(double x, int64_t y, double *result) { *result = pow(x, (double)y); return 0; } char *baz(const char *x, const char *y) { char *s = TPL_malloc(strlen(x) + strlen(y) + 1); strcpy(s, x); strcat(s, y); return s; } ``` ```console $ gcc -fPIC -c foo.c $ gcc -shared -o libfoo.so foo.o ``` Register a builtin function... ```console ?- '$dlopen'('samples/libfoo.so', 0, H), '$register_function'(H, foo, [double, sint64], double). H = 94051868794416. ?- R is foo(2.0, 3). R = 8.0. ?- R is foo(abc,3). error(type_error(float,abc),foo/2). ``` Register a builtin predicate... ```console ?- '$dlopen'('samples/libfoo.so', 0, H), '$register_predicate'(H, bar, [double, sint64, -double], sint64), '$register_predicate'(H, baz, [cstr, cstr], cstr), H = 94051868794416. ?- bar(2.0, 3, X, Return). X = 8.0, Return = 0. ?- baz('abc', '123', Return). Return = abc123. ``` Note: the foreign function return value is passed as an extra argument to the predicate call, unless it was specified to be of type *void*. Foreign Module Interface (libffi) ================================= This is a simplified interface to FFIs inspired by Adrián Arroyo Calle and largely supercedes the implementation given above. foreign_struct(+atom, +list) use_foreign_module(+atom, +list) For example... ```prolog :- use_foreign_module('samples/libfoo.so', [ bar([double, sint64, -double], sint64), baz([cstr, cstr], cstr) ]). ``` See the *library/raylib.pl* and *samples/test_raylib1.pl* for an example usage including passing and returning structs by value. See the *library/curl.pl* and *samples/test_curl.pl* for an example usage downloading a file. This is an example using SQLITE. Given the code in *samples/sqlite3.pl*... ```prolog :- use_module(library(sqlite3)). run :- test('samples/sqlite3.db', 'SELECT * FROM company'). test(Database, Query) :- sqlite_flag('SQLITE_OK', SQLITE_OK), sqlite3_open(Database, Connection, Ret), Ret =:= SQLITE_OK, bagof(Row, sqlite3_query(Connection, Query, Row, _), Results), writeq(Results), nl. ``` Run... ```console $ tpl -g run,halt samples/sqlite3.pl [[1,'Paul',32,'California',20000.0],[2,'Allen',25,'Texas',15000.0],[3,'Teddy',23,'Norway',20000.0],[4,'Mark',25,'Rich-Mond ',65000.0],[5,'David',27,'Texas',85000.0],[6,'Kim',22,'South-Hall',45000.0]] ``` ISO Prolog Multithreading ========================= Start independent (shared state) Prolog queries as dedicated POSIX threads and communicate via message queues. Note: the database *is* shared. These predicates conform to the *ISO Prolog multithreading support* standards proposal (ISO/IEC DTR 13211–5:2007), now lapsed. Note: a thread is also a queue and a mutex. Note this is an expired ISO standards proposal but is commonly supported. thread_create/3 # thread_create(:callable,--thread,+opts) thread_create/2 # thread_create(:callable,--thread) thread_signal/2 # thread_signal(+thread,:callable) thread_join/2 # thread_join(+thread,-term) thread_cancel/1 # thread_cancel(+thread) thread_detach/1 # thread_detach(+thread) thread_self/1 # thread_self(-thread) thread_exit/1 # thread_exit(+term) thread_sleep/1 # thread_sleep(+integer) thread_yield/0 # thread_yield thread_property/2 # thread_property(+thread,+term) thread_property/1 # thread_property(+term) thread_send_message/2 # thread_send_message(+queue,+term) thread_send_message/1 # thread_send_message(+term) thread_get_message/2 # thread_get_message(+queue,?term) thread_get_message/1 # thread_get_message(?term) thread_peek_message/2 # thread_peek_message(+queue,?term) thread_peek_message/1 # thread_peek_message(?term) Where 'opts' can be *alias(+atom)*, *at_exit(:term)* and/or *detached(+boolean)* (the default is *NOT* detached, ie. joinable). Note: `thread_cancel/1` is dangerous and should be avoided, it does not exist in some other Prologs and does not rightly belong in any standards proposal. These are non-standard but *SWI-Prolog* compatible: thread_join/1 # thread_join(+thread) thread_get_message/3 # thread_get_message(+queue,?term,+opts) Where 'opts' can be *timeout(+float)* to specify a timeout in seconds. Create a stand-alone message queue. Note: a queue is also a mutex. message_queue_create/2 # message_queue_create(--queue,+opts) message_queue_create/1 # message_queue_create(--queue) message_queue_destroy/1 # message_queue_destroy(+queue) message_queue_property/2 # message_queue_property(+queue,+term) Where 'opts' can be *alias(+atom)*. Create a stand-alone mutex... mutex_create/2 # mutex_create(--mutex,+opts) mutex_create/1 # mutex_create(--mutex) mutex_destroy/1 # mutex_destroy(+mutex) mutex_property/2 # mutex_property(+mutex,+term) with_mutex/2 # with_mutex(+mutex,:callable) mutex_trylock/1 # mutex_trylock(+mutex) mutex_lock/1 # mutex_lock(+mutex) mutex_unlock/1 # mutex_unlock(+mutex) mutex_unlock_all/0 # mutex_unlock_all Where 'opts' can be *alias(+atom)*. Use of mutexes other than *with_mutex/2* should generally be avoided. For example... ```console ?- thread_create((format("thread_hello~n",[]),sleep(1),format("thread_done~n",[]),thread_exit(99)), Tid, []), format("joining~n",[]), thread_join(Tid,Status), format("join_done~n",[]). joining thread_hello thread_done join_done Tid = 1, Status = exited(99). ?- ``` Prolog instances ================ Start independent (no shared state) Prolog instances as dedicated pre-emptive threads and communicate via message queues. Each thread has it's own message queue associated with it. Note: the database is *not* shared. pl_thread/3 # pl_thread(-thread,+filename,+options) pl_thread/2 # pl_thread(-thread,+filename) Where 'options' can be (currently just) *alias(+atom)*. pl_msg_send/2 # pl_msg_send(+thread,+term) pl_msg_recv/2 # pl_msg_recv(-thread,-term) For example... ```console $ cat samples/thread_calc.pl :- initialization(main). % At the moment we only do sqrt here... main :- write('Calculator running...'), nl, repeat, pl_msg_recv(Tid, Term), Term = sqrt(X, Y), Y is sqrt(X), pl_msg_send(Tid, Term), fail. $ tpl ?- pl_thread(_, 'samples/thread_calc.pl', [alias(calc)]). Calculator running... ?- Term = sqrt(2, V), pl_msg_send(calc, Term), pl_msg_recv(_, Term). Term = sqrt(2,1.4142135623731), V = 1.4142135623731. ?- ``` Concurrent Tasks ##EXPERIMENTAL## ================ Co-operative multitasking is available in the form of light-weight coroutines that run until they yield either explicitly or implicitly (when waiting on an event of some kind using pol() where available). call_task/[1-n] # concurrent form of call/1-n tasklist/[2-8] # concurrent form of maplist/1-n An example: ```prolog :-use_module(library(http)). geturl(Url) :- http_get(Url,_Data,[status_code(Code),final_url(Location)]), format("Job [~w] ~w ==> ~w done~n",[Url,Code,Location]). % Fetch each URL in list sequentially... test54 :- L = ['www.google.com','www.bing.com','www.duckduckgo.com'], maplist(geturl,L), write('Finished\n'). $ tpl samples/test -g "time(test54),halt" Job [www.google.com] 200 ==> www.google.com done Job [www.bing.com] 200 ==> www.bing.com done Job [www.duckduckgo.com] 200 ==> https://duckduckgo.com done Finished Time elapsed 0.663 secs % Fetch each URL in list concurrently... test56 :- L = ['www.google.com','www.bing.com','www.duckduckgo.com'], tasklist(geturl,L), write('Finished\n'). $ tpl samples/test -g "time(test56),halt" Job [www.duckduckgo.com] 200 ==> https://duckduckgo.com done Job [www.bing.com] 200 ==> www.bing.com done Job [www.google.com] 200 ==> www.google.com done Finished Time elapsed 0.33 secs ``` Concurrent Futures ##EXPERIMENTAL## ================== Inspired by [Tau-Prolog](http://tau-prolog.org/documentation#concurrent) concurrent futures. Uses co-operative tasks. future/3 # Make a Future from a Prolog goal. future_all/2 # Make a Future that resolves to a list of the results of an input list of futures. future_any/2 # Make a Future that resolves as soon as any of the futures in a list succeeds. future_cancel/1 # Cancel unfinished future. future_done/1 # Check if a future finished. await/2 # Wait for a Future. For example: ```prolog :- use_module(library(concurrent)). :- use_module(library(http)). test :- future(Status1, geturl("www.google.com", Status1), F1), future(Status2, geturl("www.bing.com", Status2), F2), future(Status3, geturl("www.duckduckgo.com", Status3), F3), future_all([F1,F2,F3], F), await(F, StatusCodes), C = StatusCodes. ``` See `samples/test_concurrent.pl`. Engines ##EXPERIMENTAL## ======= Inspired by [*SWI-Prolog*](https://www.swi-prolog.org/pldoc/man?section=engine-predicates) engines. Uses co-operative tasks. engine_create/[3,4] engine_next/2 engine_yield/1 engine_post/[2,3] engine_fetch/1 engine_self/1 is_engine/1 current_engine/1 engine_destroy/1 For example: ``` ✗ cat find.pl find_at_most(N, Template, Goal, List) :- engine_create(Template, Goal, Engine), collect_at_most(N, Engine, List0), engine_destroy(Engine), List = List0. collect_at_most(N, Engine, [X| Xs]) :- N > 0, engine_next(Engine, X), !, M is N - 1, collect_at_most(M, Engine, Xs). collect_at_most(_, _, []). ✗ tpl -q find.pl ?- find_at_most(5, I, between(1,1000,I), Sols). Sols = [1,2,3,4,5]. ?- ^D% ``` Compile to standalone ===================== ``` ✗ cat samples/main.pl ``` ```prolog :- initialization(main). main :- write('Hello, world!'), nl, halt. ``` ``` ✗ make compile main=samples/main.pl ✗ ./tpl Hello, world! ``` Profile ======= Why did I put this here? ```console $ time tpl -q -g 'main,statistics(profile,_),halt' -f ~/trealla/samples/out.pl 2>out.csv $ head -1 out.csv >out_sorted.csv && tail -n+2 out.csv | sort -k 3 -t ',' -n -r >> out_sorted.csv $ cat out_sorted.csv #functor/arity,match_attempts,matched,tcos 'member_/3',20505037,20036023,19362515 'can_step/5',1149136,288705,189915 'can_move/5',164848,98905,32873 'strength/4',1074794,63382,31691 'minus_one/2',1621942,1621942,0 'make_move/6',1086892,1086892,0 'member_/3',20709531,730369,0 'member/2',673508,673508,0 'occupied_by/4',673316,673316,0 ... ```