Re: handling state the database way
Jan Wielemaker <[email protected]>
| Newsgroups | gmane.comp.ai.prolog.swi |
|---|---|
| Message-ID | <[email protected]> |
On 02/16/2014 11:26 PM, Richard A. O'Keefe wrote:
>
> On 15/02/2014, at 7:28 AM, Abdallah, Samer wrote:
> [To paraphrase:] Why can't I *see* the structure?
>
> May I suggest a module?
That is always a good start :-)
>
> First draft:
>
> :- module(single_fact, [
> initialise/2,
> update/2,
> finalise/1
> ]).
> :- meta_predicate
> update(+, 2).
>
> :- dynamic store/2.
>
> initialise(Key, Datum) :-
> retractall(store(Key,_)),
> assert(store(Key, Datum)).
>
> update(Key, Updater) :-
> retract(store(Key, Old)),
> !, /* green cut */
> ( call(Updater, Old, New) -> F = true
> ; New = Old, F = fail
> ),
> assert(store(Key, New)),
> F == true.
Samer has a point wrt. atomicy ... There are quite
a few options though. Possibly this should be considered
out of the scope of a state module. Still, something that
guarantees consistency of the store if there is an exception
in Updater is probably desirable.
> finalise(Key) :-
> retractall(store(Key,_)).
>
>>
>> :- dynamic some_pred/1.
>> update(Event) :-
>> retract(some_pred(OldState)),
>> update_something(Event,OldState,NewState),
>> assert(some_pred(NewState)).
>>
>> initialise :
>> retractall(some_pred(_)),
>> assert(some_pred( <Initial state> )).
>
> This becomes:
>
> initialise :-
> single_fact:initialise(<<key>>, <<initial state>>).
>
> update(Event) :-
> single_fact:update(<<key>>, update_something(Event)).
>
>>
>> Obviously, one could write a library to do this, but I think there might be
>> benefit in performance, atomicity and control over backtracking behaviour
>> that might come from it being integrated at a lower level, like
>> flag/3.
Flag/3 should be considered deprecated. It just has no sensible place
between dynamic clauses, thread_local clauses, records, global
variables, destructive assignment and flags (hope I'm complete).
> Yes, but the library stage comes *first*.
It is certainly a good way to explore a way to solve this mess. There
are quite a few dimensions that do make sense though:
- Sharing between threads
- Thread safety (for shared state)
- Name space and scoping
- Identity (sharing of variables)
Except records vs. dynamic predicates, all mechanisms have their
own properties (could be formulated more cleanly).
- dynamic predicates
Shared between threads, module-based name scoping, multiple
clauses (can be interpreted in several ways). Both store
and load copy the value.
- thread local predicates
As above, but not shared between threads.
- Non-backtrackable global variables
Global name space (bug, should be module), storing copies,
load shares. One value per key, thread local.
- Backtrackable global variables
lobal name space (bug, should be module), both store and
load shares. One value per key, thread local, backtracks.
- Non-backtrackable destructive assignment
Local identity, otherwise as Non-backtrackable global variables.
Have to be careful with term identity.
- Backtrackable destructive assignment
Local identity, otherwise as Backtrackable global variables.
Have to be careful with term identity.
- Prolog flags
Global name space, copy on store and load, newly created
threads copy from creator (copy-on-write implementation).
> By the way, there's a proposal for adding mutable variables to
> ISO Prolog, relevant to this. I am afraid that after printing
> a copy of the proposal and skimming it, I hurled it into the
> nearest paper recycling bin in horror and disgust at the very
> idea. But if Prolog _is_ to end up with green worms wriggling
> behind its eyes (see Stross's Laundry series for the key to
> this metaphor) it would be well if they were the *same* green
> worms.
That is the idea, judging from the discussions I followed. Simple matter
of fact is that most Prolog systems have green worms in this area. They
should (IMHO), like global variables and mostly also attributed
variables, be used as a building block for high level primitives.
For example, aggregate(count, ...) uses this. Some of the other
aggregation predicates should do the same. The advantage is performance,
notably because thread locality, naming conflicts, exception handling,
etc. all come safe and simple at no additional cost.
>> What do you think? Is it the case that the cost of doing unification on the
>> state means that it is not worth the small gain of making it a built-in?
>
> The cost of unification is unlikely to be high.
> What counts more is how expensive everything *else* the applications
> are doing. And really, the only way to find out is to start with a
> simple library module and do some profiling.
>
> I will say that any Prolog program that spends a lot of its time
> in the single_fact: module will be in desperate need of
> restructuring. Or possibly of rewriting in Visual Basic.NET.
I think that the challenge is to come up with a clean and understandable
interface. Except for some issues with thread synchronisation, it should
be possible to implement everything quite satisfactory as a Prolog module.
The advantage would be that there is one point where everyone can go to
see what choices there are.
Cheers --- Jan