Skip to content

Commit fd18c9e

Browse files
JanWielemakerclaude
andcommitted
DOC: blob(Mode) read option, blobs in the standard order of terms
Documents the read_term/2,3 blob(Mode) option, why `error' is the default, the cost of blob(resolve) and the constraint that the notation is unavailable where `<' is a prefix operator. Notes that blob/2 reports the type a dead blob stands for while current_blob/2 reports `unavailable', documents blob_released/1, and states the requirement that a blob write() callback emit valid Prolog syntax between the brackets. Also fills a long-standing gap: section "Standard Order of Terms" never mentioned blobs, although they occupy a band of their own between strings and atoms and their cross-type order depends on registration order. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JRYyR4hQsjJy7dbn5G5Zmt
1 parent ec0a94a commit fd18c9e

3 files changed

Lines changed: 74 additions & 1 deletion

File tree

man/builtin.plx

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1872,7 +1872,18 @@ True if \arg{Term} is bound to an atom.
18721872

18731873
\predicate{blob}{2}{@Term, ?Type}
18741874
True if \arg{Term} is a \jargon{blob} of type \arg{Type}. See
1875-
\secref{blob}.
1875+
\secref{blob}. For a blob read using the \term{blob}{dead} option of
1876+
read_term/3, \arg{Type} is the type the blob stands for, while
1877+
current_blob/2 reports its real type \const{unavailable}.
1878+
1879+
\predicate{blob_released}{1}{@Term}
1880+
True if \arg{Term} is a blob whose data was released using
1881+
\cfuncref{PL_free_blob}{}. Such a blob still exists as a term and keeps
1882+
its type, but the object it referred to is gone: it writes as
1883+
\mbox{\const{<}\arg{Type}\const{>(freed)}} and every predicate that
1884+
expects the real thing rejects it. Note that only blobs can be
1885+
released; an ordinary atom is reclaimed as a whole by the atom garbage
1886+
collector and remains valid for as long as it exists.
18761887

18771888
\predicate{string}{1}{@Term}
18781889
True if \arg{Term} is bound to a string. Note that string here refers to
@@ -1996,6 +2007,11 @@ so-called ``standard order''. This order is defined as follows:
19962007
rationals.
19972008
\item \arg{Strings} are compared alphabetically.
19982009
\item \arg{Atoms} are compared alphabetically.
2010+
\item Non-text blobs (see \secref{blob}) occupy a band of their own
2011+
between \arg{Strings} and \arg{Atoms}. Two blobs of the same
2012+
type are compared by that type; between types the order is
2013+
the order in which the types were registered and is therefore
2014+
not guaranteed to be the same in another run.
19992015
\item \arg{Compound} terms are first checked on their arity, then
20002016
on their functor name (alphabetically) and finally recursively
20012017
on their arguments, leftmost argument first.
@@ -7056,6 +7072,45 @@ If \const{true}, read \verb$`$\ldots\verb$`$ to a string object (see
70567072
\secref{string}). The default depends on the Prolog flag
70577073
\prologflag{back_quotes}.
70587074

7075+
\termitem{blob}{Atom}
7076+
Defines how to read a non-text blob, which write/1 emits as
7077+
\mbox{\const{<}\arg{Type}\const{>}\arg{(Arg, \ldots)}}, e.g.
7078+
\exam{<stream>(0x55c1e0)}. See \secref{blob}. Values are
7079+
7080+
\begin{description}
7081+
\termitem{error}{}
7082+
Raise a syntax error. This is the default: reading such a term cannot
7083+
reconstruct a variant of the term that was written, so the reader does
7084+
not silently produce one.
7085+
\termitem{dead}{}
7086+
Create a blob that has no foreign object behind it. It is atomic, it
7087+
writes as it was read and blob/2 reports the type it stands for, but
7088+
every predicate that expects the real thing rejects it. Use this to
7089+
parse log files, stack traces or listing output that contains blobs.
7090+
\termitem{resolve}{}
7091+
As \const{dead}, but first look for a blob that is alive in this process
7092+
and writes as this text. This is what the toplevel uses, so a blob can
7093+
be copied from earlier output and pasted back into a goal. Note that
7094+
this turns text into a live handle and must therefore only be used on
7095+
trusted input. It is also best effort because the original blob may have
7096+
been reclaimed and a new blob of the same type may have allocated a
7097+
new resource at the same address (known as the \jargon{ABA problem}).
7098+
Considering the toplevel, (small) answers are recorded for reuse as
7099+
\verb|$Var| and blobs that are part of small answers are thus referenced
7100+
as long as the answer binding is not changed.
7101+
7102+
Resolving is not cheap: there is no index from the written form back to
7103+
the blob, so it examines every live blob of the requested type and
7104+
writes it to compare the result. This is fine for the toplevel, but it
7105+
makes \const{resolve} a poor choice for reading many terms. Use
7106+
\const{dead} unless a live handle is really required.
7107+
\end{description}
7108+
7109+
The notation is only unambiguous as long as \const{<} is not a prefix
7110+
operator: if it is, \exam{<a>(f)} is the legal term \exam{>(<(a),f)}. In
7111+
a module that declares such an operator the reader therefore leaves the
7112+
text alone and blobs cannot be read, regardless of \arg{Mode}.
7113+
70597114
\termitem{character_escapes}{Bool}
70607115
Defines how to read \verb$\$ escape sequences in quoted atoms.
70617116
See the Prolog flag \prologflag{character_escapes} in current_prolog_flag/2.

man/foreign.plx

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3055,6 +3055,17 @@ write() function writes
30553055
not provided, write/1 emits the content of the blob for blobs of type
30563056
\const{PL_BLOB_TEXT} or a string of the format \verb$<#$\textit{hex
30573057
data}\verb$>$ for binary blobs.
3058+
3059+
This notation is claimed by the reader, so the write() function should
3060+
emit the type name between \verb$<$ and \verb$>$, followed by the
3061+
arguments as \emph{valid Prolog syntax} between brackets. Quote
3062+
anything that needs it: a pattern, file name or message that contains a
3063+
bracket or a quote makes the whole term unparsable. Given that,
3064+
read_term/2,3 can read the blob back using the \term{blob}{dead} option
3065+
(see read_term/3), which is what makes a stack trace or a \verb$~q$ log
3066+
file containing blobs parsable. Note that the pointer is written with
3067+
\verb$%p$, which Sfprintf() renders as \exam{0x}\textit{hex} on every
3068+
platform.
30583069
\end{description}
30593070
30603071
\begin{description}
@@ -3202,6 +3213,12 @@ or callbacks that use the blob must check the result of
32023213
PL_blob_data().} If the release() function is not called, or if it
32033214
returns \const{FALSE}, \const{FALSE} is returned.
32043215
3216+
A released blob keeps its type, so blob/2 still reports it, but it is
3217+
recognised by blob_released/1. The system does not call the type's
3218+
write() or compare() callback for such a blob, as these would
3219+
dereference the object that is gone; it writes as
3220+
\mbox{\const{<}\arg{Type}\const{>(freed)}} and is ordered by handle.
3221+
32053222
PL_free_blob() may be called multiple times on the same
32063223
\ctype{atom_t}, provided the handle is still valid. Subsequent calls
32073224
after a successful call have no effect and return \const{FALSE}.

man/summary.plx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ suggest predicates from a keyword.
120120
\predicatesummary{bagof}{3}{Find all solutions to a goal}
121121
\predicatesummary{between}{3}{Integer range checking/generating}
122122
\predicatesummary{blob}{2}{Type check for a blob}
123+
\predicatesummary{blob_released}{1}{True if a blob's data was released}
123124
\predicatesummary{bounded_number}{3}{Number between bounds}
124125
\predicatesummary{break}{0}{Start interactive top level}
125126
\predicatesummary{break_hook}{6}{\hook{prolog} Debugger hook}

0 commit comments

Comments
 (0)