This page discusses features for
ProgrammingLanguages, APIs, Frameworks, and
OperatingSystems (which are really the same thing:
ApiIsLanguage,
FrameworkIsLanguage,
LanguageIsAnOs). Examples of features can be found on pages such as
OperatingSystemsDesignPrinciples,
KillerUserInterface,
GuiEngineGoals,
KeyLanguageFeature.
Features are often discussed as though they were binary in nature. And, for objective features of digital artifacts on digital machines (i.e. ignoring features that name a user or programmer as a 'participant'), that often isn't too far from the truth. But even objective features tend to describe
amalgamations of other, finer-grained properties. Even if these fine-grained properties are individually binary, the amalgamation is combinatorial and will appear from the two-foot head-to-monitor view to exist on a continuous scale.
This page discusses features appearing on such a continuum, and offers rubrics to discuss their 'level'. This page reflects
FourLevelsOfCompetence. The
FourLevelsOfFeature at increasing levels of feature are:
- TuringTarpit: You don't have the feature, and you can't merely compose existing features to achieve it. You might be unable to 'abstract' the feature for OnceAndOnlyOnce. Alternatively, you may need to build infrastructure (framework, api, and protocol) to implement the feature, and this leaks and introduces coupling in a manner that painfully limits portability, composition, or future options (related: MissingFeatureSmell). SelfDiscipline and analysis consistently fail even skilled programmers. Essentially, you're gonna fall prey to GreenspunsTenthRuleOfProgramming.
- SelfDiscipline: You can consistently achieve the feature through careful constraint of one's programming behavior, and maybe even scale it up to a team behavior. Correctness can be determined by analysis tools, UnitTesting, and code-reviews. The feature might be something you can abstract to some limited degree, but you must still apply SelfDiscipline to use of the abstraction.
- BondageAndDiscipline: The feature is forced on you, and you are (painfully) aware of it. You're forced to deal with it even when you'd rather be involved with something else, even when you don't want the feature. The feature is achieved by use of barriers and DiscontinuitySpikes, and you're continuously tripping over them. It may be that the feature doesn't have the vaunted FirstClass status.
- ElegantSimplicity: the feature is just there when you need it, and its achievement doesn't get in your way when you don't want to think about it. If the language has barriers to achieve the feature, they aren't in your way or they seem like natural fixtures so you don't ever feel the need to try plowing through them. There are no gotchas with the feature - no need for self-discipline; SymmetryOfLanguage for the feature is implicit.
There are, of course, ranges in between these four levels. For example, multi-threading safety and deadlock resistance fall somewhere below
SelfDiscipline for
CeeLanguage + Pthreads (since application of
SelfDiscipline is hardly consistent in achieving safety or resisting deadlocks, and deadlock-free programs do not in general compose to create larger deadlock-free programs), but somewhere above
TuringTarpit (since a degree of safe abstraction is possible). 'Pure'
CeeLanguage would be at the level of
TuringTarpit for these features, since
CeeLanguage itself has no way to express multi-threading. For single-threaded data safety,
CeeLanguage is at the
SelfDiscipline level with a small dose of
BondageAndDiscipline. The regular requirement for reinterpret cast in programs of even moderate complexity, however, suggests that even
SelfDiscipline doesn't provide
type safety. (Data-safety is not just type-safety.)
We all know about
BondageAndDiscipline, of course.
ManifestTyping,
CheckedExceptionsAreOfDubiousValue, etc.
ElegantSimplicity is rare on a broad scale, in part because
LifeIsaBigMessyGraph, but is still available for fine-grained features.
DataflowProgramming and good
DependencyInjection frameworks are candidates.
AbstractFactory for
PluginArchitecture, as used in browsers, is certainly up there, at least up to the point of safety and security concerns.
ImplicitTyping with typeclasses or overloading, and especially Static
DuckTyping (as in
ScalaLanguage), seem to exist at this level.
SoftwareTransactionalMemory certainly does; it allows arbitrary safe composition of concurrent access to data, and most code can be written entirely ignorant of it.
GarbageCollection would also be at the
ElegantSimplicity level, albeit with a few caveats for realtime systems (unless realtime-GC is achieved).
Not all features can be achieved simultaneously, and amalgamation features tend to be implied (e.g. for
GarbageCollection, one assumes safety, security, maybe a certain amount of performance based on modern implementations, etc.) unless explicitly redacted, so there may be some need to issue explicit caveats.
Regarding the
TuringTarpit, what if an interface to an app or scripting language can be made so that the cases the kit cannot handle can be custom programmed? This may not work with languages, but perhaps with API's.
AlternateHardAndSoftLayers is a powerful design option for apps, and a good scripting language or API can elevate some features to a higher level (ApiIsLanguage, after all), but this does not eliminate the possibility of features remaining at the TuringTarpit level. If you are continuously forced to reinvent or re-implement something in scripts or extensions with slight variations, for example, that is TuringTarpit level. Similarly if you are forced to explicitly weave code... e.g. to deal with GC, concurrency, transactions, error-handling, PolicyInjection, etc.
It is my opinion that to
KeepItSimple, one may have to forgo portions of the higher-numbered levels.
GoldPlating a nascent standard will often prevent it from being initially accepted. The market-place better accepts incremental improvements, even if this makes the final result less elegant than a 4-level-up-front kit. This is not a reality we can expect to change, only work within knowing its pattern. -t
That depends on the "it" in question - i.e. are you trying to keep it simple for the implementor? or are you trying to keep it simple for the users? Which decision is more economically sound depends on how many users and how many times they plan to use it - i.e. on the developer:user ratio - so can't be decided without more knowledge. Anyhow, the idea of achieving 'incremental improvement' is often restricted by practical concerns for BackwardsCompatibility, MindOverhaulEconomics, and CodebaseInertia. It is fortunate for upgrades to higher-level features that the market-place, in practice, accepts periodic overhauls (i.e. new gaming consoles, new programming languages, RewriteCodeFromScratch, 'major' revisions, etc.) and continuously deprecates applications that can't keep up, otherwise old OperatingSystems and such could never be significantly upgraded, and we'd still have a choice only between Fortran and Lisp.