%--------------------------------------------------% % 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). %--------------------------------------------------% %--------------------------------------------------%