anchor.diff:  
   %% Example: go through an ets table to spec a field based on a limited
   %% number of rows processed:
  -%%   fold_tab_field(my_ets_table, #my_ets_record.my_field, 1000)
  +%%   spec_tab_field(my_ets_table, #my_ets_record.my_field, 1000)
  +%% Sending eg. 'inf' as Limit disables the limit.
   
   -define(ex, ex).
   -ifdef(ex).
  -fold_tab_field(Tab, FieldNo, Limit) ->
  -    fold_tab_field(Tab, FieldNo, ets:first(Tab), new(), Limit).
  -
  -fold_tab_field(Tab, FieldNo, Limit, Opts) ->
  +spec_tab_field(Tab, FieldNo, Limit) ->
  +    fold_tab_field(fun add/2, new(), Tab, FieldNo, Limit).
  +
  +spec_tab_field(Tab, FieldNo, Limit, Opts) ->
       setopts(Opts),
  -    fold_tab_field(Tab, FieldNo, ets:first(Tab), new(), Limit).
  -
  -fold_tab_field(_Tab, _FieldNo, _Key, Spec, 0) -> Spec;
  -fold_tab_field(_Tab, _FieldNo, '$end_of_table', Spec, _Limit) -> Spec;
  -fold_tab_field(Tab, FieldNo, Key, Spec0, Limit) ->
  +    fold_tab_field(fun add/2, new(), Tab, FieldNo, Limit).
  +
  +
  +fold_tab_field(Fun, Acc0, Tab, FieldNo, Limit) ->
  +    Progress = getopt(progress),
  +    progress_init_output(Progress),
  +    fold_tab_field(Fun, Acc0, Tab, FieldNo, Limit, Progress, 0, ets:first(Tab)).
  +
  +fold_tab_field(_Fun, Acc, _Tab, _FieldNo, 0, Progress, Count, _Key) ->
  +    progress_final_output(Progress, Count),
  +    Acc;
  +fold_tab_field(_Fun, Acc, _Tab, _FieldNo, _Limit, Progress, Count, '$end_of_table') ->
  +    progress_final_output(Progress, Count),
  +    Acc;
  +fold_tab_field(Fun, Acc, Tab, FieldNo, Limit, Progress0, Count, Key) ->
  +    Progress = progress_output(Progress0, Count+1),
       [Rec] = ets:lookup(Tab, Key),
  -    Data = element(FieldNo, Rec),
  -    %io:format("Data: ~p~n", [Data]),
  -    Spec = add(Data, Spec0),
  -    fold_tab_field(Tab, FieldNo, ets:next(Tab, Key), Spec, Limit-1).
  +    Field = element(FieldNo, Rec),
  +    fold_tab_field(Fun, Fun(Field, Acc), Tab, FieldNo,
  +                   counter_dec(Limit), Progress, Count+1,
  +                   ets:next(Tab, Key)).
   -endif.
   
  -
  -%% dataspec output can be controlled to some extent. Supported options:
  +%% output progress information if configured to do so
  +progress_output(false, _Count) -> false;
  +progress_output(1, Count) ->
  +    Progress = getopt(progress),
  +    case Count div Progress rem 50 of
  +        0 -> io:format("~B", [Count]);
  +        _ -> io:put_chars(".")
  +    end,
  +    Progress;
  +progress_output(Progress, _Count) -> counter_dec(Progress).
  +
  +progress_init_output(false)    -> false;
  +progress_init_output(Progress) -> io:format("progress (every ~B): ", [Progress]).
  +
  +progress_final_output(false, _Count) -> false;
  +progress_final_output(_Prog, Count)  -> io:format("~nprocessed: ~B~n~n", [Count]).
  +
  +%% decrement counters that might be disabled by being set to an atom
  +counter_dec(N) when is_integer(N) -> N-1;
  +counter_dec(A)                    -> A.
  +
  +%% dataspec operation can be controlled to some extent. Supported options:
  +%%
  +%% options influencing spec semantics:
   %%
   %%   mag: integer()
   %%     subgrouping of numerals by magnitude
  -%%        0: turn off subgrouping;
  -%%        N: subgroup N orders of magnitude in one; defults to 3
  +%%       0: turn off subgrouping;
  +%%       N: subgroup N orders of magnitude into one (defaults to 3)
   %%
   %%   strlen: true | false
   %%     subgrouping of strings by length
   %%
  +%% other options:
  +%%
  +%%   progress: pos_integer() | false
  +%%     output progress information
  +%%       false: no output
  +%%       N: output progress info every N samples
  +
   default_opts() ->
  -    [ {mag, 3}
  -    , {strlen, false}
  +    %% The following table specifies the options interpreted
  +    %%
  +    %%      name: name of option
  +    %% undefined: value to use if option is missing
  +    %%   novalue: value to use if option is set to true
  +    %%            (implicit if the option is supplied with no value)
  +
  +    %% name          undefined     novalue
  +    [ {mag,          0,            3}
  +    , {strlen,       false,        true}
  +    , {progress,     false,        100000}
       ].
   
   %% NB. using the process dict is ugly; passing Opts around is uglier.
  
positive.code:     merge_spec(Spec, [{list, length(L)}], fun f_merge_recur/2, L);
  add_list(Type, L, Spec) -> % specially classified list, eg. str_alpha
      %% the strlen option enables subgrouping strings by length
      Path = case proplists:get_value(strlen, getopts(), false) of
                 false -> [Type];
                 true  -> [Type, {len, length(L)}]
             end,
  
negative.code: %% add a numeric under key 'integer' or 'float'
  add_numeric(Key, Value, Spec) ->
      %% interpret the {mag, N} option as:
      %% - if N = 0: no subgrouping based on magnitude
      %% - if N > 0: group N orders of magnitude into one subspec
      Path = case getopt(mag) of
                 0 -> [Key];
                 N -> [Key, {mag, mag(Value, N)}]
             end,
      merge_spec(Spec, Path, fun f_merge_num/2, Value).
  
  