persistent
declaration) see must_be/2Did you know ... | Search Documentation: |
library(persistency): Provide persistent dynamic predicates |
This module provides simple persistent storage for one or more dynamic predicates. A database is always associated with a module. A module that wishes to maintain a database must declare the terms that can be placed in the database using the directive persistent/1.
The persistent/1 expands each declaration into four predicates:
name(Arg, ...)
assert_name(Arg, ...)
retract_name(Arg, ...)
retractall_name(Arg, ...)
As mentioned, a database can only be accessed from within a single module. This limitation is on purpose, forcing the user to provide a proper API for accessing the shared persistent data.
This module requires the same thread-synchronization as the normal Prolog database. This implies that if each individual assert or retract takes the database from one consistent state to the next, no additional locking is required. If more than one elementary database operation is required to get from one consistent state to the next, both updating and querying the database must be locked using with_mutex/2.
Below is a simple example, where adding a user does not need locking as it is a single assert, while modifying a user requires both a retract and assert and thus needs to be locked.
:- module(user_db, [ attach_user_db/1, % +File current_user_role/2, % ?User, ?Role add_user/2, % +User, +Role set_user_role/2 % +User, +Role ]). :- use_module(library(persistency)). :- persistent user_role(name:atom, role:oneof([user,administrator])). attach_user_db(File) :- db_attach(File, []). %% current_user_role(+Name, -Role) is semidet. current_user_role(Name, Role) :- with_mutex(user_db, user_role(Name, Role)). add_user(Name, Role) :- assert_user_role(Name, Role). set_user_role(Name, Role) :- user_role(Name, Role), !. set_user_role(Name, Role) :- with_mutex(user_db, ( retractall_user_role(Name, _), assert_user_role(Name, Role))).
:- persistent <callable>, <callable>, ...
Each specification is a callable term, following the conventions of library(record)
,
where each argument is of the form
name:type
Types are defined by library(error)
.
close
(close journal after write), flush
(default, flush journal after write) or none
(handle as
fully buffered stream).
If File is already attached this operation may change the sync
behaviour.
reload
, but use incremental loading if possible. This
allows for two processes to examine the same database file, where one
writes the database and the other periodycally calls db_sync(update)
to follow the modified data.gc(50)
.With unbound What, db_sync/1 reloads the database if it was modified on disk, gc it if it is dirty and close it if it is opened.
persistent
declaration) see must_be/2This needs more explanation on the attached "database file".
Is it a text file with Prolog source code?
Update:
The source:
https://github.com/SWI-Prolog/swipl-devel/blob/master/library/persistency.pl
reveals that it uses Prologs term reading & writing:
https://www.swi-prolog.org/pldoc/man?section=termrw
sufficient for simple applications I guess. But once you need versioning and surety regarding massacred files because the process crashed, you have to look for more.
How about a file managed by an in-process database library like SQLite? You get transactions, memory-mapping, speed, pre-defined file format, metadata etc. for free (i.e. one of those: https://en.wikipedia.org/wiki/List_of_in-memory_databases ). A juicy development task ~~~
Links:
Prolog database operations like retract/assert[a|z] etc. are named, link:
https://eu.swi-prolog.org/pldoc/man?section=db
library(record) is named, here is the link: