Wednesday, December 03, 2008

gleaning good design advice from unit tests

I'm a beginner to the rigorous usage of xUnit unit tests, which enable easy unit test creation, execution, and repetition through coded tests that a computer can run non-interactively. I'm well-acquainted with the concept, of course, but mostly from blogs. At work, I'm alone in writing such tests. My guess is that the typical programmer categorizes up-to-date, comprehensive, effective test suites like good documentation: "nice but nonessential". The comparison is apt, since both must be maintained in parallel with the code. And the test source can function as sketchy documentation (open source projects, I'm scowling at you!).

But as so many others have observed in practice, good tests are of sufficient value to merit the developer's scarce attention. The upfront cost of writing or updating a test produces benefits repeatedly thereafter. When the code is first written, the test confirms that it meets its function. When the code changes later (it will), the test confirms that it still functions as expected and therefore won't create problems in other code that uses it according to those past expectations (this is even more important if any points of code interaction are resolved at run-time). When the code has a bug, the test that is written to check for the bug confirms that the bug is fixed and remains fixed. When the code is reorganized through the wisdom of hindsight, the test confirms that the transition hasn't accidentally abandoned established dependencies. You don't need to trust TDD or Agile wonks to enjoy these results. Just trust your tests.

The trickier aspect of unit tests, the facial mole that everybody notices and newcomers should acknowledge sooner rather than later, is the all-too-likely possibility that a typical OOP program's objects aren't the neat, independent, well-defined, composable units that facilitate unit testing. To some degree this is unavoidable, as no object is an island. Objects that are excellent individually will need to collaborate and delegate in order to perform their own useful tasks; when asked for its price including tax for a specific political domain, a sales item should need to ask yet another object for the tax rate, because tax rates are not one of an item's responsibilities. A horrendous level of difficulty of writing unit tests for an object indicates that its design or the overall design of the entire set of objects should be reexamined.

For considered as one more design constraint, greater "testability" encourages: 1) cohesion - limiting each object to a fixed and bounded purpose, 2) loose coupling - limiting brittle dependencies between objects, 3) law of Demeter - limiting the number of objects an object interacts with directly, 4) referential transparency - limiting the tendency for methods to rely on a tangled web of tedious-to-establish object states.

However, like other design constraints, testability can be detrimental when misinterpreted or carried to uncalled-for extremes. This list of downsides is more applicable to statically-typed source code that strictly enforces encapsulation (although dynamic typing is no excuse for shoddy object design, of course).
  • Exhibitionist getters and setters. The abuse of getter and setter methods is one of the evergreen blog debates. In the context of pursuing testability, inappropriate getters and setters happen because setters make object setup less of a hassle and getters make verification of a test result less of a hassle. One of the key guidelines to remember when writing unit tests is that, as much as is feasible, the test should be treating the object the same way as actual client code, so the test checks scenarios that matter. Would actual code reach down through the object's throat in order to get a drink? The true problem, as always, is that all access points that an object publicly exposes are by definition part of its (implicit) interface. Details that an object doesn't hide now have the potential to cause cascading maintenance headaches when the details change later. (Please realize that this isn't an attack against dependency injection. Getters and setters for systemic "service" collaborators, abstracted behind interfaces, is different than getters and setters for the object's internal data.)
  • Interface explosion. Since unit tests are meant for repetition, side effects are undesirable. A foolproof avoidance technique is to store objects with side effects as interface types and substitute fakes behind the interfaces during tests (this is also helpful for integration tests). The tradeoff is that interface types added purely for the sake of testing enlarge the code's size and complexity without enabling the code to do more (dynamic typing cheerleaders would say this is true of all static types). Although switching to an interface hypothetically prepares the code to work the same in the event that the class is swapped out for another, few programmers (not me) have the rare ability to exactly foretell just what common elements should be in an eternal interface. Moreover, including extraneous types, indirection, etc. conflicts with the principles of avoiding big up-front designs and stuff that's good but possibly inconsequential (gold plating). Programming to an interface instead of an implementation is good practice if an object is to accommodate frequent or dynamic replacements of its fellow objects, but this is not always the case. At this time my preference is a different strategy known as "extract and override": extract just the code that causes side effects, then override that code with a stub in a "testing version" subclass that matches the actual class as closely as feasible (similar to the Template Method OO design pattern).
  • Ugly factories. Where many interfaces are present, factory objects may be nearby, hence testability can also lead to more factories. I appreciate the decoupling that factories make possible. I recognize that factories are essential sometimes. For example, the code needs an unknown instance to fulfill an interface or an unfortunately-written object that is a chore to initialize. But I dislike factories. It feels icky and hacky to create a concrete object via a different object, not by a constructor. When objects don't have sufficient contextual information to create the right helper objects - since the more an object must know about its environment to function, the less easily it can be reused - my preference is handing those objects in (through a constructor or setter method). I'll admit there's a limit to pushing out the burden of object creation because the objects must be selected and instantiated somewhere. Centralizing object creation in an overarching Factory or Configuration singleton is a simple option but it also fails modularity. A few uses of the Abstract Factory OO design pattern could be a happy medium between factory-per-object and factory-for-all.
  • Bureaucratizing the simple. The balancing of design constraints is a cruel problem in which a compromise does what a compromise does: leaves everyone a little disappointed. After successfully dicing a jumble of concerns into testable object atoms that don't overreach, the way to accomplish a useful requirement is to...assemble an application from atoms. That's an exaggerated negative perspective, but bloggers have been mentioning or insinuating similar remarks about the standard Java APIs for a while (others might, but we understand the value of writing code with standardized stream and reader abstractions, as well as the necessity of not confusing bytes and characters in the Unicode world [Earth]). Like a UI, an API object design should have its knobs and buttons laid out understandably. Typical activities belong at front and center, with unobtrusive advanced flexibility/extensibility in the corners for heavy-duty users who know what they need. In terms of test coverage, convenience methods that straddle multiple objects are fine on the condition that the methods do nothing but delegate to fine-grained, unit-tested objects.
  • Library insulation. Libraries complicate unit tests. Closed-source libraries can't be modified in favor of greater testability, and the cost of modifying open-source libraries for testability is often significant. Insulating the code from the libraries with a lot of layers and interfaces, for faking the libraries during tests, seems like an overreaction. On the other hand, it's also wasteful writing unit tests that truth-be-told mostly exercise a mature, vetted library and just slightly exercise the new custom code that calls it. Once again, my inclination is extracting the code that needs testing from the library code that doesn't, and minimizing the untested "glue" code that actually bridges the two (like the rule that the Model and View of MVC can each be intricate but not the code in-between).
I've read that the intrusiveness of unit testing on design is meant to be embraced. But I'm sure no one is suggesting unit testing at the expense of an uncluttered, understandable, maintainable design.

Wednesday, November 12, 2008

functional languages and template engines: a StringTemplate docs quote

From the StringTemplate documentation (I started trying StringTemplate out just yesterday, when I was looking around for a currently-maintained template library for C#):

Just so you know, I've never been a big fan of functional languages and I laughed really hard when I realized (while writing the academic paper) that I had implemented a functional language. The nature of the problem simply dictated a particular solution. We are generating sentences in an output language so we should use something akin to a grammar. Output grammars are inconvenient so tool builders created template engines. Restricted template engines that enforce the universally-agreed-upon goal of strict model-view separation also look remarkably like output grammars as I have shown. So, the very nature of the language generation problem dictates the solution: a template engine that is restricted to support a mutually-recursive set of templates with side-effect-free and order-independent attribute references.

Sunday, November 02, 2008

stuff white people like, meta-midwest edition

After a recent conversation, I've discovered that white people based in the midwest U.S. enjoy Stuff White People Like. They appreciate reading wry observations about white people who differ from them. It allows them to feel better about not living like the people they see on TV, not raising awareness for popular causes, not buying the right devices, and not listening to public radio or appearing to enjoy classical music.

On the other hand, they fully relate to a fondness for shorts, dogs, apologies, living by the water (been to Michigan?), sweaters, scarves, outdoor performance clothes, etc.

Of course, midwest people in thriving urban areas or college towns just read Stuff White People Like because it is about them.

Saturday, October 18, 2008

maybe humans just swap out their thinking selves?

In the previous entry I questioned how much humans actually apply thought to their behavior. "First Person Plural", over at the Atlantic, offers another paradigm: humans switch between multiple selves that think differently. This very concept is reminiscent of I am a Strange Loop, which stated that the brain's many networked nodes commonly can and do house more than a solitary strange loop, which is the book's definition of a consciousness.

As I wrote previously in my response to the book, in my opinion the unitary self is an abstraction. It's a construct or a narrative. However, unlike the article I'm disinclined to replace the individual-self with a crowd-self, which to me seems like yet another helpful but ultimately imprecise metaphor for the reality within the functioning brain.

Instead of imagining a pair of rational and irrational selves, or the article's mention of the iconic angel and devil selves on one's shoulders, I imagine ephemeral electric storms competing to light up the brain. The brain's state is in constant flux. Observing our own brain state through the slim (censored) window of consciousness or others' through interpretation of their behavior, we categorize those states as selves. One self might be grumpy. A second, bashful. A third, happy. So these selves are no more than frames of reference for sets of associated behaviors. If a person's impatient self taps fingers or feet, then is tapping a habitual action performed by the impatient self or is tapping part of what defines the person's impatient self? It should be kept in mind that all of these supposed selves are exhibited by the same person.

Moreover, it then makes little sense to argue that all these selves are dormant or continually fighting for control. The described inner conflicts and the swapping out of selves are analogies for when some previously idle brain regions start firing more energetically. For instance, the tendency to suddenly start concocting inventive excuses when the time comes to carry out a presently unpleasant long-term plan isn't truly the substitution of one self for another. Perhaps nor is it the person reverting to a natural state of nonthinking, as I wondered in the previous entry. It's a concrete and immediate experience of displeasure galvanizing reactionary thoughts more strongly than the intellectually pictured goal.

In short, differing circumstances activate differing portions of the brain. Someone who has associated inappropriate actions with anger, maybe for many years, will have those actions prompted by anger and therefore appear to have a momentary angry self who acts differently than is typical. Since we are humans who are aware of our awe-inspiring capacity to store and retrieve a range of attitudes and impulses and reflexes, we should accept responsibility for them and attempt to mold them. Dividing up oneself into simplistic good and bad sides, then identifying with the good and striving against the bad is a faulty approach. There is only you, more specifically your brain and the networks and strange loops residing inside it, to blame for what you do. The road to freedom is taking charge of, exerting control over, your own brain.

Friday, September 26, 2008

how much do humans think, really?

Some realizations manage to be both obvious and mind-blowing, such as "much of the time, the majority of people don't apply thought to problems and actions". It explains a lot about human behavior, particularly for someone like me who at times has trouble correctly interpreting what others do. To clarify, "apply thought" refers to collecting data with a minimum of bias, analyzing and synthesizing the data, reaching a conclusion, creating a feasible plan that corresponds to the conclusion, and executing the plan. Since this is a description of the way in which people ascertain and manipulate reality, many examples exist of using it or not, across the wide spectrum of situations from choosing lunch to the most far-reaching decisions of rulers in the highest-ranking echelons.

One or more of those steps is usually skipped or botched. For instance, people substitute the conclusion step for the data collection step: "I don't need to hear anything more, I've already made up my mind". Or they disregard the analysis and synthesis step by reacting, shooting from the hip, doing a gut-check, winging it. They're ruled by their previously-trained mental associations to the stimuli. Or they mess up the plan creation step with faulty assumptions about how reality will respond to them. Or they know the plan but they do something easier instead.

On the other hand, I'm not naive enough to seriously suggest that the "apply thought" approach is all that anyone needs, all the time. It's slower, it can't operate when the data is insufficient, it requires effort, it's fallible simply because the thinker is fallible, it can prompt people to excessively trust their past conclusions and plans. For all these reasons, especially "slower" and "requires effort", people could quite reasonably argue that the cost outweighs the benefit to apply thought to tiny and small-term questions; I often agree. I even grant that large contingents of the population are, mostly through no fault of their own, ill-suited and disinclined to it. I'm also fine with the concept of other mental approaches as strong supplements.

The bit that depresses me is when humans just don't apply thought (as described here) to the questions that actually are complex and long-term, sometimes despite appearances. Those questions unquestionably merit it. And the mind-blowing fact is that someone who doesn't apply thought is someone who cannot be reasoned with. Arguments, details, etc. are all useless. They're speaking a different cognitive language, more or less. All someone can do is ensure they are "triggered" properly, not engaged in meaningful conversation.

The failure to apply thought may be most subtle, and yet the most apparent, in the case of habits. A habit is a succession of tiny and small-term conclusions and actions, and the succession as a whole is pivotal and long-term. Hence, the refusal to properly apply thought to one out of the bunch is minor and inconsequential, but the repeated refusal to properly apply thought to the aggregate is potentially catastrophic. The key is to perceive the invisible stakes stretching through time, not the minuscule visible stakes: this is the analyze and synthesize step. (I'm sure all casino gamblers realize that every "game" is by definition designed so most "players" lose, and in fact if someone devises an excellent strategy to win then the casino accuses him or her of "cheating"?)

It's revealing that a related expression, "just one more", is such a common catchphrase. A startling number of heart attack victims either don't change their behavior, change it too little, or change it for too short of a duration. When I'm most discouraged about humanity's future, I wonder if we're trapped in a deadly medium. Humans, or at least a few of them, apply thought sufficiently to empower themselves. At the same time they, or at least a few of them, don't apply thought sufficiently to handle the power well. (I'm not referring obliquely to the ethical use of power. I'm referring merely to using power non-stupidly, i.e. not being short-sighted.)

Saturday, September 13, 2008

you might be a Myers-Briggs INTP if...

...your immediate response to "INTP" isn't a variant of "What the...?" but "That one's easy: int pointer!"

If you then go on to start musing about how the INTP enforces the type of its referent, and how it handles overflow, well, there's even less doubt...

Thursday, September 04, 2008

the Algorithm will be incorrect

...and we the users (or maintenance programmers) write the pertinent definition of "incorrect". Software and hardware might execute flawlessly the exact steps proscribed by a set of exhaustive written policies. If that is the case, good jorb and a round of gold star stickers to everyone responsible. Still, the algorithm is incorrect each time it produces a different result than we think it should.

The way to cope is to plan ahead for inevitable incorrectness. As much as is reasonable, assumptions should be flexible. Options should be open-ended. Overrides should be available; users should be advised and protected from mistakes, but some users have their own good reasons to request something "incorrect".

A perfect algorithm will be incorrect. More poetically, a perfect algorithm is too good for this imperfect world.

Monday, August 25, 2008

8 perceptual metaphors for OOP objects

Why Learning F# Is So Difficult at got net? (I compliment the blog's subtitle "Kevin Hazzard's Brain Spigot") has an insightful explanation for why functional programming code is so confusing at first: usually, programmers don't have the necessary perceptual primitives, the prototypes, that would enable rapid recognition/comprehension of the code. They're expecting mutable variables, not immutable lists. They're expecting "foreach", not "map". They're not expecting statements, expressions, and functions to be somewhat synonymous. They're not expecting functions to be inputs or outputs of functions, nor are they expecting partial function evaluation. They're not expecting recursion to play a large role, perhaps because they assume it will blow the stack (which is a good opportunity to explain tail-recursion optimization). And, naturally, they're not expecting the operators to look and act so differently.

That said, the need to learn new perceptual prototypes as part of adjusting to a different programming paradigm applies to OOP, too. My experience of switching to OOP had its own moments of bewilderment, such as "Huh? Invoking a subroutine on this data type runs code two levels up in an 'inheritance hierarchy'?". I know of no less than 8 ways or metaphors for perceiving the most fundamental concept of OOP, objects:
  • Supervariables. The supervariable metaphor is most applicable to small classes that don't do much more than provide a handful of convenience methods for representing and modifying abstract data types. "Supervariable" emphasizes that an object not only stores a small conglomeration of data but also provides prefabricated code for common tasks on that data. An alternative name for this metaphor is "smart variable". A ComplexNumber object that has a produceConjugate method fits the supervariable metaphor.
  • Miniature programs. The miniature program metaphor is most applicable to classes that, like a control panel, expose intricate, full-featured capabilities. "Engine" in the class name could be a clue. (In practice, objects that are closest to "miniature programs" probably employ I/O and various other objects internally.) The point is that creating an object and running one or more of its methods can be similar to starting a program and directing it to carry out actions. Of course, the metaphor is explicit when a program specifically furnishes an "API object" interface in addition to GUI or command-line.
  • Nouns. The noun metaphor is the most straightforward and the most taught. The object represents a noun and simply consists of the noun's attributes and activities. It's a Car, a Square, a Dog (that inherits from Vehicle, Shape, Animal, respectively). It's an abstract simulation of its real counterpart in the problem domain. Since in nontrivial problems 1) the problem domain overflows with too many irrelevant nouns and 2) not all necessary parts of a program have real-world analogues in the problem domain, the noun metaphor isn't seamless nor self-sufficient. It's a good starting point for analysis and design, however.
  • Memo senders. One of the tricky mental shifts for OOP beginners is decentralization of control flow, especially when it's event-driven. Each object has its own limited, distinct responsibility and role, so the way to accomplish a larger purpose is collaboration and communication among objects. No object is inherently primary, though execution must start at one particular method. In OOP, the metaphor of object interaction isn't a top administrator dictating orders or stage actors enacting a script; it's a cooperative group of employees who send memos to one another in order to complete individual assignments. And "employees" shouldn't need to "send memos" to every other employee in the "building" - see Law of Demeter.
  • Lieutenants. From the standpoint of the memo-sending metaphor, the "lieutenant" metaphor is about the contents of the "memos". A lieutenant (or vice-president) isn't told exactly what to do (the "how") but the goal to be met (the "what"). The value of the lieutenant is in delegation. Objects shouldn't need to know much about other objects' work, as long as the objects do their "jobs". There's a good reason for this advice: the more an object knows and therefore assumes about other objects, the harder it is to modify and/or reuse each object independently. Effective error handling often requires a delicate compromise of an object knowing just enough about possible errors to respond the way it should - no more, no less (should the object abort, retry, ignore, fail?).
  • Instantiable scopes. Unlike the noun metaphor, the "instantiable scopes" metaphor is not likely to be included in introductory texts. It's one of the more obvious metaphors to seasoned programmers but one of the least obvious to novices. This is a rather literal and stark "metaphor", which is why it's of more interest to compiler and interpreter writers than to analysts. An object is a "bundle" of functions and variables whose implementation involves "this" (or "self") pointers and vtables. Sometimes, people involuntarily learn this metaphor when a serious problem occurs. Developers who are trying to add tricksy feature extensions to a language may need to think of objects in this way.
  • Data guards. Substitute "bouncers" for "guards" if desired. In this metaphor, objects are protective intermediaries between code and data. The object somehow mitigates the downsides of direct data access: checking that indexes are in-bounds, tying the disposal of acquired resources to garbage collection, etc. Like the lieutenant metaphor, a data guard object can separate an accessing object from dangerous knowledge, because the accessing object knows only as much about the data as the guard allows. This means, for instance, that the guard could change to obtain the data from a different source, or the accessing object could be reused in several different contexts at once as long as its data guard functions consistently. The obvious downside is that superfluous guards and multiple layers of guards complicate and slow the program.
  • Sets of behaviors. This is the most abstract metaphor of all. Many OO design patterns use it. According to this metaphor, objects represent actions instead of, well, objects. The object's focus is the execution of a specific algorithm, and not the expression of data. Therefore, the typical name of a behavioral object refers to the role it serves in the program: bridge, adapter, factory, iterator, strategy. Viewed through this metaphor, the important difference between all objects is behavior. Objects that have the same behavior shouldn't require multiple classes. The behavior of subclasses shouldn't violate the behavior expectations of the superclass. An object is what an object does.

Friday, August 15, 2008

something worse than missing sarcasm online...

...is assuming the online statement is sarcastic when it's actually sincere.

"My mistake. I figured that he or she couldn't possibly be so ignorant|silly|dumb|unoriginal|hateful|presumptuous|arrogant."

It also has happened when I try to adjust to common interpersonal communication after passing time reading forums. People think I'm acting arch just because I look at them quizzically after they say something that's flatly ridiculous and I'm genuinely unsure whether to take them seriously.

Monday, August 04, 2008

Tale of the Self-virtualizing Robot

Imagine a smart robot that can interact with its environment in complicated ways akin to a human. This robot is so smart that its mental model of the world is at a similar level of sophistication. Its senses, goals, and calculations all are different from a human's, but nevertheless it's more than capable of devising plans of action or correcting itself based on past mistakes, for instance. It also can theorize about the future.

Moreover, the clever robot can employ these abilities to perform a great trick: it can project its own thoughts and actions. That is to say, it has a perfectly accurate internal concept of its operation that it can apply to hypothetical data to determine what it will think and do. After successfully doing this once, it recognizes the value of incorporating self-prediction into its long-term considerations. It proceeds to run the self-prediction task more and more frequently, as data comes in. It's executing its program as part of its program. In effect, it's self-virtualizing.

But now the story takes another twist. Sooner or later, the self-virtualizing robot discovers that the computed action its "virtualized self" will take is suboptimal, or that the computed answer its virtualized self will produce is incomplete. It duly notes and accounts for this new information, thereby shifting its current and future thoughts and actions accordingly, as surely as the sum of 4 and 6 differs from the sum of 8 and 3 . Yet doesn't this mean that the robot perfectly predicted itself wrongly? Is this possible? Through bypassing and modifying its programming to decide differently, does this robot have as much freedom to choose as any human?

I think so. I'm convinced that the phenomenon that we experience as "choice" isn't a peculiar loophole in causality but instead a complex interplay of factors such as emotion (drives), reason, and creativity. The complexity is the first reason people assume it to be causeless, i.e. free. Second, the factors change within each person, sometimes gradually, sometimes abruptly (e.g. "the last straw" or psychoactive substances). Third, awareness of one or more factors can function as a factor--feelings about feelings, perceptions about perceptions. Fourth, people can ponder ideals, in a fact-value distinction. Fifth, they want to believe that they are in control. Sixth, they want to believe that their motives have superior subtlety.

If the self-virtualizing robot sufficiently mastered language to claim it could act independently of its programming, how would anyone convince it otherwise?

Saturday, August 02, 2008

questions about Truth in endlessly astounding reality

Truth was defined in the previous entry as nothing more than the measure of correspondence between specific ideas and reality. This (singularly unoriginal) stance provokes follow-up questions, whose answers serve to clarify what I meant. I wish I could properly categorize my concept of Truth, but I don't have sufficient technical knowledge and/or scholarship; pragmatism's seems similar.
  • Q: For someone to accept this definition of Truth, isn't he or she forced to assume the truth of the definition itself? A: Yes, but all definitions of Truth have the same "shortcoming". All definitions of Truth are statements, thoughts, etc., whose truth cannot be accepted except on "their own terms". Truth definitions can't be logically derived. However, one can assume the truth of the Truth definition and then apply it to itself. In my encounters with reality, to define Truth as the measure of accordance with reality is to define Truth in a way that has a high measure of accordance with reality, i.e. this definition of Truth hasn't been misleading. Hypothetically defining Truth as what Simon Says would fall apart as soon as Simon said something impossible, such as a self-contradiction (bad Simon!).
  • Q: What about the truth of "32 - 15 = 17"? A: That exact statement is neither true nor false. It doesn't correspond to reality--it's about "mental realities", symbols, generalizations. A remarkably similar statement like "separating 15 pennies on this table from the original grouping of 32 pennies will result in a grouping of 17 remaining pennies" is either true or false, but my reason suggests it is true. The utility of math, or any other formalized system of reasoning, is thanks to a human discovery: the assumption of an exceedingly small set of true statements and (formal) rules for truthfully combining statements enables an explosion of creative proofs of other true statements, "theorems". As long as the formalized reasoning system's symbols and rules indeed correspond to reality, the theorems produced should, too. The ideas known as quantities happen to correspond so easily, so well, so unambiguously, and so consistently to reality that we forget their existence is purely mental.
  • Q: Can't a proposition be true, and therefore part of the Truth, regardless of whether anyone is convinced it is true? A: Nope. If truth is the measure of correspondence to reality, that measurement (or judgment, etc.) must be executed and evaluated by someone who comprehends both reality and the proposition! No individual truth is "substantial", since it's a characteristic of the relationship between an idea and reality. Reality is "substantial". Reality is. In other words, reality doesn't "depend" on propositions. Reality's "realness" isn't contingent on the thoughts of humans. Truth is the extent by which people's thoughts succeed at agreeing with reality, not the extent by which reality succeeds at agreeing with people's thoughts.
  • Q: Given that Truth is this flimsy, and the people like us who judge it are known to make so many mistakes, how can any belief have "solid" truth? A: Quite right. This question has no easy answer, but that's expected in the face of endlessly astounding reality. The fact is, both the observation of reality and the accompanying checking of truths proceed using a plethora of methods and degrees of reliability. (A few are in wikipedia is not an epistemology silver bullet, the 12th blog entry.) As a result, some truths are more "solid" than others. The criteria to rank them is a procedural matter, not a philosophical or foundational one. Just as children learn to be skeptical after playful adults tell them lies for amusement, people continue gaining (heuristic) expertise at weighing truth. Even those who say they rely predominantly on reason to obtain truths aren't immune to making mental mistakes or starting from the wrong premises.
  • Q: To define truth's domain as the combination of thoughts and reality, doesn't that presume thoughts must be distinct from reality, i.e. thoughts are unreal? A: The terse answer is that, yes, thoughts are unreal, regardless of how true. The more elaborate answer is that although thoughts are unreal, thoughts that more closely correspond to reality (more truthful) are more "real", figuratively speaking. Note that thoughts that are at least somewhat true can still be useful, through analogy. True thoughts can act as close "simulations" of reality. In the most extreme case, thoughts that prove to be very true are what people designate as reality.
  • Q: Hey, wait a minute, now you're double-talking--if people's experiences of reality are thoughts themselves, how can people ever really determine the truth of all their thoughts versus "reality"? A: This is a variant of the age-old question "How can I know for sure that all of my experiences aren't illusory?". The absolute answer is "You can't". The practical answer is "Thoughts you can't ignore, thoughts that are most consistent, are the most true, i.e. the closest to being real and the most deserving of the convenient label 'reality', if only temporarily". Essentially, thoughts are the unreliable messengers of reality, and all people can do is compare the messages, assign degrees of truth, and in so doing compute a semblance of reality that's meaningful and usable. Think of it this way: if one thought is of a winged horse while a much more vivid second thought is of a computer screen, the second thought is likelier to be more true and "real". If turning one's head causes the thought of the computer screen to shift position, then the thought is considered still more true--among thoughts, direct sense perceptions are usually treated as true representatives of reality. But not all sense perceptions are true, and even the least ambiguous require significant interpretation...which reemphasizes the point that the heuristics of judging truth can't be simple and free of exceptions, due once again to reality being endlessly astounding.
  • Q: By defining truth as secondary to reality but refusing to unequivocally define reality, in what way does this scheme clarify what Truth is? A: Er...huh. The thinly-veiled motivation behind this standpoint isn't primarily to pronounce Truth's composition, but to promote reality through demotion of other philosophical items such as Truth. Reality is real; to be true (and figuratively "real") everything else must be based on reality. That which bears no relation to reality shouldn't be thought of as true--perhaps beautiful, good, even "correct" according to some standard, but not true. And reality itself needs no underpinnings.

Friday, August 01, 2008

defining Truth in endlessly astounding reality

The most incomprehensible thing about the universe is that it is comprehensible. -Albert Einstein
I'll start with the reminder that philosophy is neither my vocation, hobby (avocation), or education. But my college degree did include snazzy liberal arts classes. (The only course I disdained was art, in which I received low grades for my art criticism. My excuse could be either the indeterminate basis for evaluation, the capricious professor, or the unmitigated subjectivity of the "correct" answer. Yeah, I'm bitter.)

My definition of philosophical Truth is straightforward if ridiculously unoriginal: Truth does not exist. In more precise language, truth isn't transcendental. It's a common noun, not a proper noun (notice the capitalization difference?). It's never found alone because it's the measure of the correspondence between specific thoughts and reality. Where there's no thought, there's no truth. Where there's no reality, there's no truth.

This definition of Truth has the advantage of matching the way that all people learn what truth is. Truth is not lying. A lie is detected by comparing the statement to ascertained reality. Even the fictional truth of a fictional statement uttered by a fictional character can't be judged without reference to the fictional world it's from. A statement that cannot be disproved is neither lie nor truth. When people have wondered out loud to me whether reality is "really" the Dream of an unknown Dreamer, my reply has amounted to "So what?"

Another reply I could make to the same conjecture is "For being a Dream, reality is much more ordered than what I experienced when I was sleeping last night". And this reply also expresses something about the discovered nature of (common, not transcendental) truth. Reality is experienced as orderly. It's also unfathomably complicated from moment to moment, but orderly nonetheless. In fact, as creatures who have developed to productively interact with our environment, we're "hard-wired" to quickly generalize and then apply patterns, a strategy that would fail if reality had none. Thus, we repeatedly confirm the applicability of patterns to reality so often that it's strange to mention it. Since the patterns correspond to reality, by definition the patterns have truth. Moreover the pattern-making process must have truth for it to produce so many truthful patterns. But what is the usual philosophical term for the patterns and the pattern-making process? Reason. Reason has truth because it produces thoughts that correspond to reality, but reason couldn't produce those true thoughts from other true thoughts unless it operated in a way that corresponded to reality. For instance, predicting an object's future location based on its current trajectory works because objects don't change trajectory without cause. Reason works because it has truth. When reason stops working it's no longer (perfectly) corresponding to reality, and by the definition no longer has (the same amount of) truth.

However, since transcendent truth is nonexistent, transcendently-truthful reason is nonexistent, too. Similar to truth, reason is a lower-cased common noun. Put simply, reason doesn't always yield truth. Reality doesn't guarantee a confirmation of reason, seeing as how it doesn't guarantee a confirmation of any proposition or statement or thought. Reality overwhelms the capability of every example of reason sooner or later. Reality is not perfectly predictable. It's not computable in its totality, not even by an impossible all-knowing demon. Reality is endlessly astounding.

Nevertheless, I'll attempt to reason out anticipated questions in a second part.

Tuesday, July 29, 2008

peeve no. 259 is calling supernatural stories "sci-fi"

Of all the mislabeling of entertainment that goes on, none sets me on edge quite like "sci-fi" referring to stories that contain the supernatural. I appreciate that authors don't feel obligated to confine their ideas into neat little categories (nor should they!) and that consequently none is adequate. I recognize that almost from the very start sci-fi, science fiction, bifurcated into stories based on the one hand on the "hard" science of experimentally-verified theories, and on the other hand on "soft" scientific or technological speculation. Moreover, a work's scientific "hardness" lies on a fuzzy spectrum because some of its ideas could be hard and others soft. If the work is episodic, then each episode could be different on the scale. I enjoy both hard and soft sci-fi, although I prefer that a story strives to stick to its chosen hardness rather than leaping sloppingly over the line and back.

My limit is really pretty simple: once a story is so soft on science that it includes the supernatural, its sci-fi credentials should be revoked at least temporarily. I'm okay with exposition like "the 'telepathic' race communicates with one another through radio waves". I'm not at all okay with "the being made of energy can create and transform matter at will". Or rather, I'm not okay with that character's story to be known as "sci-fi". Science's relevance to that story is slim.

The dividing line shouldn't be that hard to distinguish. If a story entails a concept that science contradicts, casts doubt upon, or dismisses from consideration, then the story shall not be deemed science fiction. If a story's suspension of disbelief involves jettisoning the conventional scientific perspective, then the story is outside science--beyond it, if you prefer. It's a supernatural story. Such stories might have value for any number of reasons, but worthiness of the "sci-fi" description isn't one.

Tuesday, July 22, 2008

musings on I am a Strange Loop, part 4

I am a Strange Loop is a book about consciousness that displays unashamedly the imprint of the author. Its examples come from his life. It's far from emotionally sterile, since it includes his reactions and opinions. It offers glimpses of where his ideas originated. It includes excerpts from his letters. It shows his sincerity.

It advocates vegetarianism. To his credit, the author honestly relates how his attitude has developed and also when he has and hasn't eaten accordingly. He justifies it better than most vegetarians I have met, who focus either on the notable health benefits or on reminding everyone around them that meat does, in fact, come out of slaughtered creatures, as if the embedded blood and bones weren't sufficient clues. Belief in consciousness as a strange loop is intriguingly novel fodder for the vegetarian assertion that greater similarity in form between a human and a creature implies greater similarity in the experience of existence, pain, and death. The more that a creature's capability of awareness approaches a strange loop, the closer the creature is to being conscious, i.e. closer to being like us mentally. Monkey brain is therefore even less palatable than before.

My stance, to the extent I have one on the topic, is that consciousness and strange loops aren't pertinent to the decision of vegetarianism. Pain shouldn't be inflicted recklessly, regardless of the victim's intelligence, and power to take an action doesn't excuse it. Nevertheless, consuming other creatures ("ingesting flesh", as a vegetarian might say) doesn't have to violate those maxims, in my evaluation. Killing or torturing a creature in brutal fashion would be unethical. Killing a creature for no purpose than fun or sport would be unethical, too. Excessiveness to the point of eliminating a species, no matter the "mildness" of the individual deaths, would be unethical. A person participating in the food chain, without malice, is merely natural. Color me a concerned omnivore...an omnivore who doesn't eat a lot of meat due to the nutritional hazards, which are aggregately dire.

Besides vegetarianism, the book advocates something else: Bach. A writer can extol any composer he or she wants; that's a prerogative of writing. I won't object to the suggestion that musical preference is often symptomatic of someone's sense of identity, because music is perhaps the most vivid expression of a culture and cultural identity can be instrumental (pun intended) in self-identity. I'm agreeable to the still-stronger hypothesis that the deep "shape" of someone's consciousness interlocks more firmly with some individual musical pieces than others, depending on the piece's use or abuse of rhythm, melody, tempo, tone, harmony. However, I don't accept that the amount of attraction toward particular music is reliable for gauging a consciousness on any scale of measurement. The relationship between consciousness and musical appreciation is too nuanced, too dependent on other factors--multivariate. Having said all this, I don't care much for Bach, though I enjoy isolated compositions. That's a low measure of praise, considering I enjoy isolated compositions in almost every genre I've heard.

Finally, fittingly enough, is the epilogue, "The Quandary". It's a good summation of the book's major points, but what I like (the strange loop remarked as it contemplated its past contemplations) is the admission that the concept of consciousness as a strange loop isn't immune to the perceptual gap separating inner and outer life. A consciousness convinced that it has no independent existence remains a consciousness that assumes it has independent existence! Moreover, one of the ingredients of a coherent whole of consciousness is the inability to perceive its parts. (Of course, it's rather circular, anatomically speaking, to picture the brain having a meta-nervous system devoted to sensing the activity of its neurons.) Without the unconscious and the subconscious, consciousness would be too distracted by itself to react to its surroundings, a situation which mental illness abundantly demonstrates.

Thus, all theoretical derivations of consciousness are doomed to failure in presenting a model that convincingly matches human experience, i.e. "common-sense". Scientific theories for epiphenomena and illusions tend "to ring hollow" anyway, no matter how much support the theories have. Counterintuitive theories are like complex numbers and transcendental numbers. Simple questions can have correct answers that defy expectations. Whether to respond to these answers with dismayed doubt or energized awe is the choice of the learner. Hofstadter has undoubtedly made his.

Monday, July 21, 2008

musings on I am a Strange Loop, part 3

As I was reading I am a Strange Loop, I was surprised that two aspects of consciousness weren't deeply discussed or further emphasized. The omissions may reflect a disagreement between Hofstadter and me, or the admirable goal of keeping the book short and focused. The first aspect is the importance of language. "The Elusive Apple of My 'I'" is the chapter that explores the nature and development of "I" (self-identity). Its descriptions seem plausible, but my inclination is to more explicitly tie consciousness to language. Consciousness, abstract thought, language, and sophisticated interpersonal relations all are among the most distinctive characteristics of humanity (although many species have these qualities in lesser magnitude). Interpersonal interaction is connected to language, language is connected to abstract thought, and self-identity is an abstract thought. Language's first purpose is communication, yet it has other uses. Otherwise, why would people "think out loud" or "talk to themselves"? It's a ready method for analyzing and ordering thoughts. It's commonly used to construct cognitive "feedback loops" of putting a thought into words, then reacting to the words with more words, etc. (Thank your long-suffering counselor or therapist today!) It's a way to expand one's memory with external storage, through reminders, notes, voice recorders, or just repeated muttering that marginally extends the time period of short-term memory. What's most important, it allows for fiction: discussing and inventing the nonexistent. I don't mean to disparage nonverbal abilities such as visualization and intuition, but the superior information encoding of language, predicated on a combination of rules and flexibility, has furnished humanity with the means to construct ever-higher towers of knowledge ("standing on the shoulders of giants" and whatnot).

Language doesn't trap thoughts. Language is a tool. However, language does guide thoughts and train thoughts along well-worn paths. Each time someone forms a valid statement, the same rules of language that enable it to be comprehensible conform the communicator's expressed thought: choices must be made as to subject, adjectives, verb, adverb, prepositions, etc. Thus, merely in order to communicate a headache presumes the existence of "I" (or "my", "me", "Yo", "Je"). At some earlier time, other people said "I have a headache" or asked "Do you have a headache?" or "Does your head hurt?" Consequently, when I feel pain localized in my cabeza, it's simple to swap out the subject slot with "I", and the more adept that people become at such subject-swapping the more they learn what self-identity (consciousness) is all about. "I" ("me"?) is a set of sensations, emotions, and actions appropriated from the rest of society to be able to label the individualized stuff, the subject-ive. When I'm a member of a group, one definition of "I" is "the group member whose face I don't see" (and whose hands I control, whose voice I use). If a member of a group caused a collective project to fail by slacking off on an assignment, and the consensus is that this member is designated by the name "Terry", then it's logical for Terry to make the conceptual leap to "I am a slacker", assuming Terry doesn't deploy a range of coping mechanisms. Like any other human creation, "I" is spread from one person to another, one generation to another, which is why it's curious to question the evolutionary value of consciousness: "I" is a linguistic, social epiphenomenon that was and is discovered and disseminated through joint efforts of human groups. Natural selection bore intelligence and language; consciousness was a by-product. Hypothetically, a completely solitary human wouldn't have or need "I".

Additionally, a tie between language and consciousness supports the notion that "I" could be at least partly an illusion. The illusory effect of language has been noted by philosophers who professed the persuasive axiom that philosophy's prime questions are examples of misapplying words out of meaningful context: absolutes created through questionable manipulations of real statements. For instance, pondering if "purpleness" exists despite "purple" is clearly an adjective. While the level of reality of "I" is closer to that of a theory or generalization than of a literary fabrication, its tenacity on the mind and its seemingly rock-solid existence are similar. Just as Good, Truth, and Reality are simultaneously undeniable to all and nevertheless extremely hard to define satisfactorily in concrete and absolute terms, "I" is evident and ethereal at once.

The second understated aspect of consciousness is the importance of the separate brain areas and functions whose collaboration is the content of consciousness, if not the bedrock. Neuroscience is prone to repeatedly shattering the image of the brain as a single entity. True, it's one organ in one area of the body. But this one organ isn't homogenous. The "wiring" for receiving and interpreting sensory data is both highly significant and not fully understood. Each hemisphere has its specializations. Speech centers play a starring role. The amygdala and hypothalamus are just two of the regions whose activity can dominate consciousness. Then there are the functions that are so commonplace that people tend to notice them only during failures, such as motor control related to the cerebellum.

At those times, "I" abruptly becomes atypically ineffectual, and it's harder to picture "I" as an aloof, absolute dictator of the body. Other kinds of brain damage reduce consciousness in more drastic details like personality changes, memory problems, and speaking difficulties. The book mentions Alzheimer's, which I too have observed up-close in relatives--the gradual erasure of what makes the individual unique. All these afflictions, whether temporary or permanent, present a conundrum for the perspective that consciousness is metaphysical. In my experience, people with that belief respond by alleging either that the consciousness is "trapped" down deep inside a frail body or that the consciousness has partly let go. They may also say the consciousness will be its former "self" in the afterlife. I haven't received an explanation of why or how the consciousness would accomplish its reversion to a previous state, but I'm not heartless enough to demand one.

The point is that regardless of the connection between consciousness and strange loops, the harmonious froth in the brainpan between all the parts is key to what goes on. A theme of this book is the difference between the macroscopic and microscopic ways of examining objects and object interactions; microscopic physical particles and nerve cells underlie the macroscopic items that are more meaningful to us, and consciousness is best analyzed in the macroscopic domain as an epiphenomenon. Brain tissues and regions fit in a middle tier. This middle tier has more than enough to fill its own book, so its absence is understandable.

Leftover musings on I am a Strange Loop, on the other hand, fit in a part 4 tier.

Sunday, July 20, 2008

musings on I am a Strange Loop, part 2

The chapter "Killing a couple of sacred cows" argues against the use of or need for "free will". Human behavior is bounded from the outside by a sizable set of unchangeable constraints and motivated from the inside by a sizable set of desires and goals. Then why is a ghostly factor known as "free will" posited to explain decisions? When someone chooses, one desire has simply won out over another. How is human dignity in any way amplified by the notion that people can execute purposeless (essentially random) actions?

Although I see the point of this stance and I follow its line of thought, I must extend from where it ends. I believe that it is too dismissive of the degree of complexity and unpredictability that surrounds uniquely human choices. I'd venture that some human choices involve thought that is strange-loopy complicated, in fact, with categories building on categories (or, say, a categorical imperative?). A moldy-old observation about human nature is that, unlike beasts, a human who is ordered to do something might refuse merely because he or she was ordered. It doesn't matter what the concrete incentive or disincentive is--it's the "principle" of it and the principle is on an outer meta-level of moral meaning. A second observation as old as the first is that humans are the only beasts who kill each other based on differences in abstract beliefs, not just over mates or resources or retaliation for past injuries. In short, the arbitrary complexity of thoughts and categories that strange loops enable also enable choices to be arbitrarily complex. Moreover, the mental flexibility that makes dilemmas thorny concurrently makes highly creative solutions possible. People can compromise and mediate. People can dream up nuanced laws in an attempt to evenly balance competing concerns. The concept of free will may be misleading in saying that people are either free or willful, but free will is an exaggeration of the concept of abstract will. Humans can will ideas. Humans can love love. They can weigh the pros and cons of buying insurance.

A different proposition, just as elusive as free will, is presented in some other chapters: the proposition that a consciousness can be housed at multiple points in space and time. I prefer to use the label "consciousness" whereas the book often uses "soul". In my estimation, "soul" is too heavily loaded with previous associations to be reused without misunderstanding. After all, according to this book an instance of consciousness is a strange loop of symbols, i.e. a pattern, which is a far cry from what most would call a soul. And as politics has proved, terms matter--it's easier to convince people that a pattern, not a soul, can be copied and stored across space and time. On the Internet, data patterns (what did you think a file is?) flow continuously through time and space. Why couldn't the pattern of a consciousness?

In part 1 I agreed that an exact duplicate of the "host" of a consciousness--a person's body--would be an exact duplicate of the "hosted" consciousness too. Thus, I have no qualms about agreeing that an inexact duplicate of a consciousness' pattern, stored in another time and/or place, is an inexact duplicate of the consciousness. However, once again I diverge: I can't accept the application of this corollary to human relationships, no matter how superficially. It's too far-fetched to suppose something as multi-layered as a consciousness can be known by another consciousness, or stored alongside. One person cannot simulate a second person (to be fair, the book doesn't make this claim in so many words, although it does liken brains to Universal Turing Machines). Neither can one person know a single complete thought of a second person. Communication is too imperfect for the task: its bandwidth is too narrow, its encoding too imprecise. Not only that, but each communicator is working on a basis of strange-loop, high-level, guess-work knowledge about his or her own thoughts. The talker can't necessarily state for certain why item A reminded him or her of item B! The most accomplished storyteller can fashion a virtual reality through a story, but the reality experienced by the storyteller is not the reality virtually experienced by the listener. A storyteller who says that "I was devastated by the news", besides violating the show don't tell dictum, can't possibly communicate how he or she felt exactly. But a good listener can imagine an experience that approximates it, though too much imagination will lead to an embellished version. People can "grow close" but ultimately each consciousness only has an indistinct, shallow sketch of any consciousness besides its own. These rough sketches shouldn't be construed as capturing any more than the most rudimentary caricature of a consciousness pattern; the deceased don't "live on in memories" in any sense that merits the terminology, but they are remembered. People say they sympathize, but to authentically know someone else is an incredibly hard (and perpetually unfinished) undertaking that requires a huge trial-and-error effort by the knower and the known. Not everyone can.

The level of empathy in a person's consciousness ("soul") is one of the scales the book tentatively suggests for judging the person's degree of consciousness. If that is so, I don't see how. The book's first tenet is that consciousness is a strange loop originating from thought and perception. A relative lack of aggression toward fellow beings and a talent for intuiting a fellow being's perspective are good qualities, yet the two aren't correlated with the skills of thought and perception. Aggressive geniuses exist. Gentle dunces exist. People who are excellent at analysis and categorization might not be good at relating to people (they might have no more emotional range than a teaspoon). Others who are easily confounded by elementary math might be excellent at appraising people within ten minutes of meeting them. Moreover, an individual who has a natural knack for perceiving a psyche's pleasure and pain points may choose not to be a humanitarian but to be a master manipulator who expertly uses people to achieve selfish goals.

Drat! The cornucopia of food for thought furnished by I am a Strange Loop entices me to ruminate some more to come...

Saturday, July 19, 2008

musings on I am a Strange Loop, part 1

I am a Strange Loop by Douglas Hofstadter isn't GEB II, as the author ably explains in the preface. While GEB touched on many topics that apply to Hofstadter's perspective on intelligence and illustrated those topics with Dialogues, this book has tighter focus on consciousness and strange loops. It has a couple Dialogues, much fewer visuals, and mostly photographs in place of artwork (with some great color pictures in the middle). Escher and Bach have fewer appearances. But Gödel and his Theorem are still prominent, which is fitting given the applicability to strange loops. The writing tone remains frank and conversational and it brims with extended analogies, in my opinion too many. I particularly dislike the analogies whose length is excessive, or whose connection to reality is threadbare, or whose wordplay is nauseatingly cute (Hofstadter is remarkably bold here and in GEB--I wouldn't have the chutzpah to mention the coincidental similarity between "Gödel" and "God"). It also felt repetitive to me in its insistence on restating the same conclusion in several ways, but I respect the effort to thoroughly explain rich yet abstract notions. For intangibles, especially directed at a general audience, too verbose is probably better than too terse. At any rate, I believe that I understood what I read, which counts as praise to any nonfiction author. Also praiseworthy is the undeniable pleasure I experienced through the reading. I hope it's not gratuitous to express my gratitude for the gratification, and I hope that it gratifies the one responsible for the gracious gratuity of an additional book beyond GEB.

But like my other "reviews" I'm less interested in critiquing style than musing on ideas. The book's title is apt, since the topic is the big questions about consciousness and the primary answer is strange loops. A strange loop occurs when multiple "levels" exist but the levels mutually intertwine such that repeatedly jumping "outwards" from level to level results in a return to the starting level. All the "level-jumping" in a strange loop appears to be paradoxical until viewed in a larger context, where the strange loop is clearly seen to in actuality be a circular entity (a loop) dependent on outside support for its creation and/or maintenance. An unofficial strange loop that I thought of is the (childish) economic question "where does a factory worker's money come from?". Well, the worker receives money from the level of a manufacturer, the manufacturer receives money from the level of a distributor, the distributor receives money from the level of a retailer, the retailer receives money from the level of a customer such as a plumber, and perhaps the plumber received payment from the level of the factory worker...

Gödel-like self-reference is a more subtle example because it consists of jumping between levels of meaning. To not be patently meaningless, a "self-referential" statement must jump to a new level of meaning. That is, self-referential statements must be considered as meta-statements. Assuming I comprehend him correctly, Hofstadter asserts that consciousness is a similar instance of a self-reference. Consciousness happens when the capability to think and perceive is sufficiently powerful to have its object be thinking and perceiving. The strange loop of consciousness is a thinker who thinks about his or her thinking, thereby "jumping out" to an enclosing level of context/meaning, and then possibly thinks about thinking about his or her thinking, and then possibly thinks about thinking about thinking about his or her thinking (as an aside, I wonder precisely how many meta-levels a book about consciousness has?). One of Hofstadter's objectives is to show that the strange loop of consciousness isn't really infinite, paradoxical, or otherworldly, but it is a natural epi-phenomenon which is a consequence of the symbolic, abstractive power of the human brain attempting to "wrap around" onto itself like how a Gödel theorem wraps a symbolic logic system around onto itself.

This means that consciousness is a highly convincing illusion. I like the comparison of consciousness' reality to the reality of a rainbow. A rainbow appears to be definitely located and as colorfully vivid as a flower, but if the viewer or the light moves or the water stops then the rainbow vanishes. I was a bit relieved to read someone else writing that consciousness is illusory because I've thought the same thing numerous times. Consider how little consciousness/attention a learned or practiced behavior requires. Consider that in split-hemisphere experiments the verbal hemisphere claims to have performed an action that the nonverbal hemisphere just did by itself. Consider the many, many cases when people "unintentionally" express or "betray" their "true" beliefs/feelings. Consider the many, many "gaps" in consciousness that a typical person experiences: sleep, intoxication/sedation, hypnosis (I remember going through a childhood stage of not wanting to fall asleep due to anxiety about the accompanying lack of consciousness). Consider how many of consciousness' conclusions are post-hoc--"I snapped at someone, therefore I must be cranky". Finally, consider the often-abysmal accuracy of people about themselves, the pleasant fiction they cling to--if you doubt whether someone is innocent, just ask him or her, they'll tell you.

To categorize consciousness as illusory is to conflict with dualist perspectives on consciousness. Some of my favorite parts of the book contain patiently logical dismantling of dualism. Its words again mirrored some of the deductions I have reached independently. Belief in objective and physical causality (is there another kind?) implies that a consciousness, a soul, is an unavoidable and convenient mental/social concept rather than a "known unknown" that in some magical way exists separately from reality yet simultaneously impacts it. Before reading this book I've pondered the question about whether unique self-identity is preserved after a perfect clone emerges from a vat or a traveler from a teleporter, and I reasoned that an exact duplicate must exhibit the "same" soul/identity as the original. If no practical method exists to make the distinction between x and y, x and y are for all purposes as identical as 3 and 2.999999999... The same proof applies to distinguishing a machine's or zombie's thoughts from a human's thoughts, which is another question that I encountered before reading this book--if the machine or zombie shows every indication of being as conscious and capable as a human, then it's reasonable to say that the machine or zombie has a claim to a consciousness/soul every bit as much as a human does.

I have more musings to be covered in subsequent parts.

Friday, July 18, 2008

the Woven cipher

Like the previous other two ciphers, the Woven cipher isn't great at encrypting, but nevertheless it was an engrossing hobby project that had a few fascinating wrinkles (is it any wonder I sometimes have trouble explaining to other people how I spend my time?). The basic idea behind this cipher is to convert all the characters of the plaintext into binary digits (bits), combine the bits of adjacent characters by alternately taking n bits from one then the other, and then iteratively mixing the resulting adjacent bit sequences in the same manner until only one remains. The key is the set of n values to use, which the algorithm cycles. Forming an intact whole out of little parts through an alternating process is analogous to weaving, hence the name "Woven". A second excellent name, perhaps more accurate but less appealing, is "the Multiplexed cipher", where the multiplexing is done using fixed data lengths, as opposed to fixed time periods for instance.

To work, the algorithm can't be quite that simple...
  • Most of the time, when the numbers don't match up exactly right, a source sequence won't have enough bits. When that happens, the strategy is to instead use an extra 0. Since the "weaving" happens from right to left, least-significant to most-significant, these are leading 0s so the numerical value of the original sequence is unchanged. Of course, the intermediate sequences aren't numbers so the leading 0s matter very much. And if the very last sequence, or each chunk (7 bits long?) that rather long sequence is broken into, must be interpreted as a fully-representative binary numeral, there would need to be an additional convention like always prepending an extra 1. Extra 0s are needed if the sequences are different lengths, like a space's 6 compared to a letter's 7, and/or the sequence isn't evenly divisible by n. These filler 0s cause the encrypted (i.e. transposed?) message to be longer than the original. Unorganized testing shows that the amount of expansion for short sentences is commonly 100% or more, probably an exponential-like effect of extra 0s requiring still more extra 0s in successive iterations.
  • Another extremely common complication is an odd quantity of sequences to be woven. The coping technique I eventually settled on was to spontaneously create a "null" or "empty" sequence of length 0 to pair up with the loner. Loner or not, the quantity of data sequences to be woven is half the previous quantity of sequences, rounded up. For instance, 10, 5, 3, 2, 1. Or 8, 4, 2, 1. The rounding--throwing away information-- implies that when decryption happens, the encrypted message and the key aren't enough to reconstruct the original. This is one reason why the third decryption input, the original number of characters, is necessary upfront (the other reason is to let the decryption algorithm know for certain when to quit unraveling sequences).
I wrote the encryption and decryption using generators, which fit a lazy, functional paradigm. As Wikipedia says, a generator "looks like a function but behaves like an iterator". I think of it as an "on-demand" stream of values, potentially infinite, that you only receive when and if you ask for 'em--that's why a generator is "lazy". A generator function, to fulfill the role of an iterator, must have enough state to remember its "position" in the "stream" in-between individual requests for values. A closure meets that requirement--by definition a closure retains its own copy of the variables from enclosing lexical function scopes at the time of closure definition/creation. A generator implemented with a closure is then a function created by still other functions, so in addition to being lazy it's "functional". By the way, creating generators with closures, whether or not the generator is referred to merely as an "iterator", is covered extensively in Higher-Order Perl (finish making the book available on-line, already!). My choice to use generators came from a few silly motivations: 1) the old easy-peasy procedure for decimal-to-binary conversion--repeatedly divide by two and use the remainders--happens to produce an answer bit by bit; 2) I like functional programming and attacking problems through recursion, when I can get it to work; 3) once I start to solve a problem one way I'm reluctant to abandon it for a different way (that would feel like surrendering to failure and wastefulness), especially when I don't have a deadline.

In the encryption and decryption, the role of each bit sequence is filled by a generator. This includes the special-case generators at the start and finish, described in the code as "Initial" or "Final". These generators are somewhat similar to the rest but perform additional conversion tasks and/or obtain data directly from arrays. As a result the program's high-level design is also functional: first it constructs one or more function(s) that will produce the answer (bit by bit) and then it repeatedly calls the function(s). By contrast, the imperative design would be to manipulate actual bit sequences, not generators, by using one set of sequences to create the next set of sequences, etc. The loops are virtually the same in the imperative and functional designs, but the functional equivalent has generators in place of bit sequences--each set of generators is the input for the next set of generators. And each generator (a closure) only knows about the generators that were explicitly placed into its scope at creation.

Encrypt generators merge the output of two encrypt generators: they have two parents and one child. Decrypt generators do the reverse, requesting solely "half" of the output from one source decrypt generator and then dividing it up so that it can return the "left" half to one destination decrypt generator and the "right" half to another destination decrypt generator: they have one (-half?) parent and two children. Encrypt generators act like multiplexers. Decrypt generators act like demultiplexers. The individual generators know and do very little:
  • When an encrypt generator is created, it's passed a 'left' source generator, a 'right' source generator, and the number of bits to read from a generator before switching to the other--this number is one of the digits from the key, so only digits 2-9 are admissible in the key. The encrypt generator keeps a running count of how many bits it has processed, whether it is currently reading from the left or right, and whether either of the generators has run out of bits. If both generators are empty (i.e., return '') then the generator returns '', the empty string. If the generator set to return the next bit isn't empty, then return that bit. If the generator set to return the next bit is empty but the other generator is not empty, return a '0' for filler. Encrypt generators take one 'op' parameter. If the op is 'peek' then the generator skips updating any variables (and it also passes 'peek' if it needs to ask a source generator for the next bit) , otherwise it increments the bit counter and then switches which generator to read if the counter modulo the key value is 0.
  • When a decrypt generator is created, it's passed one source generator, which "side" of bits the decrypt generator will always ask for from its source generator (left or right), and the number of bits to process before switching the side that should receive the next bit coming from the source generator--once again, this number is a key digit. Decrypt generators take one parameter, the "side" that is requesting its next bit. The decrypt generator keeps a running count of how many bits it has processed and whether the next bit it reads from the source generator should be returned as a 'left' bit or 'right' bit. It also keeps short queues for left bits and right bits. If it's asked for a bit for a side whose queue isn't empty, then it returns from the queue. Else, it checks which side should receive the source generator's next bit. If the side matches, get a bit and return it. If the side doesn't match, fill up the queue for the opposite side with bits from the source generator, then return the next bit that does apply to the requested side. Naturally, at any time the source generator sends a bit, update the bit counter and toggle the "side" variable if counter modulo n is 0. When the requested side is "all", the decrypt generator does nothing more than return the next bit it receives from its source generator (which is always just from one "side"). The "all" case is purely for the last stage of decryption, when no more "unraveling" is necessary.
The "direction" of iteration over the key's digits (to determine the n for each generator) alternates between sets of generators. For example, if for one set the index into the key starts at 0 and increases, then in the next set the index starts at the maximum and decreases. This behavior is so all the key values are more evenly used among the sets.

My Javascript implementation of the Woven cipher is part of the blog layout, down at the bottom on the right. In the page source, search for 'wovencipher' (one word). If you use it or download it I am not responsible for any awful consequences. I don't advise you to use it for encrypting anything important or unimportant. Legal disclaimer, etc. It's purely for entertainment, educational, and/or edutainment purposes.

Thursday, July 17, 2008

hey genious, you misspelled something there

I know it's just mean to laugh at spelling mistakes. The point of writing is to successfully communicate, and most of the time minor errors don't hamper that. (On the other hand, if it's a setting in which correctness matters--e.g., formal messages or applications for jobs that involve writing them--little oversights might rightly or wrongly communicate subtle secondary information about the incompetence or apathy of the author!) Also, each time I jibe I'm inviting fate to trip me next so as to restore the Universal Balance. Yes, I know there's no such thing, but in case humanity does someday discover it I figure I should be prepared.

However, I draw the line at "genious", because that's just too hilarious and self-referential to pass up. Ha ha ha! I can think of some related quotes: "Judge a pig contest? I don't know, I'm no super-genius... or are I?" by a Mr. Homer Simpson. "Me fail English? That's unpossible." by Ralph Wiggum.

Tuesday, July 15, 2008

peeve no. 258 is dynlang users who disrespect Perl

Before I get my rant on, I'll do the lawyerly thing and attempt to clarify my terms to avert misunderstanding.
  1. "dynlang" is 9 characters shorter than "dynamic language", the name of a fuzzy category of programming languages that allow, support, and exploit the capability to make run-time changes to a program's very structure--its data types, classes/data structures, method/function dispatch. Although program interpretation is quite better suited than compilation to implement such a capability, it's too simplistic to assume that all dynamic languages are necessarily interpreted and all static languages are necessarily compiled, and for that matter too simplistic to assume that a particular language implementation never does both. (Groovy cheerleaders are fond of mentioning that their dynamic language is compiled, while compiled Objective-C code sends object messages at run-time.)
  2. "Disrespect" refers to an attitude, not to an engineering-like objective evaluation of trade-offs. An example of the latter is "Java's static typing nature can result in code that is difficult to adapt to unanticipated purposes." An example of disrespect is "Java sucks. And one of the reasons it sucks, and the people who choose to use it also suck, is that the stupid types strewn throughout a class do nothing but get in the way when I just want to pass in an instance of MyTempObject, and get the idiotic compiler to stop tying my hands on my behalf". (By the way, I apologize for the inaccuracy. This example of Java disrespect is a bit too lucid and courteous to match the corresponding real statements elsewhere on the Web.)
Disrespect is not the same as disagreement. I'll be frank: I realize Perl's syntax and semantics are a jumble of accretions, some of which are helpful more than not and some of which are harmful more than not. Quick, what values are false in Boolean context? What does the '...' operator do in scalar context? Which __special attribute__ do you use to define accessors and mutators (oops, that's Python)? Whenever I've written Perl, I've mentally chosen one of two "write modes": messy mode or neat mode. Messy mode, such as using the Perl monkey command line 'perl -ape', is good for processing one-time text files, but not much else. Neat mode should weight the concerns of readability and development speed differently than messy mode because programs that are used regularly spend much more time undergoing future maintenance than initial development. Neat Perl is choosy about what features to use. I agree that Perl code is frightening in wrong or reckless hands, i.e. in the grip of someone who only has messy mode. However, it's laughably naive to place most of the blame on the dollar signs and slashes...it's in the same league as being too distracted by the parentheses of Lisp-family languages to notice the self-referential power of expressing both functions and data structures as lists.

So I know that Perl has its problems. In fact, my encounters with Perl nowadays stem from two sources: 1) ad-hoc administrative tasks and/or data processing (but if I need to interact with one or more of standard formats, databases, JVM APIs then I switch to groovy), 2) upkeep of legacy Perl that acts either as "glue" between systems or as rough internal CGI interfaces for simple yet vital business tasks (I'd also note that the "legacy" Perl has no firm date for replacement). I'm not bothered when language cheerleaders call attention to Perl's weaknesses; this practice is hardly a new phenomenon, and when the latest critic repeats the same years-old tired refrain I can barely manage to react at all. No, what gets me worked up is when someone is dismissive, contemptuous, or mocking toward Perl, which has on numerous occasions, despite its acknowledged icky portions, served my purposes, enabled me to achieve unconventional solutions, and taught me intermediate-to-advanced programming concepts. Without Perl, I might have had to hack together a comparatively ugly combination of grep, find, sed, awk, etc., and shell script. (At the time I'm describing, pretty much the only other programming language available on the system was C or C++, and later Java.)

I'm not too irked by cheerleaders for static languages who throw rocks at Perl for fun. Their derisiveness is a subset of their general antipathy for dynamic languages. If Perl was the epitome of language design perfection then they would still pronounce it junk because of characteristics that by definition apply to any dynamic language: performance overhead, lack of mandatory type enforcement, multiple inheritance, chaotic data structures that are modifiable at will, and so on.

As the title says, the true irritants are dynamic language users who explicitly or implicitly assert that Perl is horrible, terrible. These users know the advantages (and disadvantages) of a dynamic language. They know Perl is a dynamic language. They know that they would probably rather use Perl rather than a static language to solve the same problems (okay, maybe some of them would instead use ML-family, Haskell, or statically-compiled Lisp-family--just maybe). Moreover, the reasons they give for why Perl is abominable sometimes strongly resemble the reasons static language cheerleaders give for avoiding dynamic languages altogether: too many operators and punctuation in general, syntax features that can interact in complicated ways, too few built-in data types (no "official" numbers or strings, just scalars!), the choice to use or ignore OO, special variables that affect execution, accumulated design cruft that makes some tasks too awkward or tricky. The stranger cases are dynamic language users who formerly used Perl primarily, but after changing their habits suddenly stop recognizing anything valuable about Perl at all. Perhaps they used Perl for the general benefits of a dynamic language, all the while having strong distaste for the actual "Perl-ness", and as soon as they could obtain a dynamic language without that flavor they were glad to kick it to the curb. Or (shudder) perhaps some current dynamic language users spent a long time solely in the static language camp, where they grew accustomed to decrying Perl as a mysterious, confounding mess, but after using and liking a dynamic language they continued to denounce Perl more out of habit than out of scorn for dynamism anymore.

Perl is what it is (except when it purposely breaks compatibility in some ways for Perl 6), though it does keep changing and code written using current recommended practices is superior to what you might remember. All I wish is for dynamic language users to give Perl the credit it's earned, is still earning, will earn. Stop mercilessly bashing it as if it ate your parents. But know that I join you in disliking sigils, list/scalar context distinctions, and autovivification.

Tuesday, July 08, 2008

performance dunderheads check this first...

(Note that even the best people are dunderheaded from time to time.) If an application has been deployed to a new environment but its performance is, say, 8x worse or more, ensure that the relevant database indexes are functioning properly. The hardware may be magnificent and the database well-tuned, but a sequential scan of many records will deliver a gut-punch to performance. It's also obviously not well-suited for caching.

Bonus tip: a good database engine has the ability to show the plan/strategy it computes for a query. Start there to find query optimizations that matter.

Monday, June 30, 2008

I am reality pondering reality

Philosophical thought experiments like the Chinese Room can lead to an unnerving conclusion. These experiments argue that an indispensable ingredient of human-like consciousness is semantics. Human thought uses "high-fidelity" representations of reality, not merely symbols. And a mind switches between "raw" (though still contextualized) data and symbols as necessary...while talking, for instance. Being minds, this is natural to us.

If an essential trait of genuine thinking is the affinity of thoughts to reality, then clearly the very capability to think is deeply influenced--informed--by someone's experiences. And if experiences inhabit a strong role as the substances of thoughts, then what the mind does is intimately tied to how reality impinges on it. Thus, whenever a mind thinks, one simplified interpretation is that reality is doing a computation on itself. An analogy is two parts of reality finding each other. Reality co-wrote the concept of tabula rasa.

An alternative statement is that sensations mold neurons but those same neurons perhaps significantly influence future thoughts. This is consistent with the research that has confirmed the benefit of deep and varied education, in the broadest sense, on effective mental function.

Wednesday, June 25, 2008

the Autonomous cipher

The basic outline of a cipher is that it combines a message and a key to produce unreadable text. At some level, then, the key is like a set of instructions that informs the encryption algorithm about what to do with the message. Compare the process to that of assembling an attractive grill. The message is like the parts, the key is like the sheet of directions, and the finished grill is the encrypted message (presuming the directions' language isn't French--"Le grille?...What the hell is that?!"). Why not have the key actually be a set of encryption instructions?

The Autonomous cipher uses this concept. In this cipher, each digit of the key represents two operations (the key "self-directs" the encryption to a large degree, hence the name Autonomous). The operations' instructions assume a "current cipher index" variable, i, of an array of numbers representing the message. For that matter, the message array could be visualized as a long "tape" and i could be visualized as a "read/write head" that shifts position along the tape:
  1. "0" increases by 3 the array value whose index is i and subtracts 3 from i; corresponding operations of the remaining digits will be displayed as pairs
  2. "1" is (3,2)
  3. "2" is (1,2)
  4. "3" is (3,-1)
  5. "4" is (1,-3)
  6. "5" is (2,3)
  7. "6" is (1,-1)
  8. "7" is (3,4)
  9. "8" is (2,-2)
  10. "9" is (1,4)
Bonus points to anyone who noticed the Turing Machine references. The rest of the encryption algorithm is simple (simpler than the Pointed cipher) because all it does is interpret the key.
  1. Convert the message to a numerical representation as an array of numbers. Duh.
  2. Set both i and the reading position of the key (key index) to 0. Perform the following two steps until the resulting value of i is 0.
  3. Use the key index to determine which instruction to execute, then execute it. This means i and the message array are updated accordingly. When i "falls off" either end of the array, merely wrap it around to the other end.
  4. Increment the key index. Wrap around to the start of the key if the end is reached.
  5. Undo the last increment of the key index; it wasn't actually used. Then append this last value of the key index to the end of the message array.
The decryption algorithm is the encryption algorithm in reverse (flip integer signs, change i before modifying the array, etc.). It should be noted that a more helpful visualization of the message array is a circle, because of the wrap-around. Study of the cipher leads to some conclusions.
  • Any given key may not really work for any given plaintext, for two definitions of "not work": 1) the key starts with a sequence like '18' (i + 2 followed by i - 2) and therefore halts before barely encrypting at all, 2) the key consists of instructions that will jump around but never return to 0 in a reasonable time for the message text. The most extreme example of case 2 is a key that starts with '9' (i + 4) then continues in infinite repetitions of '583' (i + 3, i - 2, i - 1). However, all finite keys will repeat eventually, and with enough repetitions all keys must return to 0. When the net effect of a key is to go from 0 to "x" on a message of length "m", there exist a number of repetitions "r" and a multiple of m "n" such that m*n = r*x. (Any multiple of m works for returning to index 0 because it's identical--congruent--to repeatedly moving from 0 to 0, 0 = (0 + m) % m.) Rearranging terms yields m/x = r/n where m and x are constants. One solution among many to this two-variable equation is r = m and n = x. Stated in practical language, no matter what a finite key does and no matter how long the message is, the index will certainly return to 0 after cycling through the entire key a number of times equal to the message length. For long keys and even longer messages, this absolute upper bound on the number of key repetitions is hardly restrictive. In any case, the key may return to 0 much sooner anyway, of course.
  • Although infinite keys may cause problems, any finite key may be arbitrarily extended to produce a longer key, a key that requires more cycles to return to 0. This extension can be accomplished with a couple techniques, but I suppose there are many more possibilities. 1) Replace one or more digit(s) with other digits that have the same net effect. The key thereby will take more iterations to return to 0, as long as none of the "intermediate" steps of the replacement lands on 0. Two '1' or '2' digits (+2 each) can take the place of one '9' digit (+4). A '76' (+4 then -1) can take the place of a '5' (+3). A more complicated replacement is '2' (+2) by '2233' (+2 then +2 then -1 then -1). And each '2' in '2233' can then be replaced by '2233', and so on--but each of these specific replacements only succeed when the value of i before the replacement point, "j", solves none of the equations (j + 4) % arraylength = 0, (j + 3) % arraylength = 0, (j + 2) % arraylength = 0. 2) At some point in the key when i is not 0, i.e not at the start or end, insert another (full) key. By definition, the net effect of the inserted key will be 0. However, as before, the inserted key itself must not incidentally hit 0 as it runs. Assuming the value of i in the enclosing key is "j" at the point before insertion, the inserted key must not ever attain the i index "message length - j" when it runs on its own. For instance, consider two boring keys for a simple message length of 8. Key A is '99' (+4 then +4). It has only one intermediate value, 4. Key B is '2143' (+2 then +2 then -3 then -1). To make B longer by inserting A, A could be inserted before '3' to produce '214993' because one of A's intermediate i values isn't 8 - 1 = 7. A couldn't be inserted before '4' to produce '219943' because (the only) one of its intermediate values is 8 - 4 = 4. The key would end up being '219', one digit smaller than the original! Notice that B could also be inserted into B, '2(2143)143', since B never assigns i the index 8 - 2 = 6.
  • Looking at the list of ten operations, some interrelationships stick out. Five decrease i (03468), five increase i (12579). For each particular change in i, one or two operations perform it. -3 has 0 and 4, -2 has 8, -1 has 3 and 6, +2 has 1 and 2, +3 has 5, +4 has 7 and 9. Complementary pairs of digits that undo each other's effects, and so are by definition keys, are (0 or 4) and 5, (1 or 2) and 8. Complementary triples of digits that have a net effect of 0, and so are by definition keys, are (1 or 2) and (3 or 6) and (3 or 6), (3 or 6) and 8 and 5, (7 or 9) and 8 and 8, (7 or 9) and (0 or 4) and (3 or 6). Remember that order doesn't matter in these keys. '385' is a key so '538' is also a key.
  • The digits of irrational numbers might not be as promising for keys as one would think. pi is not. '3141', one of the possible four-digit keys, is when pi stops. The square root of 2 isn't much better. It stops at '14142', five digits long. '141' having a net effect of +1 is a bit of a show-stopper.
  • A direct crack of a ciphertext, as opposed to indirect methods like brute-force and related-key, begins by looking for clues that help indicate how the ciphertext could have been generated. Metaphorically, the destination ciphertext aids in reconstructing and reversing the encryption journey. But for this cipher, the origin and the destination are the same in one aspect: i's value. The tracing of i hinges on examining the array values; the question is how each final array value differs from what it was before one or more visits from i. Unfortunately, those differences are difficult to compute, because determining the unknown starting value of each array element is precisely the goal of the entire cracking process. Nevertheless, since the starting values are in a known range and each individual increase is in the range 1-3, someone could calculate "ballpark estimates" for the number of "i visits" that possibly resulted in a particular number. The final number of the encrypted message array, an index into the key, also establishes a lower bound for key size. Additionally, a key that repeated before returning to 0 would probably show more telling patterns than a key that didn't. On the other hand, larger keys are more bothersome to work with.
  • Alternative bases for the key digits is a possible variation that would enable a greater quantity of symbolized operations. The operations could become as complex as desired, too, although all operations must be exactly reversible, have inverses. A key in one base could be equivalent (isomorphic) to a key in a second base as long as the set of symbolized operations for the keys was equivalent. The key in the larger base would then be a "short version" of the key in the smaller base, similar to how programmers use hexadecimal.
My Javascript implementation of the Autonomous cipher is part of the blog layout, down at the bottom on the right. In the page source, search for 'autonomouscipher' (one word). If you use it or download it I am not responsible for any awful consequences. I don't advise you to use it for encrypting anything important or unimportant. Legal disclaimer, etc. It's purely for entertainment, educational, and/or edutainment purposes.