An interesting exploration of unimaginative thinking.
http://web.archive.org/web/20100411190630/http://www.designingwbt.com/content/hct/hct.pdf
Horsed carriages are "petroleumless cars".
What if the horse uses KY jelly for recreation?
Maybe the GoldenHammer AntiPattern is an instance of HCT.
For an emblematic example of HorselessCarriageThinking, launch MS windows calc.exe or kde's kcalc. --ChristopheThibaut
And for one possible resolution, load Excel or Gnumeric. (Or if you are so inclined, just write some code.)
At this point though, I see one benefit of HCT: people will understand it. Anyone who has used a "normal" calculator, and can figure a mouse out, can use calc.exe. -- m
Yes, but it still won't be useful to them unless they use the keyboard interface (which has nothing to do with the GUI). Clicking the buttons with the mouse is unbelievably tedious; I can't imagine that anyone would seriously attempt to do it.
Unless they were using a stylus device or touch screen. Imagine! M$ writing a program which was not to be appreciated for its full value for several years. Question is, why doesn't MS Word have a full keyboard at the bottom, on which you click the mouse to type? It already has a "keyboard" of formatting options... -- m
AlanKay's speech The Computer Revolution Hasn't Happened Yet is related to HCT. See the video at (http://www.educause.edu/conference/e98/webcast98.html). It includes the most compelling example of HCT I've ever seen, as well as generalizing HCT from "something that Just Happens" to "an evil that afflicts all new revolutionary technologies".
In fact, genuinely new things to do aren't even remotely related to the technology you're applying. The connection is only obvious to the inventor that comes up with them.
Don't confuse new things to do with the new technology itself. Good roads for the Horseless Carriage are not a new thing to do, but a requirement of the new technology. The same with traffic signals, lanes, interchanges. The same with passenger cars, trucks, semis, buses and tow-trucks. (Though the bus system is a distinct technology.)
Pedantic note: I've been told that the good roads were actually the result of lobbying by (non-motor) cyclists, prior to the Horseless Carriages. -- MikeAnderson
Also don't confuse new things to do with old shit that's done with the new technology. Any sentence that starts with "replace" does NOT refer to a new thing!
Examples of new things are lawn-mowers, leaf-blowers and so on.
Here's some more good examples of what I mean.
New way to do the same old shit:
Something like the USB stick is more comparable. Hooking to a network is not always possible or practical. USB only defines the interface, not the medium (storage technique), and thus has more staying power. Early USB sticks maybe topped out at around 16 meg, but now you can find 16 gig or more. The older equivalents didn't really have this ability (although tape did get thinner and stronger). However, the existing interface does limit the transfer speed, which has resulted in some somewhat kludgey changes for later USB versions.
The key fact is that this is never, ever done by the generation that invented the technology, and sometimes not even by the generation that follows it. Take for example the internet.
The first and most important use it's been put to is faster mail. How long has it taken for groupware to develop?
The second most important use of the internet has been a faster printing press (the web) with the exact same kind of annotation/reference capability as can be found in any paper book. How many decades has it taken for wikis to develop? How many decades more until bidirectional links take hold?
And going back to the HCT example, trains had already created commuting so this was in no way novel to the inventors of the horseless carriage. Highways and interstates were invented only many decades after. Which all goes to show how backwards-looking and unimaginative is horseless carriage thinking.
But in fact, this is all just legalisms and beside the point red herrings. "thinking" vs "doing". What the bloody hell is that all about?? You're raving on and on and on about how great and creative every pissant inventor must have been, without showing any evidence of understanding the central point of this page.
Was email a great advance over postal mail? Sure it was. But it wasn't an advance that required radically different thinking. Someone just figured out a better way to do the same old shit that everyone else had been doing.
Figuring out how to do the same old shit better is a tried and true means of invention. It requires a little engineering, a little creativity, a lot of thought, and not much else. And it's very different from figuring out completely new and different uses for technology.
Figuring out new things to do instead of better ways to do things is where the real hard work of invention comes in. It's at least a hundred times more difficult to do, as judging by the ratio of people who are able to perform each activity. It's difficult because it requires certain character and emotional traits which are very rare in people. It requires,
The dichotomy between "new ways to do the same old shit" and "new things to do" is nowhere more oppressive than in OperatingSystemsResearch. The field is dead not because there aren't any more OS researchers, but precisely because those that remain are exclusively in the first category. No one is left to do original research anymore, only industrial engineering types masquerading as researchers. -- RK
In response to RK's rant above:
You seem to be arguing that for an invention to be worth anything the inventor must have a GrandVision of its purpose in society. What difference does that make? It's rare in the extreme for an inventor to have such foresight, and yet, I don't see why that's a problem.
When the inventor doesn't have a grand vision, we're shackled to a StupidVision?. Like for example, email. Threading and categorizing are still not widespread in email, despite the fact that threading at least is widespread in newsreaders. Or rather, was widespread since GUIed newsreaders seem to have taken a step backwards. EmailIsObsolete since it's just straightforward mimicry of the postal / secretary system which is itself obsolete. The form of email was obsoleted by its underlying technology, but most developers of email are too dumb to realize it. The only exceptions I've ever encountered are Gmail and Timber (http://www.chiark.greenend.org.uk/~sgtatham/timber.html) and Timber is vaporware.
The article implies that a majority of people in the industry reject new technologies. This is often not the case. The Automobile, telephone, and radio all had plenty of stock backers. In fact, many times the industry rushes into ideas before they've been better-tested and tuned, as all the failed early dot-coms (pre-2002) demonstrates. The "problem" is that there's a disconnect in the appropriateness of which technologies receive attention and those that don't. People do bet on future horses, they just keep picking the wrong ones. But we don't know whether this is an inherent flaw in human-nature, or the difficulty in knowing what can work in practice and what doesn't.
A current example of horseless carriage thinking is DataOriented programming. It is possible to start programming in the InformationOriented sphere using EnDemes and certain other InformationOriented approaches. So far endemes have been used in production only for things that DataOriented approaches already do. These are inventory status handling, exception handling, and security authorization. The powerful uses for EnDemes have not yet been allowed in production, probably because companies can not even imagine doing things they do not already know how to do. They have needs that are not filled, but simply discount their needs as being unobtainable.
You wrote, "The powerful uses for EnDemes have not yet been allowed in production..." It sounds like there's a story behind that. Would you be willing to tell it?
The story is mostly a story of me trying to figure out what I've got and how to use it. I have talked with numerous employers and co-workers about this, however since the theory and practice are still under development, most of this talking has been along the lines of 'I've got this idea, any idea how we could use it?, or 'I've figured out how to use this idea but it's not production ready yet'. Maybe in a year to two the discussion will go farther. I have listed some semi-theoretical benefits of EnDemes on the EnDemes page. I have come up with practical uses too but not posted them yet.
I'm curious about your EnDemes but I'm not clear how they'd be used in practice. Could you demonstrate with some examples? Could you show how Endemes would facilitate inventory status handling, exception handling, security authorization, and the more powerful uses that you have in mind?
I have included the Exception Handling example below as it is fully non-proprietary.
I'm also curious to see how EnDemes would facilitate something more InformationOriented than, say, representing the same data using a conventional RelationalDatabase approach. For example, to record the same data as Employee endemes, we might define a table of Characteristics, a table of Employees, and an EmployeeCharacteristics table to represent the many-to-many relationship between Employees and Characteristics. (I realise there's probably a better page for this question, but I'm not sure what it is. Feel free to move this to there.)
There is a discussion of these very issues on the BusinessTalentEndemeSet and KnowledgeDatabase pages.
Exception Handling 'Horseless Carriage' Example
The exception handling code is not proprietary and so I can show a couple of key methods here. Note that this is 'horseless carriage' code, and maybe should be done with DataOriented techniques instead.
The endeme set code is:
public static class Throws { // ---------------------------------------------------------------------------------------- /// <!-- Actions --> /// <summary> /// A)lert, C)ommon, D)atalog, E)mail, F)ill, G)AT, I)nternal log, J)unk/ignore, L)og event, O)utput window, P)ause, Q)uit, R)ethrow, S)quawk, T)race, U)nique /// </summary> public static EndemeSet Actions { get { if ((_actions == null)) { //ABCDEFGHIJKLMNOPQRSTUV|lt label description _actions = new EndemeSet("Exception Action"); //----------------------+------------------------------------------------- _actions.Add('A', "Alert" , "alert the support staff about the problem right now"); //A] -I- -S- |A. Alert *helpdesk _actions.Add('B', "Bug" , "continue the program, displaying a bug as in an Assert.That statement"); //AB]D -T- |B. Bug Debug, Test, Assert, Equals (does this make any sense?, probably remove it) _actions.Add('C', "Common" , "uses the default action list rather than the one passed to Throws.A"); // [C]D- N -S- |C. Common Normal, Standard, Default _actions.Add('D', "Datalog" , "log the event to the database"); // [D] -I--L- |D. Datalog *Log to database, Insert _actions.Add('E', "Email" , "email someone about the event/exception"); // [E] -M- |E. Email Mail _actions.Add('F', "Fill" , "fill out the exception with a stack trace and source if there is not already one"); // -C-[F] -S- |F. Fill *Source & Stack _actions.Add('G', "GAT" , "include a literal 'GUI Action Trace' (GAT) with the error message"); // [G] -TU-|G. GAT UI actions, Trace _actions.Add('H', "Here" , "log the errors into a list internally here inside Throws"); // [H]I-L- -T- |H. Here compile errors here in Throws _actions.Add('I', "Internal", "log the errors into a list internally here inside Throws"); // -H[I] L- -T- |I. Internal *compile errors here in Throws _actions.Add('J', "Junk" , "ignore the event/exception entirely (prevents any action at all)"); // -D- -I[J] -P--S- |J. Junk *Ignore, Dump, Prevent, Scuttle _actions.Add('L', "Log" , "log the exception to the Windows event log"); // -E- [L] |L. Log *Event _actions.Add('O', "Output" , "send mesage to Visual Studio Output window"); // [O] -V|O. Ouput Visual Studio output window _actions.Add('P', "Pause" , "pause the program (if in debugger mode and you have set the breakpoint)"); // [P] |P. Pause *the program _actions.Add('Q', "Quit" , "take no further action (action chain ends here)"); // -DE- -N-[Q]S- |Q. Quit end, none, stop _actions.Add('R', "Rethrow" , "rethrow the exception (which may crash the program) preserving the stack trace"); // [R]T- |R. Rethrow *Throw _actions.Add('S', "Squawk" , "send an error message to the user in some sort of pop-up"); // -C-E- -M--P-[S]U-|S. Squawk *User, send popup to user no repeats _actions.Add('T', "Trace" , "add a stack trace to the error message"); // -S[T] |T. Trace Stack trace _actions.Add('U', "Unique" , "terminate the action string here if the error text is repeated"); // -N- [U]|U. Unique message, no repeats } //----------------------+------------------------------------------------- return _actions; } } private static EndemeSet _actions = null;The key method is:
// ---------------------------------------------------------------------------------------- /// <!-- A --> /// <summary> /// Provides a breakpoint in development and throws an exception in production /// </summary> /// <param name="ex">exception</param> /// <param name="actions">bug action</param> /// <returns>The id in the database if sent to the database</returns> public static Guid A(Exception ex, EndemeSet eSet, Endeme actions, EventSourceCreationData? eventSource) { // --------------------------------------------------------------------------- // Prepare various things // --------------------------------------------------------------------------- if ((_oldMessages == null)) { _oldMessages = new List<string>(); } actions = Insure(actions); ThrowIfThrowsNotActivated?(eventSource); string strGat = ""; string msg = PrepareMessage?(ex); // --------------------------------------------------------------------------- // 8153 is usu. a normal condition regarding including nulls in SQL agregates // I also don't want to hear about deadlock victims // Also, if the actions endeme says ignore the exception, then ignore it // --------------------------------------------------------------------------- Guid dbId = Guid.Empty; if (Regex.IsMatch?(ex.Message, "(8153|Deadlock victim)", RegexOptions?.IgnoreCase?)) { return dbId; } // --------------------------------------------------------------------------- // Meta-characteristics // --------------------------------------------------------------------------- if (actions.Contains('J')) { return dbId; } if (actions.Contains('C')) { actions = _defaultActions; } // --------------------------------------------------------------------------- // This is how to do it with an endeme // --------------------------------------------------------------------------- char[] act = actions.ToCharArray?(); bool run = true; int i = 0; while (run && i < act.Length) { switch (act[i]) { case 'A': SendAlert?(ex, PageName); break; // send alert to support staff case 'D': dbId = SendToDatabase?(msg, "", "", ""); break; // TODO: rebuild, log the exception in a database table case 'E': SendAlert?(ex, msg); break; // send email, currently uses the same code as alert case 'F': ex = FillOutException?(ex, PageName); break; // fills in missing members (stack trace) in the exception object case 'G': strGat = ""; break; // TODO: needs to be rebuilt, include a GAT with a message case 'H': ErrorList?.Add(msg); break; // here: log the errors here in Throws case 'I': ErrorList?.Add(msg); break; // internal: log the errors here in Throws case 'J': run = false; break; // junk/ignore the exception, you're done handling the error case 'L': LogToActionLog?(msg, eventSource, strGat); break; // Log event to windows event log case 'O': WriteToDebugWindow?(msg); break; // Write to visual studio output window case 'P': Pause(); break; // useful for development, set a breakpoint in Pause() case 'Q': run = false; break; // quit the error handling sequence, you're done handling the error case 'S': Squawk(msg); break; // MessageBox.Show(msg) or something case 'T': msg = msg + Environment.StackTrace?; break; // include a stack trace with a messsage case 'U': if (_oldMessages.Contains(msg)) { run = false; } break; // quit handling an error with an identical message to a previous one case 'R': Type ForExceptionType? = typeof(System.Exception); // I got this off the net, supposedly it BindingFlags? flags = default(BindingFlags?); // preserves the stack trace during a 'Throw ex', flags = BindingFlags?.Instance | BindingFlags?.NonPublic?; // In theory you are never supposed to use ForExceptionType?.GetMethod?("InternalPreserveStackTrace?", flags).Invoke(ex, null); // 'Throw ex' because it dumps the stack trace, throw ex; // but since this whole class is based on break; // 'Throw ex', the stack trace must be managed } i += 1; } _oldMessages.Add(msg); return dbId; } public static Guid A(Exception ex, EndemeSet eSet, Endeme actions) { return A(ex, eSet , actions , _eventSource); } public static Guid A(Exception ex ) { return A(ex, Actions, _defaultActions, _eventSource); }Usage:
if (!string.IsNullOrEmpty?(_errors)) Throws.A(new DataException?(_errors), Throws.Actions, "PSA");When the 'Throws.A' method gets run, it starts a loop to process the "PSA" endeme, the 'P' in the case statement runs the 'P' routine (Pause at a breakpoint), then the 'S' case runs the 'S' routine (squawk to the user that there is a problem), then the 'A' case runs the 'A' routine which sends an alert to the support staff that there is a problem.
The exception handling system allows a programmer to handle an exception in a nearly infinite number of ways and that is the benefit of doing it this way.
In this example, an EndemeSet is used as a small, embedded, DomainSpecificLanguage that can be used to describe ExceptionHandling behaviour? That's cool!
What else can you do with an EndemeSet? How would you use it in an InformationOriented way?
Here are some areas where I have ideas: