Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 73 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,26 @@ constructors a program makes as it runs. The module comment of
relation `intern/2`, the Id and its shape, the templates, the rewrite of a
clause, the two rules for a file that opts in, and the known defects.

### Calling a module that hands out layers

A module that does not opt in may read an Id one layer at a time and hand
out that layer, a plain compound whose arguments are Ids. In a file that
opts in, a plain `slot(Index)` written in a call would be rewritten into an
Id pattern and would not match it. `rewritten/2` names the argument
positions that hold such a layer: there the constructor stays as written
and only its arguments are rewritten. The first argument of
`hash_consing:represented/2` is such a position already.

```prolog
:- hash_consing:rewritten([slot(_), link(_, _)],
[layer_arguments([stored_layer(_, layer)])]).

slot_read(Slot, Index) :-
stored_layer(Slot, slot(Index)). % slot(Index) is a plain layer
linked_index(Link, Index) :-
stored_layer(Link, link(slot(Index), _)). % the inner slot/1 is an Id
```

## Templates are patterns

What is interned is chosen by patterns, not by constructors. A template is
Expand Down Expand Up @@ -117,6 +137,59 @@ keeps its meaning and loses the first argument index there. A body goal hands
such an occurrence over decided: what is bound of it must settle whether a
template matches, or the call raises an instantiation error.

## The store

The store belongs to the process: every thread and every engine interns into
it and reads its Ids, with nothing to install. An Id is never freed nor
reused, and two Ids are `==` exactly when their terms are. The size of the
store is read with `store_property/1`:

```prolog
?- hash_consing:store_property(bytes(Bytes)).
```

with the properties `ids(Count)`, `nodes(Count)` and `bytes(Bytes)`. The
Prolog flag `hash_consing_store_limit` bounds the store, as the stack and
table limits bound theirs: past it, the next insertion raises
`resource_error(hash_consing_store)` and the store keeps what it holds.

```prolog
?- set_prolog_flag(hash_consing_store_limit, 536870912).
```

A process that runs many jobs on one store, each in a thread of its own,
bounds each job by what it adds instead: `store_growth_bounded(Bytes)` lets
the calling thread grow the store by `Bytes` from the call on. Only the
thread's own insertions count, so jobs running in parallel do not charge
each other and a job is not charged with what earlier jobs interned. A
thread created later inherits the bound with a count of its own from zero,
as it inherits `stack_limit` and `table_space`. Interning a term already in
the store adds nothing and costs nothing against the budget.

```prolog
?- thread_create(( hash_consing:store_growth_bounded(268435456), job ), Thread, []).
```

An interned term is a value, not a cell: the store keeps a copy of the term
as it was when interned, and `setarg/3` or `nb_setarg/3` on the original or
on a layer read back changes that copy only. Keep mutable cells (thunks,
memory cells updated in place) out of the store. Never mutate an Id: a
handle that is not one the store gave crashes the process.

## Incompatible changes in 0.3.0

- The store is a fact of the process, no longer two global variables of the
loading thread: a thread or engine interns into it with nothing to
install, and code that read or set `hash_consing_store` or
`hash_consing_spellings` drops those reads.
- In a file that opts in, the template list handed to `internalized/3` or
`declared/1` is kept as written; 0.2.x rewrote it like any other argument.
- In a file that opts in, the first argument of `represented/2` is a plain
layer; 0.2.x rewrote it into an Id pattern, and the call raised.
- New predicates: `store_property/1`, `store_growth_bounded/1`,
`rewritten/2`; new Prolog flags `hash_consing_store_limit`,
`hash_consing_growth_budget`.

## Incompatible changes in 0.2.0

- A nested template requires its constructor: `apply(apply(*, _), _)` matches
Expand Down
2 changes: 1 addition & 1 deletion pack.pl
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name(hash_consing).
version('0.2.2').
version('0.3.0').
title('Hash-consed terms by clause rewriting').
keywords([interning, 'hash-consing', trie, term_expansion]).
author('Yang, Bo', 'yang-bo@yang-bo.com').
Expand Down
Loading
Loading