Version française
Home     About     Download     Resources     Contact us    
Browse thread
[newbie] Define and use records in sum types
[ Home ] [ Index: by date | by threads ]
[ Search: ]

[ Message by date: previous | next ] [ Message in thread: previous | next ] [ Thread: previous | next ]
Date: -- (:)
From: Frank Atanassow <franka@c...>
Subject: Re: [newbie] Define and use records in sum types
[reusing record labels by putting them in different modules]

Markus Mottl writes:
 > On Fri, 21 Jul 2000, Frank Atanassow wrote:
 > > But I stopped because inevitably, after some development, the type t
 > > becomes recursive, and you cannot have recursive types spanning modules,
 > > so I would have to go back to the straightforward method.
 > Inevitably? Why?

Because non-recursive datatypes are trivial, and baby programs are trivial,
but they grow up into non-trivial adults.

 > At least in the cases where I found a problem with building modules, I
 > always discovered an easy (and generally much cleaner) way to restructure
 > the design to get around it. In fact, such problems point me to design
 > errors. (Yes, there are rare examples, where redesigning does not help, but
 > I never felt the need to implement one for real purposes).

Let's not get into discussions about refactoring or recursive modules.

The fact is that in your example you used only the namespace-handling aspect
of modules to achieve field label overloading. The restriction against modules
being recursive has nothing to do with their namespace-handling aspect, but
rather implementation and type-theoretic issues. If you had opted to solve the
problem by just adding a prefix to the labels (or used my proposed
mechanism), and kept them in the same module, then you would not have had to
appeal to (largely unrelated) issues like refactoring, and would have happily
let there be recursive dependencies among the types in question, if the
situation warranted it.

What I'm saying is that, because the module system treats both the
namespace-handling aspect and the type-theoretic aspects in one language
construct, the restrictions on one aspect carry over to the other. Therefore,
if you use the module system solely for namespace-handling, it's liable to
bite you in the end.

Of course for something like salary records, you aren't likely to engender
any recursion no matter how long you develop the program; my objection is to
the general applicability of the technique.

 > > It's true that the uses often coincide, but I think it obscures the
 > > issues. In particular, I think it encourages programmers to conflate
 > > syntactic information-hiding (like not exporting some constructors or
 > > functions) with type-theoretic information-hiding (like abstracting a
 > > type with a functor). The first ought to be considered only as an
 > > engineering technique to reduce complexity. The second OTOH is a logical
 > > technique to increase flexibility of implementation.
 > But how about "capturing logical invariants"? You need both ways of
 > information hiding there: restricting the number of exported functions
 > *and* restricting their types! Otherwise there could be ways to destroy the
 > invariant.

Can you be more specific about "capturing logical invariants"? I don't know
quite what you mean.

An instance I can think of where access to names could have semantic
significance is if it has to do with generativity. I hate generativity with a
passion, but eliminating it from ML modules would require a wholesale revision
of the module system, which it is not my object to propose here. So, I will
concede that generativity may cause name access to acquire semantic
significance. (But I hope you agree that it is at least desirable that names
and namespaces should have no semantic significance whatsoever.)

BTW, I inadvertantly omitted the most important reason for namespace-handling
constructs: to prevent conflicts and unwanted shadowing arising when you use
modules developed by different people.

 > > As it stands, though, I realize that the current module system is far too
 > > entrenched to change, so I would be happy if we could just qualify
 > > constructor and field labels by their type constructor's name, so that
 > > this would typecheck:
 > > 
 > >   type r1 = { name: int } type r2 = { name: float } type t1 = A of int
 > >   type t2 = A of float let (r2,t2) : r2 * t2 = { name = 1.0 }, A 2.0 let
 > >   (r1,t1) : r1 * t2 = { = 1 }, t1.A 2
 > I'd rather use different namings like "r1_name" and "r2_name" for the
 > field: although this requires me to always include the name of the type,
 > one cannot get confused either: it makes the sources easier to understand.

I don't find that argument very convincing; Ocaml _already_ allows type, value
and field label definitions to shadow each other, which many people consider a
cognitive burden. I don't have an opinion on that issue; I'm just trying to be
consistent with the way Ocaml treats bindings currently, and add a little bit
more flexibility.

In fact, I don't see how this makes programs any less readable.

If you don't add this mechanism, then the rule remains, "an identifier X
refers to the last definition of X in scope".

If you do add this mechanism, then that doesn't change (since the identifier
[path] "t.x" is still distinct from "x").

The only problem I see is that for the toplevel you will lose the ability to
throw away bindings that got shadowed by newer definitions, even if they never
appear inside some existing binding. But as far as I know, Ocaml's top-level
doesn't garbage collect old bindings anyway, so it's a non-issue for now
(unless the implementors intend to change this detail in the future).

Frank Atanassow, Dept. of Computer Science, Utrecht University Padualaan 14,
PO Box 80.089, 3508 TB Utrecht, Netherlands Tel +31 (030) 253-1012, Fax +31
(030) 251-3791