On Thu, Jan 17, 2019 at 05:32:52PM -0800, Walter Bright via 
Digitalmars-d-announce wrote:
> On 1/17/2019 11:31 AM, H. S. Teoh wrote:
> > [...]
> Thanks for the thoughtful and well-written piece.
> But there is a counterpoint: symmetry in mathematics is one thing, but
> symmetry in human intuition is not. Anytime one is dealing in human
> interfaces, one runs into this.  I certainly did with the way imports
> worked in D. The lookups worked exactly the same for any sort of
> symbol lookup. I thought it was great.
> But I was unable to explain it to others. Nobody could understand it
> when I said imported symbol lookup worked exactly like any lookup in a
> name space.  They universally said it was "unintuitive", filed bug
> reports, etc.  Eventually, I had to give it up. Now import lookup
> follows special different rules, people are happy, and I learned
> (again) that symmetry doesn't always produce the best outcomes.

Alas, it's true, it's true, 100% symmetry is, in the general case,
impossible to achieve.  If we wanted 100% mathematical symmetry, one
could argue lambda calculus is the best programming language ever,
because it's Turing complete, the syntax is completely uniform with no
quirky exceptions, and the semantics are very clearly defined with no
ambiguity anywhere.  Unfortunately, these very characteristics are also
what makes lambda calculus impossible to work with for anything but the
most trivial of programs. It's completely unmaintainable, extremely hard
to read, and has non-trivial semantics that vary wildly from the
smallest changes to the code.

For a human-friendly programming language, any symmetry must necessarily
be based on human expectations.  Unfortunately, as you learned, human
intuition varies from person to person, and indeed, is often
inconsistent even with the same person, so trying to maximise symmetry
in a way that doesn't become "counterintuitive" is a pretty tall order.

As somebody (perhaps you) said once, in Boeing's experience with
designing intuitive UIs, they discovered that what people consider
"intuitive" is partly colored by their experience, and their experience
is in turn shaped by the UIs they interact with.  So it's a feedback
loop, which means what's "intuitive" is not some static set of rules
(even allowing for arbitrarily complex rules), but it's a *moving
target*, the hardest thing to design for.  What's considered "intuitive"
today may be considered "totally counter-intuitive" 10 years from now.

In the case of imports, I'd argue that the problem is with how people
understand the word "import".  From a compiler's POV, the simplest, most
straightforward (and most symmetric!) definition is "pull in the symbols
into the local scope".  Unfortunately, that's not the understanding most
programmers have.  Perhaps in an older, bygone era people might have
been more open to that sort of definition, but in this day and age of
encapsulation and modularity, "pull in symbols into the local scope"
does not adequately capture people's expectations: it violates
encapsulation, in the following sense: symbols from the imported module
shadow local symbols, which goes against the expectation that the local
module is an encapsulated thing, inviolate from outside interference.
It breaks the expectation of encapsulation.  It breaks the symmetry that
everywhere else, outside code cannot interfere with local symbols.

Consequently, the expectation is that imported symbols are somehow
"second class" relative to local symbols -- imported symbols don't
shadow local symbols (unless you explicitly ask for it), and thus
encapsulation is preserved (in some sense).  So we have here a conflict
between different axes of symmetry: the symmetry of every module being
an inviolate, self-contained unit (encapsulation), and the symmetry of
having the same rules for symbol lookup no matter where the symbol came
from.  It's a toss-up which axis of symmetry one should strive for, and
which one should be compromised.

I'd say the general principle ought to be that the higher-level symmetry
(encapsulation of modules) should override the lower-level symmetry (the
mechanics of symbol lookup).  But this is easy to say because hindsight
is 20/20; it's not so simple at the time of decision-making because it's
not obvious which symmetries are in effect and what their relative
importance should be.  And there's always the bugbear that symmetry from
the implementor's (compiler writer's) POV does not necessarily translate
to symmetry from the user's (language user's) POV.

Still, I'd say that in a general sense, symmetry ought to be a
relatively high priority as far as designing language features or
adding/changing features are concerned.  Adding a new feature with
little regard for how it interacts with existing features, what new
corner cases it might introduce, etc., is generally a bad idea. Striving
for maximal symmetry should at least give you a ballpark idea for where
things should be headed, even if working out the details is generally
not so straightforward.  It may be that you will discover
mutually-exclusive symmetries that, no matter which way you take, will
lead to trouble later on.  Conflicts of this sort usually cannot be
fully resolved without essentially gutting the entire system to the core
and rebuilding from scratch, which usually is not a practical thing to
do.  So we're stuck with imperfection.  Nevertheless, IMO we should
still strive for perfection, even if it's ultimately unattainable.
Being slightly closer to perfection is better than being far off.

> User interfaces (and programming languages certainly are user
> interfaces) are hard and (ironically) are anything but intuitive to
> design.

Yes, it's very unintuitive to design intuitive interfaces. :-D  It's the
paradox of user-interface design.


Life is too short to run proprietary software. -- Bdale Garbee

Reply via email to