[m-rev.] for review: Define equality for version_hash_tables
Peter Wang
novalazy at gmail.com
Mon May 27 13:03:14 AEST 2013
On Mon, 27 May 2013 10:45:40 +1000, Paul Bone <paul at bone.id.au> wrote:
> For review by anyone.
>
> Define equality for version_hash_tables.
>
> library/version_hash_table.m:
> Add user-defined equality to the version_hash_table/2 type.
>
> Use a notag type to make deconstructions easier.
>
> Introduce a new function to handle deconstructions that promise that all
> theoretical solutions are equivilent.
equivalent
>
> Rename fields of the inner structure as the num_occupants function
> no-longer has the same type as the function generated by the field of
> the same name.
> ---
> library/version_hash_table.m | 126 ++++++++++++++++++++++++++++++++++---------
> 1 file changed, 102 insertions(+), 24 deletions(-)
>
> diff --git a/library/version_hash_table.m b/library/version_hash_table.m
> index 04d3389..3640ca5 100644
> --- a/library/version_hash_table.m
> +++ b/library/version_hash_table.m
> @@ -226,17 +226,22 @@
> :- import_module require.
> :- import_module string.
> :- import_module type_desc.
> +:- import_module unit.
> :- import_module univ.
> :- import_module version_array.
>
> %-----------------------------------------------------------------------------%
>
> :- type version_hash_table(K, V)
> + ---> ht_outer(ht_inner(K, V))
> + where equality is version_hash_table_equals.
> +
> +:- type ht_inner(K, V)
> ---> ht(
> - num_occupants :: int,
> - max_occupants :: int,
> - hash_pred :: hash_pred(K),
> - buckets :: buckets(K, V)
> + ht_num_occupants :: int,
> + ht_max_occupants :: int,
> + ht_hash_pred :: hash_pred(K),
> + ht_buckets :: buckets(K, V)
> ).
>
> :- type buckets(K, V) == version_array(hash_table_alist(K, V)).
> @@ -282,7 +287,7 @@ init_2(HashPred, N, MaxOccupancy, NeedSafety) = HT :-
> NeedSafety = no,
> Buckets = version_array.unsafe_new(NumBuckets, ht_nil)
> ),
> - HT = ht(0, MaxOccupants, HashPred, Buckets)
> + HT = ht_outer(ht(0, MaxOccupants, HashPred, Buckets))
> ).
>
> %-----------------------------------------------------------------------------%
> @@ -297,7 +302,9 @@ unsafe_new_default(HashPred) = unsafe_init(HashPred, 7, 0.9).
>
> %-----------------------------------------------------------------------------%
>
> -num_buckets(HT) = size(HT ^ buckets).
> +num_buckets(HT) = size(inner(HT) ^ ht_buckets).
> +
> +num_occupants(HT) = inner(HT) ^ ht_num_occupants.
>
I think the changes would be cleaner by (un)wrapping within the heads
only, e.g.
num_occupants(wrap(HT)) = HT ^ ht_num_occupants.
copy(wrap(HT0)) = wrap(HT) :- ...
where
:- func wrap(ht_inner(K, V)) = version_hash_table(K, V).
:- mode wrap(out) = in is det.
:- mode wrap(in) = out is det.
Also, I would like to see any differences with hash_table.m minimised.
> +:- pred version_hash_table_equals(version_hash_table(K, V)::in,
> + version_hash_table(K, V)::in) is semidet.
You could name it "version_hash_table.equals".
> +% This pragma is required becase termination analysis can't analyse the use
> +% of higher order code.
> +:- pragma terminates(version_hash_table_equals/2).
because
> +version_hash_table_equals(A, B) :-
> + promise_pure
> + ( semipure pointer_equals(A, B) ->
> + true
> + ;
> + inner(A) ^ ht_num_occupants = inner(B) ^ ht_num_occupants,
> + % XXX: Max occupants is allowed to differ and hash pred may not be
> + % compared.
> + fold(compare_item(B), A, unit, _)
> + ).
Please write out the deconstructions so it's easy to tell which fields
are ignored. Delete the remark about the max occupants.
> +
> +:- pred compare_item(version_hash_table(K, V)::in, K::in, V::in,
> + unit::in, unit::out) is semidet.
> +
> +compare_item(Table, K, V, unit, unit) :-
> + search(Table, K, V).
> +
> + % We create inner (and the extra level of types) in order to make user
> + % defined equality easier to support.
> + %
> +:- pred inner(version_hash_table(K, V)::in, ht_inner(K, V)::out) is det.
> +
> +inner(Outer, Inner) :-
> + promise_equivalent_solutions [Inner]
> + Outer = ht_outer(Inner).
Add brackets around the sub-goal.
> +:- func inner(version_hash_table(K, V)) = ht_inner(K, V).
> +:- pragma inline(inner/1).
> +
> +inner(Outer) = Inner :-
> + inner(Outer, Inner).
Not sure why the extra predicate is necessary.
> +:- semipure pred pointer_equals(T::in, T::in) is semidet.
> +:- pragma inline(pointer_equals/2).
> +
> +:- pragma foreign_proc("C", pointer_equals(A::in, B::in),
> + [promise_semipure, thread_safe, will_not_call_mercury,
> + will_not_throw_exception, terminates],
> + "
> + SUCCESS_INDICATOR = A == B;
> + ").
Use standard indentation. Add brackets around the expression.
> +:- pragma foreign_proc("Java", pointer_equals(A::in, B::in),
> + [promise_semipure, thread_safe, will_not_call_mercury,
> + will_not_throw_exception, terminates],
> + "
> + SUCCESS_INDICATOR = A == B;
> + ").
> +
> +:- pragma foreign_proc("C#", pointer_equals(A::in, B::in),
> + [promise_semipure, thread_safe, will_not_call_mercury,
> + will_not_throw_exception, terminates],
> + "
> + SUCCESS_INDICATOR = System.Object.ReferenceEquals(A, B);
> + ").
> +
> +% Conservative default if a backend does not have pointer equality, such as
> +% Erlang. (Erlang does have erts_debug:same/1 but I don't know if we can
> +% rely on that.)
> +pointer_equals(A, B) :- false.
Unused variables. Maybe you need semidet_fail?
Peter
More information about the reviews
mailing list