[m-dev.] Suggested interface for sequence types in lib-v2
Ralph Becket
rbeck at microsoft.com
Mon Oct 23 21:06:32 AEDT 2000
Here's the updated spec., having taken Fergus' comments on board.
%
----------------------------------------------------------------------------
%
% medison.sequences.m
% Ralph Becket <rbeck at microsoft.com>
% Wed Oct 11 12:05:57 BST 2000
% vim: ts=4 sw=4 et tw=0 wm=0 ff=unix
%
% This is a draft spec. for the interfaces for various library data
% types for version 2 of the Mercury standard library. It is inspired
% by Chris Okasaki's Edison library, but does not attempt to conform
% in any particular way (partly due to aesthetic differences, partly
% due to the fact that Mercury is also a logic language.)
%
% Ideally, this specification would be presented as a set of typeclasses.
% Unfortunately, Mercury does not currently support constructor classes
% and functional dependencies on typeclasses, so we can't do this yet.
%
% The library is divided into four sections:
% - Sequences
% - The collection hierarchy
% - The map hierarchy
% - Miscellaneous
%
% XXX We really want to parameterise the modes for these things where
% appropriate when this facility becomes available.
%
% XXX For now I've omitted documentation where I believe things are
% obvious. Proper documentation needs to be added.
%
% XXX I think we should provide default implementations for various
% methods, e.g.
%
% singleton(X) = cons(X, new)
% snoc(X, Xs) = append(Xs, singleton(X))
% from_list(Xs) = foldr(cons, Xs, new)
% etc.
%
%
----------------------------------------------------------------------------
%
%
----------------------------------------------------------------------------
%
%
----------------------------------------------------------------------------
%
% SEQUENCES
%
----------------------------------------------------------------------------
%
%
----------------------------------------------------------------------------
%
:- type seq(T). % A sequence of T objects.
% CONSTRUCTORS
:- func new = seq(T).
:- func singleton(T) = seq(T).
:- func cons(T, seq(T)) = seq(T).
:- func snoc(T, seq(T)) = seq(T). % RAO'K hates this name.
:- func from_list(list(T)) = seq(T).
:- func n_copies(int, T) = seq(T).
% DESTRUCTORS
% XXX Should we overload [_|_] for sequences?
:- func head(seq(T)) = T.
:- func semidet_head(seq(T)) = T is semidet.
:- func tail(seq(T)) = seq(T).
:- func semidet_tail(seq(T)) = seq(T) is semidet.
:- pred head_tail(seq(T)::in, T::out, seq(T)::out).
:- pred semidet_head_tail(seq(T)::in, T::out, seq(T)::out) is semidet.
:- func last(seq(T)) = T.
:- func semidet_last(seq(T)) = T is semidet.
% not empty(S) => (S = snoc(last(S), all_but_last(S)))
%
:- func all_but_last(seq(T)) = seq(T).
:- func semidet_all_but_last(seq(T)) = seq(T) is semidet.
% XXX Do we want {semidet_,}all_but_last_last/1?
% OBSERVERS
:- pred empty(seq(T)::in) is semidet.
:- func length(seq(T)) = int.
:- func to_list(seq(T)) = list(T).
:- pred contains(seq(T)::in, T::in) is semidet.
% project_member/2 simply reverses the argument order of member/2.
% It may be more useful for higher order programming.
%
:- pred member(T::out, seq(T)::in) is nondet.
:- pred project_member(seq(T)::in, T::out) is nondet.
% CONCATENATION AND REVERSAL
:- func append(seq(T), seq(T)) = seq(T).
:- func seq(T) ++ seq(T) = seq(T). % Synonym for append/2.
:- func reverse(seq(T)) = seq(T).
:- func rev_append(seq(T), seq(T)) = seq(T).
:- func condense(seq(seq(T))) = seq(T).
% MAPPING AND FOLDING
:- func map(func(T1) = T2, seq(T1)) = seq(T2).
% Ignores those members for which F is not defined.
%
:- func partial_map(func(T1) = T2, seq(T1)) = seq(T2).
:- mode partial_map(func(in) = out is semidet, in) = out is det.
:- pred partial_filter_map(func(T1) = T2, seq(T1), seq(T2), seq(T1)).
:- mode partial_filter_map(func(in) = out is semidet, in, out, out) is det.
% Removed until sufficient demand has been demonstrated.
% Equivalent code would be
% condense(map(F, S))
% or, more efficiently,
% foldl(func(X, Xs) = F(X) ++ Xs, new, S)
%
% :- func condense_map(func(T1) = seq(T2), seq(T1)) = seq(T2).
% The suffix `_1' indicates that the starting value is taken as
% the head of the sequence. It is an error to pass an empty
% sequence to such a function.
:- func foldl(func(T1, T2) = T2, T2, seq(T1)) = T2.
:- func foldl_1(func(T1, T1) = T1, seq(T1)) = T1.
:- func foldr(func(T1, T2) = T2, T2, seq(T1)) = T2.
:- func foldr_1(func(T1, T1) = T1, seq(T1)) = T1.
% Reduce assumes the function argument to be associative and is
% free to bracket in any order.
% e.g. reduce_1((*), [W, X, Y, Z]) = (W*X)*(Y*Z) = (W*(X*Y))*Z = etc.
%
:- func reduce(func(T1, T1) = T1, T1, seq(T1)) = T1.
:- func reduce_1(func(T1, T1) = T1, seq(T1)) = T1.
% XXX Do we want partial_{foldl,reduce} etc?
% SUBSEQUENCES
:- func take_n(int, seq(T)) = seq(T). % Error if length(S) < N.
:- func take_up_to_n(int, seq(T)) = seq(T).
:- func drop_n(int, seq(T)) = seq(T). % Error if length(S) < N.
:- func drop_up_to_n(int, seq(T)) = seq(T).
% split_after_n(N, S, F, B) <=> F = take_n(N, S), B = drop_n(N, S).
%
:- pred split_after_n(int::in, seq(T)::in, seq(T)::out, seq(T)::out).
:- pred split_after_n_or_end(int::in, seq(T)::in, seq(T)::out, seq(T)::out).
% subseq(I, N, S) = take_n(N, drop_n(I, S))
%
% safe_subseq(I, N, S) = take_up_to_n(N, drop_up_to_n(I, S))
%
:- func subseq(int, int, seq(T)) = seq(T).
:- func safe_subseq(int, int, seq(T)) = seq(T).
:- func filter(pred(T), seq(T)) = seq(T).
:- mode filter(pred(in) is semidet, in) = out is det.
% partition(P, S, Ys, Ns) <=> Ys = filter(P, S), Ns = filter(isnt(P), S)
%
:- pred partition(pred(T), seq(T), seq(T), seq(T)).
:- mode partition(pred(in) is semidet, in, out, out) is det.
:- func take_while(pred(T), seq(T)) = seq(T).
:- mode take_while(pred(in) is semidet, in) = out is det.
:- func drop_while(pred(T), seq(T)) = seq(T).
:- mode drop_while(pred(in) is semidet, in) = out is det.
% split_where(P, S, F, B) <=> F is the prefix of S for which P fails,
% B is the remainder of S.
:- pred split_where(pred(T), seq(T), seq(T), seq(T)).
:- mode split_where(pred(in) is semidet, in, out, out) is det.
% REMOVING MEMBERS
% Error unless S `contains` X.
:- func delete_first(T, seq(T)) = seq(T).
:- func delete_first_if_any(T, seq(T)) = seq(T).
:- func delete_all(T, seq(T)) = seq(T).
% INDEX BASED OPERATIONS
% index_in(S, head(S)) = 0.
:- func index_in(seq(T), T) = int. % Error unless S `contains` X.
:- func semidet_index_in(seq(T), T) = int is semidet.
:- func lookup(seq(T), int) = T. % Error unless 0 =< I < length(S).
% The jury is still out on this one.
% :- func seq(T) @ int = T. % Synonym for lookup/2.
:- func search(seq(T), int) = T is semidet.
% replace(S, I, X) = take_n(I - 1, S) ++ cons(X, drop_up_to_n(I, S))
% Error unless 0 =< I < length(S).
%
:- func replace(seq(T), int, T) = seq(T).
% change(S, I, F) = replace(S, I, F(S `lookup` I)).
% Error unless 0 =< I < length(S).
%
:- func change(seq(T), int, func(T) = T) = seq(T).
% XXX Do we want {filter,map,foldl,foldr,reduce}_with_index etc?
% ZIPPING AND UNZIPPING
% interleave([A, B, C], [X, Y, Z]) = [A, X, B, Y, C, Z].
% Error unless length(S1) = length(S2) + N for N in {0, 1}.
%
:- func interleave(seq(T), seq(T)) = seq(T).
% uninterleave([A, X, B, Y, C, Z], [A, B, C], [X, Y, Z]).
%
:- pred uninterleave(seq(T)::in, seq(T)::out, seq(T)::out) is det.
% Error unless length(S1) = length(S2).
%
:- func zip(func(T1, T2) = T3, seq(T1), seq(T2)) = seq(T3).
%
----------------------------------------------------------------------------
%
%
----------------------------------------------------------------------------
%
--
Ralph Becket | MSR Cambridge | rbeck at microsoft.com
--------------------------------------------------------------------------
mercury-developers mailing list
Post messages to: mercury-developers at cs.mu.oz.au
Administrative Queries: owner-mercury-developers at cs.mu.oz.au
Subscriptions: mercury-developers-request at cs.mu.oz.au
--------------------------------------------------------------------------
More information about the developers
mailing list