type safety (was Re: FAQ terminology harmonisation)

C.Reinke C.Reinke@REDACTED
Fri Apr 4 01:28:09 CEST 2003


> We won't have type safety until we have truly opaque types.
> We have a warning not to let type information leak out of a module.
> But we currently *have* to implement types on top of existing types:
> usually tuples, or lists, or binaries could be used, or even refs or
> pids or atoms... but all of these can be confused with others of the
> same type, used in a different role.
..
> Because no matter how well I try to make it opaque, I can't.

While I don't disagree with the general message, you may be
overly pessimistic about data abstraction. In fact, I thought
this was handled in the programming rules, but re-reading
those after your message shows it isn't.

There are some fairly old tricks (sorry, don't have the 
references here right now - Reynolds, and Morris, I think)
that allow us to do slightly better than the queue example
in the programming rules - if our language supports 
first-class functions or procedures. 

The basic idea is that if there is one abstract type in the 
language, we might be able to build other abstractions on top
of that. Functions fit that bill wonderfully (the only thing
you can do with a function is to apply it). In particular, 
there is no need for a module implementing an abstract type
to expose anything but the API functions.

Cheers,
Claus

% here's the queue from the erlang programming rules, 
% 3.11 Don't allow private data structures 
%      to "leak" out of a module

% note that this module exports accessor functions 
% for the "abstract" queue type, but _also_ externalises 
% and thus exposes the "hidden" list representation:
% it is passed unprotected in and out of the API functions!
%% -module(queue).
%% -export([new/0, add/2, fetch/1]).
%% 
%% new() ->
%%   [].
%% 
%% add(Item, Q) ->
%%   lists:append(Q, [Item]).
%% 
%% fetch([H|T]) ->
%%   {ok, H, T};
%% fetch([]) ->
%%   empty.

% choosing a functional representation instead,
% the only thing exported and passed around are 
% the API functions, whereas the internal list
% representation is hidden in partial applications
% of the internal functions:
-module(queue).
-export([new/0, add/2, fetch/1]).

% internal API
mk_queue(Q) ->
  { add_fct(Q), fetch_fct(Q) }.

add_fct(Q) -> fun(Item) ->
  NewQ = lists:append(Q, [Item]),
  mk_queue(NewQ)
  end.

% sigh.. lazy evaluation would help here
fetch_fct([H|T]) -> fun(Delay) ->   
  {ok, H, mk_queue(T) }
  end;
fetch_fct([]) -> fun(Delay) ->
  empty
  end.

% the old API, for convenience.
% hides both the internal list and the Delay/force handling..
new() -> mk_queue([]).

add({Add,Fetch}, Item) ->
  Add(Item).
  
fetch({Add,Fetch}) ->
  Fetch(force).





More information about the erlang-questions mailing list