On Fri, Jul 29, 2011 at 3:12 AM, BGB <cr88...@gmail.com> wrote:

> I generally start with a more "complex" description and tend to see what
> can be shaved off without compromising its essential properties. doesn't
> always work though, such as if requirements change or a dropped feature
> comes back to bite one sometime later.
>

That strategy does not seem very wise. Many systems are minimal (you can't
remove a feature without breaking them) but not simple - Turing tarpits, for
example. Thus, if you don't start with a superset of a simple solution, you
won't find a simple solution by shaving things away. Given N features, there
are 2^N subsets. Thus, if you don't start with a 'close' superset of a
simple solution, your search-space will be much too large.

You need to know your essential properties, but you only learn more about
them when you've made your solution too simplistic and get 'bitten' sometime
later. It's much more efficient start simplistic and accept your bites
early.


>
> problems don't fully appear until one tries to implement or test the idea,
> and one is left to discover that something has gone terribly wrong.
>

Sure. I've gone through five implementation cycles in eight years. I learned
a lot from them. I've rejected transactions, promises, connectionless
message-passing, and other partial solutions based on realizations during
implementation. My 2009 attempts at multi-cast, resilience, and mobility -
in the context of legacy adapters for common capabilities (like Internet
access) - led (through intermediate models) to the development of RDP.

But, often a problem hops out *before* I implement and test. My set of
developer stories, use cases, and pseudo-code tests grows with each failure
or inspiration. They help me develop idioms and abstractions and get a real
feel for a new model. Discovering a flaw or some undesired property at this
time is orders of magnitude more efficient than waiting for a full
implementation and real-world testing.



IMO, glass-ceilings and brittleness are far more often a result of cruft
> than of complexity in itself.
>

Complexity - i.e. being built of many, small, independent parts - can be
acceptable, when the emergent behavior is predictable. Unfortunately,
complexity often leads to unpredictable chaos and complication, especially
at scale. Consider symptoms such as convoying, priority inversion,
heisenbugs, deadlock, livelock, flash crowds, confused deputy, resource
thrashing, packet floods, inconsistency, path dependence, state after
partial failure, dead links, tangled dependency forests, and so on.

Complexity easily becomes complicated - i.e. difficult to predict, or
explain. We can counter this by introducing artificial barriers at scale,
rendering the model less scalable but more predictable. But those become the
same 'glass ceilings' I mentioned earlier. We imprison ourselves.

I've learned to counter complexity by focusing on *compositional properties*.
With 'composition', we have a standard set of operators for combining
elements, and the result of composition can be further composed by the same
operators. (That is, we have something akin to algebraic closure.) A
property is compositional if, and only if, we can reason about that property
in the result from knowing it for each operand. Critically, this means we
cannot peek inside each operand.

For example: progress is *not* compositional in a system with mutexes,
because we can combine two deadlock-free subprograms but cannot say whether
the resulting program deadlocks.

I'll note that common models of  OOP are notoriously non-compositional.
There are no *standard* operators for combining two or more objects into a
new object, and very few properties we can reason about. Actors model is not
compositional because we cannot treat a configuration of actors the same as
one actor due to races between messages. For composable paradigms (of
varying quality), I would direct you to functional programming, arrows,
monads, FRP, dataflow, concatenative languages, grammars, and logic
programming.

Compositional properties allow us to achieve a sort of 'simplicity at
scale', i.e. the ability to (a) reason about and (b) integrate with existing
systems, without knowing deep operational details. To be 'scalable' requires
quite a few compositional properties that are useful at scale (*progress* is
only one of them). And to be 'open' - i.e. support runtime extension,
federated systems, independent maintenance and development, security -
requires even more compositional properties.

With Reactive Demand Programming, I achieve the following compositional
properties: idempotence, commutativity, concurrency, eventual consistency,
 useful classes of optimizability (continuous compilation and specialization
of slow changing behaviors via asynchronous reactivity; duplicate
elimination and ad-hoc multi-cast content distribution via commutativity and
idempotence), predicative space (bounded-space per client), predicative time
(real-time, duration coupling), performance isolation (up to resource
limits), resource management (resource acquisition is demand - RAID,
implicit release via duration coupling), resource scheduling (via
anticipation), support for ad-hoc coordination of independent systems (via
anticipation and reactive propagation), visible security (object capability
model, automatic revocation based on reactivity), resilience, and
predicative failure modes (all failures reduce to disruption, which can be
handled elegantly via disruption tolerance patterns and code distribution).

And my *implementation* of RDP supports even more than that. I.e. I use vats
to support parallelism, batch-processing, global snapshot consistency, local
determinism (up to a small group of in-process vats). I have ideas on how to
support these properties in an RDP language. Eventual consistency makes a
nice safety net, but I don't like to depend on it.


>
> Snow Crash: "dot pattern from space -> brain-damage
>

Ah, yes, that wasn't the bit I wanted to create from Snow Crash. Just the
vision of the cyber-world, with user agents and a multi-company federated
but contiguous space.


>
> I started out with language design and VM implementation some time around
> 2000.
> at the time I was using Guile, but was frustrated some with it. for
> whatever reason,
>
I skimmed over the source of it and several other Scheme implementations,
> and
>
threw together my own.
>

My start in language design is similar. Guile was one of the implementations
I studied, though I did not use it.


>
>  *Too much power with too little perspective - that is the problem.*
>
> I doubt "power" is the cause of this problem.
>
being simplistic or made from cruft or hacks are far more likely to be their
> downfall.
>

Power, in the absence of perspective, leads humans to develop cruft. Hacks
are how we compose cruft.

Self-discipline and best practices are ways to 'tame' the use of power, when
we have too much of it. Unfortunately, these are not practices that scale.
Self discipline won't help when your libraries suck because *someone else* was
not disciplined.

We cannot universally fix perspective. Humans *need* the ability to
specialize - it takes ten years of passionate education to gain a tiny
fraction of useful human knowledge today. Those numbers will only further
diverge over time, and filtering the useful knowledge from speculation and
drivel will only become more difficult. It is too easy to miss lessons of
the past. That problem will only get worse.

Between power and perspective, power is the problem we can most effectively
tame by technological means.


>
> I regard this as "ivory tower VM design", the likely biggest example of
> this strategy being the JVM, however, it is not unique to the JVM (most VMs
> have it to a greater or lesser extend).
>

The JVM was not designed in an ivory tower, BGB. Further, it was pressured
and pushed by Sun's marketing before its developers thought it ready. JVM
was a classic example of 'worse is better, get on base first and patch up
later'.

For ivory tower languages, I suggest you review Haskell,
Oz/Mozart, Maude, Coq, Lustre, Charity, Bloom, Mathematica, and Scheme. I'm
certain you could learn a lot from them.

People who use "ivory tower" as a snub offer the impression of
being close-minded and prejudicial. For impressions sake alone, you might
want to work on that.


> I don't think the problem actually requires anywhere near this level of
> resources, rather it more requires FFIs capable of doing a bit of "heavy
> lifting" needed to integrate disparate languages and technologies in a
> relatively seamless and automatic manner.
>

> a good portion of the problem then boils down to data-mining and
> heuristics.
> source code/headers/... itself actually provides a good deal of information
> for how to interface with it.
>

I agree that we can automate a lot more of FFI/foreign-service integration,
and that doing so is a worthy goal.

But for integration to be 'seamless' is much more difficult. I don't expect
*seamless* integration even with a common programming model, due to
variation and mismatch in data models. (Reactive and RESTful help, but there
are fundamental ontology issues.) Foreign service integration is faced with
many extra challenges: different concurrency and concurrency-control models,
different evaluation-properties (strict, lazy, parallel, beta-reduction),
different type systems, different error-propagation models, different memory
management models, different data and control-flow models, different
temporal and spatial properties, different distribution models, and on and
on and on.

Only when two languages are conceptually 'close' to one another (e.g. two
procedural languages) is integration typically effective. I'll repeat that:
effective integration requires that the languages share a lot in common -
i.e. a common basis, foundation, substrate.

All that aside, 'the problem' I was discussing seems to be independent of
foreign services integration: today, we cannot effectively compose,
integrate, and scale frameworks, DSLs, and applications developed *in the
same* programming model or language. Even *without* the extra complications
of foreign service integration, there is a major problem that needs solving.

On a side note, I have spent a lot of time developing some excellent
approaches for foreign-service integration. I've rejected use of a 'Foreign
*Function* Interface' in favor of a plug-in extensible runtime. Each
'plugin' publishes modules and capabilities to the runtime, along with
metadata to distinguish them. These are made available to application
developers as objects or modules within the language. For the runtime
itself, I supported built-ins,  dynamic plugins, and separate executables
(via Unix or TCP socket). Applications are also able to register their own
caps as modules, and thus provide services..

The resulting model is much close in feel to how browsers use plugins, how
web-serves use CGI, or how service-oriented architectures locate
dependencies, but has the advantages of being open, extensible, flexible,
distributed, securable, resilient (i.e. allowing fallback services),
relatively easy to debug, language agnostic, and supporting a continuous
transition strategy for slow absorption of foreign services.

My original design for this was developed in 2008 and mostly implemented in
C++ in 2009, but I've since transitioned to Haskell and will need to start
from scratch. That's okay, it should be much easier the second time. Parts
of my implementation, especially the 'vat' model, are already designed with
this integration in mind.

So I have not ignored the foreign service integration problem. I just
haven't considered it a difficult or interesting problem for a few years
now.


> there is still a lot that can be done without requiring fundamental
> changes.
>

I agree. Problem is, many of the more interesting things I want to do would
be complicated, inefficient, unsafe, or insecure with our current
foundations.


>
> one may parse code, and then compile it to a stack-machine based IL, as
> personally I have had the most luck working with stack-machines. IME, stack
> machines are fairly easy to reason about from software, so I have had best
> luck with things like
> temporaries/register-allocation/type-analysis/value-flow/... working in
> terms of the stack.
>

The very idea of 'control-flow' is difficult to reason about - especially
once you add a dash of concurrency, parallelism, reactivity, inversion of
control, or heterogeneous memory (FPGA, CUDA, distributed, etc.). Stack
machines are quite *simplistic* in the grand scheme of things.

Seriously, we've been building some massive amounts of cruft around these
stack machines for decades now. If you have a language that implements
without complication on a stack machine, I hypothesize that your language is
just as simplistic.
_______________________________________________
fonc mailing list
fonc@vpri.org
http://vpri.org/mailman/listinfo/fonc

Reply via email to