[e-lang] Protocol tests are necessary (was Re: Rename Guard, ValueGuard)

Dean Tribble tribble at e-dean.com
Sat Sep 3 18:28:03 EDT 2005


> > I'm confused about what "ExtendedGuards" are.  If they are the guards
> > that add the infrequently-used operator overloadings,
>
> Yes.

In that case, I would advocate that we provide no support over what
the language otherwise requires.  It's does not appear to be a common
scenario, and for any truly interesting guard, the programmer can
define an appropriate guard class.  The net result is no additional
mechanism, no loss of generality, and relative familiarity to any E
programmer of the special cases.

> >> However, I think it will eventually be important to preserve the
> >> guard's ability to override those operations to be more than that; for
> >> example, the guard resulting from this expression can be much more
> >> efficient if IsAny (previously discussed for replacing the ==-pattern)
> >> can implement | to define a new IsAny guard instead of an or-guard:
> >>
> >>    accum none for item :notNull in dataSource { _ | IsAny[item] }
> >
> > This looks completely unfamiliar to me.  Can you explain?
>
> Context:
>
> 'none' is a (hypothetical) guard which accepts no values.
>
> 'accum' is a pragma-enabled syntax for accumulating/reducing/folding.
>
>      accum INITIAL-VALUE LOOP-EXPR { _ RIGHT-SIDE-OF-CALL-EXPR }

I understand my confusion now.  I was trying to figure out why there
were new guard positions in the experimental accumulator syntax.  What
you are actually doing is using the accumulator syntax to accumulate a
combination guard; the guards are not new extensions of the
accumulator syntax.  I understand now, thanks.

In general for guards, the programmer locally knows what they want.
It seems extremely unlikely to me that they will require compositional
mechanisms to optimize arbitrary guard combinations.  If the goal is
to dynamically generate guards based on data input, that seems
extremely rare, and a significant piece of the semantics of that
program.  I would not expect to trip over it in an misleadingly
trivial application of existing syntax.  I would expect to see
something like

def funkyBar(arg :DynamicGuard[dataSource])

where a programmer could then look at how dynamic guard did its work.

> If the guard's | is custom, then this can yield the O(1) guard
>    IsAny[0, 1, -1]

Yes.  Why wouldn't I just write it that way?

> whereas if the only way to request compositions of guards does not ask
> the guards themselves, the result will be

I would take this as arguing against trying for complex guard
construction with simple compositional operators.  We already have
extensibility for guards by having the ability to define more of them.
 What requirement are you trying to address that is not already
straightforwardly addressable?

> >> This requires a well-defined way to ask an object if it is an
> >> ExtendedGuard....
> >
> > A general principle in OO is that any such manual type test is a "bad
> > smell in code", and signifies a bad design pattern.  There are always
> > exceptions, but that's a pretty good heuristic.  If we see a lot of
> > type testing, that means our type hierarchy is bad because we
> > are unable to get polymorphism via message dispatch.
>
> In order to use 'plain' messages in this way, the client and the
> polymorphic target must have an agreed set of messages to support.

Exactly.  A system without such agreement gets vastly more complicated
because it becomes extremely difficult to reason about or test the
combinations.

> The
> solution of this form is to make ExtendedGuard the protocol for all
> guards, so that any object which recieves a guard knows that it
> supports or/1 (as in my examples above) and will get either typical or
> custom behavior.

If this is a good thing to support, then it might very well make sense
to do that.  E tried that, with compositional operators supported on
every guard.  The few uses of them shed confusion rather than clarity,
and so the "feature" is deprecated.   Bringing it back for even rarer
circumstances does not seem valuable, especially given that the same
result can be achieved in other ways.  If we really wanted to have a
subworld of complex guards, we could use a wrapper on basic guards
(e.g., GuardCombo[any] | Knot[foo]).

> However, in a *distributed* system, this is insufficient, because
> global agreement is not feasible. What if the original ExtendedGuard
> doesn't cover some operation we want later?

For some things, global agreement is pretty feasible, because it's
part of the standard E libraries and contracts.

In any case, "type extension" from Joule is another (cleaner?)
direction to achieve approximately what you are asking for.  In this
case, I think the guard contract should stay really simple, but the
general problem does arise.  MarkM and I have occasionally talked
about it, but it doesn't fit well with E dispatch (because it is
string based).

> We have the basic Slot protocol, and Lamport Slots...

The example uses of type-case (one of the terms for this. isKindOf
was the Smalltalk mechanism) that you gave for this are essentially
about debugging access to the slot.  Debugging is really a different
class of issues than the prior examples, and often use a *meta-level*
type-case that doesn't send messages to the actual object (or else
inspecting/debugging changes the execution you are trying to debug).
It's not that there aren't exceptions in which type-case is useful,
it's that they are almost invariably better achieved through good
protocol design.

> My current hypothesis is that for E to be usable for the large
> heterogenous distributed systems it was designed for, it is *NECESSARY*
> to have a well-defined "do you support this protocol" query protocol.

I agree that it is necessary to support it, because there are
exceptions (and hacks) that require it.  I also believe that a system
that uses it extensively is not understandable, testable, nor
maintainable.  Hence, any mechanism that relies on it should be
retired or redesigned.



More information about the e-lang mailing list