Next: , Previous: std_util, Up: Top   [Contents]


88 store

%--------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%--------------------------------------------------%
% Copyright (C) 1994-1997, 2000-2008, 2010-2011 The University of Melbourne.
% Copyright (C) 2013-2022 The Mercury team.
% This file is distributed under the terms specified in COPYING.LIB.
%--------------------------------------------------%
%
% File: store.m.
% Main author: fjh.
% Stability: low.
%
% This file provides facilities for manipulating mutable stores.
% A store can be considered a mapping from abstract keys to their values.
% A store holds a set of nodes, each of which may contain a value of any
% type.
%
% Stores may be used to implement cyclic data structures such as circular
% linked lists, etc.
%
% Stores can have two different sorts of keys:
% mutable variables (mutvars) and references (refs).
% The difference between mutvars and refs is that mutvars can only be updated
% atomically, whereas it is possible to update individual fields of a
% reference one at a time (presuming the reference refers to a structured
% term).
%
%--------------------------------------------------%
%--------------------------------------------------%

:- module store.
:- interface.

:- import_module io.

%--------------------------------------------------%

    % Stores and keys are indexed by a type S of typeclass store(S) that
    % is used to distinguish between different stores. By using an
    % existential type declaration for `init'/1 (see below), we use the
    % type system to ensure at compile time that you never attempt to use
    % a key from one store to access a different store.
    %
:- typeclass store(T) where [].
:- type store(S).

:- instance store(io).
:- instance store(store(S)).

    % Initialize a new store.
    %
:- some [S] pred init(store(S)::uo) is det.

%--------------------------------------------------%
%
% Mutvars
%

    % generic_mutvar(T, S):
    % A mutable variable holding a value of type T in store S.
    %
    % The mutable variable interface is inherently not thread-safe.
    % It is the programmer's responsibility to synchronise accesses to a
    % mutable variable from multiple threads where that is possible,
    % namely variables attached to the I/O state.
    %
:- type generic_mutvar(T, S).
:- type io_mutvar(T) == generic_mutvar(T, io).
:- type store_mutvar(T, S) == generic_mutvar(T, store(S)).

    % Create a new mutable variable, initialized with the specified value.
    %
:- pred new_mutvar(T::in, generic_mutvar(T, S)::out, S::di, S::uo)
    is det <= store(S).

    % copy_mutvar(OldMutvar, NewMutvar, S0, S) is equivalent to the sequence
    %   get_mutvar(OldMutvar, Value, S0, S1),
    %   new_mutvar(NewMutvar, Value, S1, S )
    %
:- pred copy_mutvar(generic_mutvar(T, S)::in, generic_mutvar(T, S)::out,
    S::di, S::uo) is det <= store(S).

    % Lookup the value stored in a given mutable variable.
    %
:- pred get_mutvar(generic_mutvar(T, S)::in, T::out,
    S::di, S::uo) is det <= store(S).

    % Replace the value stored in a given mutable variable.
    %
:- pred set_mutvar(generic_mutvar(T, S)::in, T::in,
    S::di, S::uo) is det <= store(S).

    % new_cyclic_mutvar(Func, Mutvar, !S):
    %
    % Create a new mutable variable, whose value is initialized with the value
    % returned from the specified function Func. The argument passed to the
    % function is the mutvar itself, whose value has not yet been initialized
    % (this is safe because the function does not get passed the store, so it
    % cannot examine the uninitialized value).
    %
    % This predicate is useful for creating self-referential values such as
    % circular linked lists. For example:
    %
    %   :- type clist(T, S)
    %       --->    node(T, generic_mutvar(clist(T, S), S)).
    %
    %   :- pred init_cl(T::in, clist(T, S)::out, S::di, S::uo)
    %       is det <= store(S).
    %
    %   init_cl(X, CList, !Store) :-
    %       new_cyclic_mutvar(func(CL) = node(X, CL), CListVar, !Store),
    %       get_mutvar(CListVar, CList, !Store).
    %
:- pred new_cyclic_mutvar((func(generic_mutvar(T, S)) = T)::in,
    generic_mutvar(T, S)::out, S::di, S::uo) is det <= store(S).

%--------------------------------------------------%
%
% References
%

    % generic_ref(T, S):
    %
    % A reference to value of type T in store S.
    %
    % The reference interface is inherently not thread-safe.
    % It is the programmer's responsibility to synchronise accesses to a
    % reference from multiple threads where that is possible,
    % namely references attached to the I/O state.
    %
:- type generic_ref(T, S).
:- type io_ref(T, S) == generic_ref(T, io).
:- type store_ref(T, S) == generic_ref(T, store(S)).

    % new_ref(Val, Ref):
    %   /* In C: Ref = malloc(...); *Ref = Val; */
    %
    % Given a value of any type T, insert a copy of the term
    % into the store and return a new reference to that term.
    % (This does not actually perform a copy, it just returns a view
    % of the representation of that value.
    % It does however allocate one cell to hold the reference;
    % you can use new_arg_ref to avoid that.)
    %
:- pred new_ref(T::di, generic_ref(T, S)::out,
    S::di, S::uo) is det <= store(S).

    % ref_functor(Ref, Functor, Arity):
    %
    % Given a reference to a term, return the functor and arity of that term.
    %
:- pred ref_functor(generic_ref(T, S)::in, string::out, int::out,
    S::di, S::uo) is det <= store(S).

    % arg_ref(Ref, ArgNum, ArgRef):
    %   /* Pseudo-C code: ArgRef = &Ref[ArgNum]; */
    %
    % Given a reference to a term, return a reference to
    % the specified argument (field) of that term
    % (argument numbers start from zero).
    % It is an error if the argument number is out of range,
    % or if the argument reference has the wrong type.
    %
:- pred arg_ref(generic_ref(T, S)::in, int::in,
    generic_ref(ArgT, S)::out, S::di, S::uo) is det <= store(S).

    % new_arg_ref(Val, ArgNum, ArgRef):
    %   /* Pseudo-C code: ArgRef = &Val[ArgNum]; */
    %
    % Equivalent to `new_ref(Val, Ref), arg_ref(Ref, ArgNum, ArgRef)',
    % except that it is more efficient.
    % It is an error if the argument number is out of range,
    % or if the argument reference has the wrong type.
    %
:- pred new_arg_ref(T::di, int::in, generic_ref(ArgT, S)::out,
    S::di, S::uo) is det <= store(S).

    % set_ref(Ref, ValueRef):
    %   /* Pseudo-C code: *Ref = *ValueRef; */
    %
    % Given a reference to a term (Ref),
    % a reference to another term (ValueRef),
    % update the store so that the term referred to by Ref
    % is replaced with the term referenced by ValueRef.
    %
:- pred set_ref(generic_ref(T, S)::in, generic_ref(T, S)::in,
    S::di, S::uo) is det <= store(S).

    % set_ref_value(Ref, Value):
    %   /* Pseudo-C code: *Ref = Value; */
    %
    % Given a reference to a term (Ref), and a value (Value),
    % update the store so that the term referred to by Ref
    % is replaced with Value.
    %
:- pred set_ref_value(generic_ref(T, S)::in, T::di,
    S::di, S::uo) is det <= store(S).

    % Given a reference to a term, return that term.
    % Note that this requires making a copy, so this pred may
    % be inefficient if used to return large terms; it
    % is most efficient with atomic terms.
    % XXX current implementation buggy (does shallow copy)
    %
:- pred copy_ref_value(generic_ref(T, S)::in, T::uo,
    S::di, S::uo) is det <= store(S).

    % Same as above, but without making a copy. Destroys the store.
    %
:- pred extract_ref_value(S::di, generic_ref(T, S)::in, T::out)
    is det <= store(S).

%--------------------------------------------------%
%
% Nasty performance hacks
%
% WARNING: use of these procedures is dangerous!
% Use them only as a last resort, only if performance is critical, and only if
% profiling shows that using the safe versions is a bottleneck.
%
% These procedures may vanish in some future version of Mercury.

    % unsafe_arg_ref is the same as arg_ref,
    % and unsafe_new_arg_ref is the same as new_arg_ref
    % except that they doesn't check for errors,
    % and they don't work for no_tag types (types with
    % exactly one functor which has exactly one argument),
    % and they don't work for arguments which occupy a word with other
    % arguments,
    % and they don't work for types with >4 functors.
    % If the argument number is out of range,
    % or if the argument reference has the wrong type,
    % or if the argument is a no_tag type,
    % or if the argument uses a packed representation,
    % then the behaviour is undefined, and probably harmful.

:- pred unsafe_arg_ref(generic_ref(T, S)::in, int::in,
    generic_ref(ArgT, S)::out, S::di, S::uo) is det <= store(S).

:- pred unsafe_new_arg_ref(T::di, int::in, generic_ref(ArgT, S)::out,
    S::di, S::uo) is det <= store(S).

%--------------------------------------------------%
%--------------------------------------------------%


Next: , Previous: std_util, Up: Top   [Contents]