Friday, December 31, 2010

hidden conceptual connectors

The least controversial statement possible about the brain's functioning is that it's enigmatic on large scales. Despite knowledge of input and output in any given situation, the complex path in-between can't be interpolated. It's a fuzzy tangle of inherent unpredictability as it continually adapts with each new environmental shift in the flood of external information. Small wonder that some scientists have insisted instead on restricting analysis to the most predictable of tangible, measurable behaviors.

But a statement that contradicts this one is also uncontroversial: from a first-person perspective, the brain's work isn't quite as enigmatic. It yields ephemeral subjective/mental/semantic "sensations" under the catch-all name, the conceptual stream of consciousness. Examination of the stream leads to psychological speculation about its supposed causes, e.g. the dream proves that you have a death-wish. Extraction and conversion of the stream to the form of an ordered chain of logical reasoning is a vital step to publishing polished arguments for others to appreciate and check.

It seems to me that a persistent frailty of these post-hoc explanations of the first-person experience is omission of essential steps. Sometimes it's crystal-clear that something must be missing from the person's fragmentary or abbreviated account. Sometimes the person may not have realized that they skipped a step in the telling; when questioned they say it was too "obvious" to be noticed. Sometimes one or more steps are purposely excised to avert dull boredom of the audience. Hence, although the underlying physical motions in the brain that produced the output are undeniably inscrutable, the perceived conceptual motions in the first-person consciousness or "mind" are also filled with gaps! Pure reason, the foremost ally of philosophers, is reliant on hidden connectors between the manifest thoughts.
  • Inventive parts of my brain must be whirring away without my express order or consent. How else can I explain the noteworthy clue that my consciousness can play its own soundtrack in the absence of corresponding sound waves? The first guess is that the music filling the interstices of my attention is randomized firings, but the selection is too often apropos to be due to coincidence. The meaning of the lyrics aligns with my focus. More than a few times, I had an anticipatory song. I read toward the top of the page, heard a song in the "mind's ears", and further down I read a textual header whose content matched up more or less directly to the already-in-progress song (most obviously when the header's a pun on well-known song lyrics). As my eyes searched the page as I read, my brain subliminally picked up the text of the below header in passing and that snippet then seeded a highly meaningful background tune. I mention the "smart jukebox in the head" as a prosaic illustration of the currents which are active yet not consciously initiated.
  • The next indication is visual. Abstract topics under extended consideration by me may become entangled in a particular unrelated location from my memories. When I return to the topic, a view (always from the same approximate "orientation") of the associated location "shows up". But unlike the inner jukebox music, the choice of stage has no connection to the topic; it's "just there" as the main sequence of my thought is engrossed in the topic. My theory is that when I'm deeply sunk ("transported") into the topic, my usual contextual awareness of my actual location loses force and allows an arbitrary remembered location to emerge. As I ponder the topic, it intertwines with the secondary setting, and from then on the two are joined. Like the apt background music, this occurrence isn't perceivably helpful to the main process, but it suggests that more is happening than the stream of consciousness (would we call the rest of the activity "brooks"?). 
  • By contrast, heuristics can be wonderful aids. A few good mental shortcuts reduce the time and effort to obtain an answer, as long as the error rate is tolerable. What's striking is the frequency at which heuristics develop without deliberation. When a person performs repetitious tasks, generalizations crystallize. If a computer program always asks for confirmation and "Yes" is always clicked in response, there's no need to read the words every time thereafter. Sooner or later users may find it so routine and mindless that they barely can recall the clicking, either. Long exposure and training build an intricate web of heuristic reactions. People who perform identical tasks at their jobs for years appear "dead" to onlookers but they're really more like robots. They do A for case X, B for case Y, C for case Z. Eventually, solely the most exceptional cases manage to evade the heuristics. "In all my years working here, I've never seen..." The ability to leap instantly to the correct course of action isn't as remarkable when the solver is hastily reapplying a hidden, practiced heuristic.
  • While sneaky street-smart heuristics receive less credit than forthright book-smart education, the latter depends on unrevealed links too. Verbal learning happens as teachers lay new definitions upon previous definitions. As they become better acquainted with the new ideas, the learners likely won't need to refer to the various mediators that first facilitated their initial understanding. After the information is embedded in the learners, the comparative "glue" employed by the teachers during the lessons is unnecessary. Still, the educated person must acknowledge that these clumsy intermediaries were formerly invaluable for achieving minimal comprehension. The teacher's formula "___ is like a ___" was a bridge that could've been forgotten afterward. Thinking of atoms or other particles as microscopic balls or points is deficient to the reality in several respects, but the image is close enough for an introduction. 
  • A skeptic could cast doubt on the necessity of the stepping stones laid during learning. Couldn't the instructor plainly state the base facts rather than fumbling with imperfect comparisons? Perhaps, but ultimately it's unavoidable because all communication is the learning process in miniature. Each word shapes the interpretation of its successors. The importance of context shouldn't be underestimated. When someone proclaims a love for "bouncing", the information intended by the proclamation is unlearnt until the arrival of more details. If according to grammar the object of the bouncing is "pogo stick", then the listener's uncertainty decreases, provided he or she knows about pogo sticks. Similar illumination would happen were the object of the bouncing "rubber ball", "check", "unwelcome guests". The point is that the word "bounce" is isomorphic to multiple stuff, a word like "check" that constrains "bounce" could also be isomorphic to multiple stuff, and the final overall sentence isomorphism is an intersection or conjunction of the possible/candidate isomorphisms for the individual words. It's a fundamentally parallel process that harnesses the ambiguity of isomorphisms to permit efficient and creative messages; word choices need not be exact, and continual recombinations use limited word sets to cover an astronomical range of thoughts real and unreal.
  • The sheer power of language for logical reasoning, fused to the uncanny human capacity and motivation for inventing explanations, can give the misleading impression that the form of nonverbal reasoning is no more than unverbalized logic. However, countless counterexamples are everywhere. A familiar category is visual estimation and evaluation such as whether a floor lamp can fit inside a box. Skill at such questions is independent of skill at logical/mathematical questions. Moreover, translating a question into a different format, viz. visualizations, can greatly affect its perceived difficulty. Solutions can arise through subtle analogies. Many anecdotes show a startling similarity in the structure of major breakthroughs: the discoverers were confounded until they adjusted their underlying approach or otherwise became inspired by knowledge from seemingly unrelated domains. In the more mysterious accounts, they arrived at the answer partly by interpreting material from dreams or dream-like meditative states in which frothy free association ended up mentally attaching and attacking the problem in the forefront. Metaphors may be simultaneously ridiculous and indispensable. Asking "what if?" might lead to a fruitless destination. On the other hand, it might lead to the key insight. A nonsensical whole can act as a temporary frame between important contradictions before the thinker determines the plausible interconnection.
  • At another level of remove, seasoned experts of a specific mental domain describe ideas about the ideas and assumptions about the assumptions. This "feel" is largely what qualifies the expert as an expert. A novice would pursue tempting and irrelevant side details or surface illusions where an expert would employ the well-honed knack for striking at the most beneficial edge. They speak of a "nose" for the right path or a "sense" for the likeliest "shape" of the right conclusion. For them the domain approximates a familiar environment in which even abstract concepts are almost like manipulable objects. Upon prompting they try to describe the domain's "aesthetics", which probably involve symmetry and complementarity. Their patterns of analysis enable them to quickly decompose a challenge and make it appear easy by a lack of wrong turns. Once again the end result doesn't fully expose the hidden concepts that somehow participated in its birth.
  • Lastly, and most mystifying of all, are the opaque reports. "I don't know." No matter how they are pestered, they claim to be oblivious to the origin of their highly-confident outcome. In short they know but not how. Naturally, they could actually be wrong, but for a time at least they express certainty. "It just is." No work is remembered. No argument left memorable traces. It's as if verification is possible but not reverse-engineering. There's no clearer proof for the uninformed nature of our reckoning of our own reckonings.

Saturday, December 18, 2010

bow to the gitextensions cow

Recently I tried out gitextensions. A rhapsodic blog post seems to be in order.

There's an installer that includes msysgit and kdiff3. This means I haven't needed to download anything else to get started. The installer asked, up-front, how to handle the *nix/Windows line-ending issue and what to use for my name and email address. The GUI contains an easy way to edit .gitignore entries and it comes with default entries that are relevant to almost all Visual Studio development. It suggests and directly supports integration with the PuTTY tools for SSH authentication. This means I haven't needed to find and edit configuration files or go online to research recommended entries. As someone who considers himself to be at least minimally competent, I'm not phobic of manual configuration or command line usage, but why shouldn't the easy and predictable modifications be even easier?

My intense appreciation continued as I started using it. All the typical functions and their typical options are available. (Long-time git users doubtless prefer to perform the same tasks by rapid rote typing; there's an icon to pop open a "git bash" at any time, which is good to keep in mind.) Creating a branch is just a matter of entering a name when prompted, with a checkbox if you want to also immediately check it out.

The view includes the annotated history graph, the current working directory, and the current branch. Clicking on the branch name brings up a drop-down list of other branches. Choose one, and you check it out. Clicking on a commit in the graph brings up information about it in the bottom part of the screen, such as full commit details and the diff and the file hierarchy (each directory expandable and each file right-button-clickable for file-level commands like individual history). Clicking one commit then CTRL-clicking a second brings up the diff below.

Remember how git newbs tend to have trouble navigating the movements of files between the index and the working directory, especially before git became more friendly and talky? In gitextensions, the commit window simply has separate panes with buttons to move added/modified/deleted files in-between. There's also a button for amending. After the commit, or any other moderately-complicated operations, the git output pops up in a window for review.

Of course, pull, push, merge, rebase, cherry-pick, branch deletion are present, too. All are fairly straightforward assuming the user can follow the on-screen instructions and isn't completely ignorant about git. gitextensions has a manual that contains abundant screen captures, yet I imagine it's more useful as a reference for figuring out where/how in the GUI to accomplish a specific task than as a tutorial. I was pleasantly surprised by the smoothness of my first series of gitextensions conflict resolutions. kdiff3 came up, I chose the chunks and saved, then I clicked a continue button. Despite my later realization that I could've accomplished my goal through a more streamlined procedure, the end result was nevertheless perfect in the sense that I didn't need to apply a "fix-it" commit afterward (the credit likely should be split among git and kdiff3 and gitextensions).

My praise keeps going. gitextensions offers fine interfaces for "gc" and "recover lost objects", although thus far I haven't strictly needed either in my short usage span. It adds right-click items to the Windows file explorer. It adds both a toolbar and a menu to Visual Studio. If it isn't obvious, my personal preference is to keep the gitextensions GUI open all the time, supplemented by git-bash. On occasion, when I'm otherwise manipulating a file in explorer, I might invoke file operations right from there.

The remaining question is: are gitextension upgrades frictionless? Sooner or later the cow will tire of wearing that Santa hat...

Postlude: Farewell, Mercurial

Uh, this is uncomfortable. I'm sure you've heard this before, but it's not you, it's me. The cause definitely isn't something awful you did. You're still a great VCS that could make other developers very, very happy. I'm just looking for something else. My horizons have broadened a bit since we first met, and we don't think as alike as we did then. There are other options and considerations that I need to take into account. If I stuck with you forever, I worry that I'd become regretful or resentful. Some day, as we both change over time, I may come back to visit. Until then, I genuinely wish you well.

Sunday, December 05, 2010

impossible flawless imitation

The inherent limitations to the analysis of a Turing Machine (TM) include a consequence that deserves more attention. A TM cannot be imitated perfectly through a generalized algorithm. Stated differently, a TM's full "description" cannot be inferred from an arbitrary amount of its output ("tape"). No empirical method can be all-sufficient for determining the exact source TM. You can't deduce a TM by its "footprints".

The reasoning is similar to other TM arguments, e.g. the inability to compute when/if a TM will halt. Suppose we know that some TM "S" produces a specific sequence of finite bits, and thereby we contrive our own TM "I" that produces the same sequence. Well, what about the next bit after that sequence (or the next erasure, etc.)? "I", according to its present configuration, produces either a 1 or 0. But given that the output is only assured of matching up to the last bit, there's no theoretical reason why "S" couldn't be identical to "I" in every way except for the configuration for the next bit, in which case our otherwise flawless imitation is ruined. For example, even if "S" has steadily produced alternating bits, it still might produce "00" at position 30, or 35, or 1,462.

Moreover, the situation could be quite possibly worse in a multitude of creative ways. Perhaps "S" is really a complex specimen of Universal Turing Machine with two "source" TM that are much different (analogous to a "piece-wise" mathematical function). "S" executes source TM "A" and then increments a count. When the count exceeds a predetermined value, "S" branches to a configuration that executes the other source TM "B". One may elaborate on "S" with further counts, branching, and source TMs. The point is to reiterate that although we can invent an "I" to imitate "S", we can never conclude that "I" and "S" are exact matches, and in fact we can't figure the ultimate similarity at all as execution stretches to infinity. Failure of "I" is due to missing information about the "S" TM, but worse is the more subtle problem: there's no way to know how much information is missing at any time!

So...
  • Generally speaking, clean-room or black-box re-implementations of software can't be guaranteed to succeed in every detail. For instance, exceptional conditions or highly specific combinations of input could trigger diverging outcomes. The imitation of the previous software's bugs can be particularly troublesome.
  • Whenever we compute/emulate anything that requires TM-level processing, we can't be absolutely sure whether we ever achieve total fidelity. This doesn't imply that a conceptual TM is the most convenient theoretical model of a phenomenon (it usually isn't!), but merely that the chosen model can somehow run on a "computer". Or in more formal terms, the model outlines an explicit procedure that yields predictions of one or more modeled quantities. In some open-and-shut cases, the model's error is persistently trivial under all experiments and we therefore have no justification for doubting its effectiveness. Yet history is filled with times in which a serviceable first model (circular planetary orbits with epicycles) is later replaced by one with still greater nuance and realism (elliptical).
  • Third-person human brains (and behavior) undoubtedly fall under these conclusions. That is, any verbalized or intuitive model of a specific brain cannot be completely understood. Sympathy has its limits. While careful observation combined with genuine insight yields an impressive array of knowledge about "what makes his or her brain tick", especially among species evolved to be highly social, the application of the knowledge often proves how superficial it truly is. Biographers can detect what they call formative experiences, but tasking the biographer to reliably predict what a living subject shall do next will illustrate that the analysis works best retroactively. Of course, if someone theorizes that the human brain performs computations beyond the power of a TM, then the argument is correspondingly strengthened. Two such brains surely cannot be synchronized when two lowly TMs cannot. The proposed mechanism of brain-based super-computation, e.g. quantum effects of exotic pieces/states of matter, would increase not decrease the difficulty. 
  • More surprisingly, first-person human brains (and behavior) are also prone. There's no "imitation" of oneself, but there's a self-concept. The very process of answering the self-evident question, "Why did I think or do that?", necessitates the mental model of self. The brain attempts to develop a "TM" whose workings imitate the actual and mysterious workings of itself. Unfortunately, it's sabotaged by bias toward a pleasing answer! Transparency and self-knowledge are notoriously plagued by mistakes. Countless fictional works have relied on the humor if not the tragedy of self-delusion.

Thursday, December 02, 2010

escaping infinity

I've previously mentioned a chain of reasoning similar to this: 1) everything that is real is material, i.e. subject to the usual scientific "laws"; 2) whenever a substance "means" another substance, that relationship is an isomorphism recognized/superimposed by a material brain; 3) there is no ultimate theoretical barrier to the possibility of inventing a sufficiently advanced computer/program that could process "meaningful content" (i.e. isomorphisms) as well as a brain.

Many varying objections apply. One category pertains to an insinuated reduction and comparison of exceedingly-complicated consciousness to the current generation of inflexible, literal-minded computers and programs. And although it's likelier than not that the computers and programs that would successfully imitate the brain would be laid-out much differently than ours, these objections still make some valid points: a computer with more or faster components is "merely" another computer and a program with rather chaotic and evolutionary qualities, broken into segments running in parallel, is "merely" another program.

For instance, assume (if you're an objector, purely for argument's sake) that in principle a brain could be adequately simulated by the appropriate computer and program named Q. Brains, at least after suitable training, can simulate Turing Machines, so Q can also. We know that a sufficiently clever/defective program can end up running for infinite time (nonterminating loops) regardless of how "smart" its host is. Despite its sophistication, Q remains susceptible to this threat. But if Q is susceptible, then by assumption the brain that it adequately simulates is susceptible. How absurd! Everyone knows that people don't suffer from infinite thought repetitions. Thus, the original assumption has led to a false conclusion and there must not be a Q.

My list of responses fit a standard akin to natural selection. The brain's, and by extension Q's, safeguards against infinity aren't perfect (that's impossible), but are good enough.
  • Distraction. Brains are exquisitely tuned to react to change. Whether an alarming signal arrives through the senses or a long-gestating answer to a dilemma bursts into flower, the typical course of thought is continuous transformation in unanticipated directions. In the face of the avalanche, perhaps the better question is how there can ever be a mental impression of a unitary and uninterrupted flow of logic. Healthy and normal brains display a moderate weakness, if not desire, for distraction. Meditation is difficult. For some, stopping their feet from tapping and their knees from bouncing is difficult!
  • Memoization. The importance of context shouldn't be underestimated. It's fueled by short-term memory, the "scratchpad", which contains a temporary record of recent brain work. Moreover, since retrieval strengthens a memory, any cycle in the brain work will tend to self-promote. Hence the brain is primed to store previous trips through the loop. The other ingredient is pattern-matching. Each trip, something in the end leads back directly to the start. It's not much of a leap for the brain to construct an isomorphism among these remembered trips, thereby detecting the similarity and extracting the common pieces. Finally, these pieces allow for a short-circuit or "unrolling" of the loop because the brain now knows that the start always leads eventually to the start once more. There's no more need to carry out the (infinite) loop; staying at the start has the same ultimate effect. The execution of the loop has been optimized out of existence or "memoized". Clearly, memoization works best for short or easily-generalized loops. Lengthy or subtle loops could evade notice perhaps indefinitely. Consider cases of asymptotic progress, in which someone is fooled into the belief that the destination is reachable because it grows closer.
  • Simulation. The power of simulation or imagination allows a brain to contend instead with a comparatively toothless and "imaginary" version of the infinite loop. At all times, the brain can maintain its metaphorical distance, its suspension of disbelief. Through simulation, the loop is closer to an object under manipulation than a dictator reciting irresistible commands. The loop can be stopped, resumed, replayed backward, etc. The brain halts momentarily after every operation to review and reconsider the result, so the danger of runaway computation is nonexistent. If the whole enterprise is carried out with tools such as writing implements or another suitable device (e.g. smart phone?), then the halt is accomplished by nothing more intricate than resting hands. In short, the vital difference in simulation is that the brain may treat the loop "code" as data. Strictly speaking, it never really switches between modes of perception and simulation. It perceives and simulates and perceives and simulates. Dynamic feedback is built-in.
  • Ruination. Undeniably, real instances of infinite loops don't run forever on devices. Assuming the loop accomplishes something cumulative, its effects are likely to start causing noticeable trouble, even to the point of a forced halt when memory or disk is full. Alternatively, the physical parts of the system will quit functioning eventually since that is what continuous wear and tear does. Earthy considerations also affect brains. Moreover, brains are notoriously prone to failure. Fatigue, hunger, boredom, illness, etc. will cause a loop-stuck brain to produce mistakes in processing the loop, and the mistakes could incorrectly terminate it (e.g. your ComSci classmates who played Halo all night fail to find the right outcome during a "paper trace" question on the final exam). Once again, the greater the complexity of the loop, the more this factor kicks in. As a system shaped by evolutionary forces, a brain is better at survival than flawless mental calculation. Robust error recovery and correction is a more realistic strategy.
It may appear silly to ponder why biological beings don't end up in infinite algorithmic loops. However, infinite behavioral loops surely aren't nearly as ridiculous. We usually call them habits.

Sunday, November 07, 2010

shared state is not hard to find in Javascript

...and it's called "DOM" (the webpage Document Object Model). State is shared between pieces of code whenever all the pieces may read and write to it, and that applies to the DOM. No declarations are necessary; neither reads nor writes require express permission or coordination. In this freewheeling land known as the "document", anything is up for grabs.

Yet why does it matter, given that dynamic changes to the DOM are a huge part of Javascript's appeal? It matters because any shared state, by its nature, can lead to problems for the unwary, especially as size and complexity grow.
  • If various Javascript functions touch the DOM, then those functions may cease working properly whenever the page changes structure. One change, no matter how trivial, has the potential to mess up code in several places at once. For instance, say that two tabs switch order...
  • Similarly, whenever there's an algorithmic adjustment, all the functions that affect relevant individual parts of the DOM must be spotted and redone. Rounding and displaying four decimal places, but only in inputs for numerical data of a particular category, means code changes for everywhere that those input values are set or read.
  • My impression from some blogs is that people are feeling skeptical about the actual prospect of code reuse in many circumstances, but it's still a worthy ideal. As always shared state is a hindrance to reusability simply because it isn't parameterized. A function that includes an instruction to remove a style class from the element of id "last_name" is pretty difficult to reuse elsewhere.
  • On the other hand, shared state opens up the possibility of no-hassle collaboration. Function A (assuming a better name!) can take on the responsibility of setting up the shared state in some way. Then function B can do some other task to the shared state, any time after A has recently run. But function C can run directly after either A or B. So function A must leave the shared state in acceptable configurations for either B or C, and B must also account for C. Of course, if there's a new special data value in the shared state that affects what C must do, one must be careful to modify both A and B to set it accordingly. Hence, although shared state makes it highly convenient to intertwine the operation of many pieces of code, the intertwining also greatly reduces readability! It's much easier to analyze separate pieces of code with well-defined connection points.
  • Furthermore, implicit shared-state dependencies don't combine well with asynchronous execution. Javascript doesn't have concurrent execution (i.e. multithreading), but it's certainly possible for separate pieces of code, such as callbacks for clicks and timers and network requests, to execute in an unpredictable order. So while there won't be deadlocks in the traditional sense, unwitting callbacks that fire in an unintended sequence could leave the DOM in a useless or false form after the dust clears. 
Many techniques could apply to mitigation of the shared state known as DOM. For information storage, rely on variables residing in purposeful scopes rather than DOM elements and/or attributes. Treat the DOM as an end, instead of intermediate, data format. Isolate the code that handles the DOM from the code that processes data. DOM modifications that always happen together should be collected into a single function with an appropriate abstract/semantic name.

Not all of the shared state in an application consists of variables. Whether log file, database, or DOM, access logic should be carefully considered to avoid maintenance headaches.

Monday, November 01, 2010

a bad statistics joke

Since I recently posted something with a statistical lean...

I recently encountered the statement: "Why be normal? No one else is."

And all I could think in response was: "Why be within one standard deviation of the mean? Only 68% of the population is, assuming it to be normal."

Saturday, October 30, 2010

statistical inference and racism

Up-front disclaimer: None of the following should be construed as supporting racism. If anything, my point is that racism is as shaky intellectually as it is morally. It's simply wrong to unfairly treat an individual based on indirect inferences from their similarities to a particular race (or ethnicity, gender, religion, etc.). I also apologize up-front for offending statisticians.

It occurred to me, as it doubtless has to others, that racism has some connections to faulty statistical inferences. Of course, I don't mean that the typical racist is computing actual statistical regressions, but that part of their error consists of an "intuitive" misunderstanding that would appear unsound if it was quantified and written out. And naturally, the inability to estimate correct conclusions about probability and statistics is present in any untrained person, not just the racist one.

the instrumental variable

I've heard people aver that, since racial discrimination has continued to decrease, race should no longer be considered a determining factor in people's lives. While this is a happy thought, it naively misses a crucial aspect: (direct) discrimination is not the only way that racism acts.

Assume a statistical model or equation in which race and discrimination aren't present. It could be for any personal outcome like educational attainment, as long as race isn't included in the model's variables. Essentially, the mathematical equation expresses a complete lack of obvious relevance and causation between race and the outcome. Discrimination doesn't even enter the equation, as we posited.

Now run a thought experiment on our model. Suppose we apply the model to randomly selected people, i.e. we grab the info for each person and then calculate (not measure) the outcome with the model. Further suppose that we also record each person's race. Then we find, as in reality, that when we partition the calculated outcomes by race, the distribution is drastically and consistently different. How can this be? Race wasn't one of our variables, because without outright discrimination it had no mechanism of causation.

The answer is that, in this hypothetical model and thought experiment, race could still act as an instrumental variable. Roughly speaking, race is correlated with the outcome through the model variables. For instance, in a model of personal income, it's highly reasonable to have the parents' income as a variable. Although the model may leave out "race", race can nevertheless affect personal income (the outcome) as long as it affects the parents' income (the modeled variables). Thus race can be a cause of an outcome despite not being a "cause".

Moreover, race is likely to be an instrumental variable for an array of models, and all the models are likely to be related too. Bad (or missing) education tends to lead to low-paying employment or unemployment, which tends to lead to poverty, which tends to lead to crime, which tends to lead to low property values and taxes, which tends to lead to low school funding, which tends to lead to bad education. Regardless of whether there's a blatant racial barrier presently at work, the long-lasting "fingerprints" of racial differences may be active (e.g., systematic segregation sometime in the past that restricted access to the same set of opportunities).

the overeager Bayesian

Stereotypes are simplifications by nature. Sometimes a stereotype can be a helpful mental shortcut, but often it doesn't reflect the most accurate concept of a "typical" sample from a population. This is notoriously true of racial stereotypes that highlight the worst representatives.

For compared to a proper Bayesian analysis, "reasoning" by a stereotype is overeager to apply shaky conditional probabilities. Consider a very hypothetical population with only two races that are each 50% of the population. 10% of the entire population has an undesirable characteristic (left unnamed). Of the people in the entire population who have the undesirable characteristic, 75% are of race A. Whenever someone in this population is of race A, what is the probability of having the undesirable characteristic?

When it comes to a stereotype for the undesirable characteristic, chances are the stereotype is of race A due to prevalence within that subgroup. So the stereotype would tend to override the right Bayesian answer, 7.5%, with a much greater probability that overlooks the facts that 1) only 10% of the entire population has the undesirable characteristic at all and 2) therefore 85% of race A does not have it.

Friday, October 29, 2010

peeve no. 261 is Obi and programming language mentality

The Force can have a strong influence on the weak-minded. (--Obi-Wan)

Similarly, a programming language can have a strong influence on weak-minded programmers. If your programming language "changed your life", "made you a better coder", "expanded your mind", etc., then I suggest that you may be going about this programming thing the wrong way. If a lack of language support for OO results in you producing a tangled web of dependencies among procedural functions, or easy access to global/file scope results in you spraying program state everywhere, then the programming language is your crutch. It's the guide-rail for your brain.

I'm glad that you found a programming language that you enjoy, but consider me unimpressed by rather impractical claims about the ways that it alters your consciousness.

Wednesday, September 29, 2010

neuronal pragmatists

Two ways in which neurons act like fans of pragmatist philosophy:
  • In the pragmatist viewpoint (interpreted by me), the significance of knowledge is closely tied to its value in practice, assuming highly elastic definitions of "value" and "practice". Similarly, the strength and variety of connections between neurons are closely tied to the impact of neurotransmitters. And the release of neurotransmitters is clearly correlated with emotions, broadly categorized as "pleasure" or "pain", and emotions in turn are a large part of subjective value. Research has also shown that once this link is forged between knowledge and emotions, mere anticipation and contemplation can trigger the experience, albeit at a less vivid level. Hence, anatomy and physiology support the pragmatic notion that creatures naturally evaluate knowledge based on its effects. There's a feedback loop between knowledge and its short-term and long-term results. Of course, the mechanism can operate independently of high-level abstract insights about danger or desirability, like in cases of phobia and mental trauma. Or when someone has difficulty memorizing knowledge that's important yet provokes no reaction in the person besides indifference.
  • More mysteriously, the judgment of knowledge commonly has a nebulous and inarticulate component of "reasonableness" that's integral to pragmatic truth but neglected by other philosophies. When this component is verbalized, people describe it as intuition, the "ring of truth", "feeling right", "clicking into place", "harmony", "seeing the whole picture", "jumping levels of meaning", maybe even related to the "oceanic feeling". (Software developers probably refer to it when they babble on and on about "beauty" and "elegance" in their computer code. Mathematicians say that they felt "guided" and "sensed the right track" to a solution.) My speculation is that this brain sensation corresponds to instances of mental economy. In other words, one "item" of knowledge connects up to another item of knowledge to form some kind of unity, even if it takes the form of opposites on a continuum. Data or concepts were in chaos, but now are in order. Exceptions turn into new, subtler rules. Furthermore, I speculate that this mental economy corresponds in turn to synchronization of the firing neurons that store those items. It's well-known nowadays that the brain is filled with activity in most of its states, and that the unconscious/subconscious/non-conscious takes up a rather startling percentage of that activity. Thus it's noteworthy whenever there's an unusual amount of synchronization of neurons in consciousness (does this help explain the human enjoyment of music?). Any knowledge that acts as a bridge or otherwise "performs double duty" causes a synchronization and a new network-of-networks of the participating neurons. So when we speak of resonant ideas, the description approaches literal truth, for the ideas' neuron firings resonate. Perhaps this is a feasible physical hypothesis for what happens when pragmatists claim that people value an idea based in part on its "reasonableness".

Saturday, August 07, 2010

working truth

Whether or not my overall standpoint qualifies as bona fide pragmatic philosophy, I appreciate and appropriate some of its postulates . Unfortunately, one in particular has attracted both informed and ignorant criticism. Expressed in its shortest and most recognizable form: "the truth is what works." If this doesn't mean "anything that can make me rich must be right", as some allege, then what does it mean?

As I understand it, the intended meaning of "the truth is what works" is simply that the bestowal of the label "true" is the product of physical or mental action. True is reached, earned, assigned, verified, measured. People set criteria, and then they employ those criteria to divide true from false. True (or false) is a conclusion.

And the vagueness of this definition of truth is intentional, in order to cover an unbounded expanse. The operation of determining when something "works" is tightly coupled to the object and the goal, each of which could vary. What also varies is the threshold for delineating "does work" and "doesn't work" in the operation's results.

Clearly, some cases are self-evident. For the proposition "The battery is missing", the object is the battery, the goal is to confirm that it's missing (but that goal is probably a sub-goal to something larger like "force my camera to start responding again when I press the power button"), and the operation is removing the cover to the battery chamber to look inside. The threshold of the operation is seeing the uncovered battery chamber with or without batteries; "The battery is missing" works and is therefore true on the condition that one observes a chamber without batteries.

But other cases are more difficult, like the proposition "Person X is trustworthy." Eyeballs aren't sufficient to detect trustworthiness, the object. Which operation and how stringent its threshold depend greatly on the importance of the goal. Is Person X a candidate to house-sit for me, or just someone whom I'm asking for directions to the nearest location of engine fuel? Someone will likely use not only their senses but their reason, creativity, emotion, intuition, and so on, in conscious and subconscious double-checks upon double-checks.

No matter what they tell you, people frequently "feel" the truth, although it may act as a starting point rather than the entirety of the procedure. Such factors illuminate the muddiness of separating objective and subjective truth in many routine instances. Thus all the ways in which a truth "works" form a unity, fused together by the person, within the context of the person's other truths, whether those be sensations or reasonings or any variety of "mind-stuff".

However, these personalized unities-in-contexts don't imply solipsism or relativism, at least not in crude terms, because of two manifest and natural meta-truths that are normally left unstated. 1) It works to assume that some contents of experience have independent (albeit malleable) existence apart from one's thoughts. When someone writes a reminder, he or she is explicitly relying on the reminder to not be dependent on, or as flighty as, human memory. 2) It works to assume that, based on communications and empathy, other people interact with the same thought-independent contents of experience. To take an early example, a group hunting a mammoth could hardly coordinate the attack without this working assumption.

Put together, what one could call "the two meta-truths of objectivity" outline a common area of endeavor for people, a ground for discussion. And the recognition of its predominance characterizes a mature perspective. A person's uniqueness lies in the combination of "ingredients" that are individually unremarkable and pervasive. Other people have lived on the same planet and nation and culture and city, have experienced similar personalities and feelings and upbringing and education and traumas, and according to an evolutionary/genetic analysis are not much different (though the differences can have vital implications in society and health). Hence, the humble person, aware of his of her startling non-uniqueness, will exploit all the many applicable truths found and transmitted by other people, after winnowing out irrelevancies and mistakes. Of course, the secondhand truths are subject to the rule: do these reports work, evaluated by whichever mental/physical operations? If direct verification isn't possible, as it often isn't, then the operation of trust is unavoidable.

Trust is doubly (or triply) necessary for truths that are universal. By definition, a universal truth is intended to hold true not solely for example Q or group X but for all actual and hypothetical N that meet the truth's conditions. Universal truths are special in that no (finite) number of confirmations is enough. To invoke a universal truth in the execution of an activity is to engage in a pre-sumptive, pre-emptive "confirmation" of it. For universal truths aren't exceptions: truthfulness isn't inherent but confirmed time after time. In fact, each one owes its very existence to how well it works in acting as a tool or shortcut in summarizing, predicting, etc. in the midst of the unities-within-contexts previously explained.

Admittedly, the confirmation can be a perfectly unsurprising formality, especially for inarguable formulas or other patterns undergirded by rigorous "airtight" experiment/proof. The proof's effect is to demonstrate impersonally that the end is substitutable for the start, without doubts or gaps between. Afterward, nobody needs to rework or review the "guts" of the proof to use it, as long as a person trusts the correctness of the proof like any other secondhand truth. (To be sure, people could and do rework or review proofs for a wide range of valid reasons.)

On the other hand, there is a category of universal truths whose correctness isn't quite provable but elicits strong commitment regardless: ideals. An ideal is zealous desire embodied by a universal idea. It "ought to be". In some sense, it's a fiction made real via its connection to a person's irresistible sentiments. It could be elaborate or fragmentary. It could be solitary or part of a complex. It could be structured or incoherent. In any case, the ideal's impact on behavior is precisely how it works; that's its level of truth. People who pursue an ideal are witnesses to it.

Therefore people who ignore an ideal thereby mark its truth as flawed or incomplete. But they could nevertheless use the ideal by feigning their devotion. Even ideals claimed by no one to be "true" might still work as interesting fodder for conversation or as imagined adversaries to rally around. Just as ideals come in varieties, so do reactions to ideals. Objections to "the truth is what works" might be objections to uncreative or minimalist definitions of works. I don't think it's always synonymous with "profitable". If people seek to "maximize a payoff", then realistically speaking the payoff, options, and factors must be churning like a turbulent flow in endlessly intricate directions (e.g. affected by psychological priming). Part of the reason for the unpredictability is that ideals may conflict and the ideal that "wins" in one situation can "lose" in another. Altruism and self-denial are unprofitable, but all that's necessary to uncover the "mysterious" rationale is to ask the participant which ideals were the motivation. A lack of obvious profit surely doesn't prevent the ideals from working for the participant in some way.

Truths are real, not ethereal. And a truth is known by its real consequences on real people who perform real actions.

Tuesday, July 27, 2010

I am a Cylon, and candid opinions thereof

I'm aware that I'm not a Cylon. Cylons are fictional, first of all.

But it's an excellent metaphor for how I felt when it began to dawn on me that, despite the deeply religious tenets that I formerly cultivated throughout my life, I underwent a "gradual intellectual anti-conversion" which culminated several (less than five) years ago. "Intellectual" because emotion didn't participate in the process; there was no instance of shaking my fist at clouds and also no desperate clinging to my fading assurances. "Anti-conversion" because I view the change as a reversal/erasure of specific beliefs rather than switching to a new "side" as such. Like the fictional characters who lived as humans only to arrive at the breathtaking realization that they were unmistakable examples of the hated enemy Cylons, I discovered to my surprise that my assumptions had changed sufficiently that the most accurate label for me now is "atheist".

It was "gradual" because while it was happening I sometimes noticed the mental conflicts, but I couldn't recall a definitive moment of selection or rejection. There was no single choice, but at the end there was a belated acknowledgment that some of my beliefs had simply deposed some others in the manner of a silent coup. In fact, further mental struggle had long ago become pointless since one side now occupied all the "territory". Further battles could only be as "asymmetric" as guerrilla warfare.

Before the point of mental consolidation, I considered myself "religious with doubts and reservations". My thinking seemed unsteady but regardless I continued to act the same. After the crystallization point, the prior motivations and habits were present yet ceased to exert authority; I saw through. I could select my level of compliance without compulsion, like adjusting the volume on the playback of a speech. The majestic and imposing scenery shrunk down to matte background paintings. However, my new-found godlessness didn't lead to unrestrained lawlessness or carelessness (just as not all Cylons who lived as humans chose immediately to quit behaving like humans). I suppose that as an immigrant to atheism, by default I naturally cherish the customs of the land of my previous patriotism.

Actually, this phenomenon was another jolt to my perspective. The religious are accustomed to categories such as 1) young people who rebel passionately by doing exactly what is forbidden, perhaps when they start higher education at a faraway location, 2) wishy-washy attendees who "backslide" progressively worse in their actions until they can't bear to even pretend to be devotees any longer, 3) people who deny the existence of any god and therefore act as chaotic and selfish manipulators, iconoclasts, and nihilists (I can very well envision someone commenting "Say what you like about paganism, at least it's an ethos"). My gradual intellectual anti-conversion doesn't fit into this tidy taxonomy.

For regardless of my unbelief, I'm surely not one of the bogeymen in the scary third category. Sure, some of my politics and causes are different now, but I'm not aiming to overthrow the right to religious expression and culture, presuming there's no entanglement among important social institutions that should be neutral toward such beliefs. I'm also not against religious evangelism and conversions as long as there's no abusive coercion or exploitation. Frankly, my interest in which religion dominates dropped tremendously when I self-identified as atheist, for whom afterlife and divine judgment are nonexistent consequences of incorrectness. I don't even care about convincing other people of my atheistic point of view, as much as I care that atheists not be stereotyped or persecuted in the larger society.

Furthermore, my present sentiments regarding religion go beyond a lack of competitive zeal against it. I have a lingering appreciation for it. Although not all effects of religion are good, to say the least, I know many people for whom religion is an essential pillar of the mindsets that calm their psyches and motivate them to accomplish amazing progress in themselves and their surroundings. And I think it's baldly inaccurate to accuse the religious of widespread stupidity or weakness. Religion can at times demand either courage or intelligence. Besides, evangelistic atheists should keep in mind that shaming people into following your example is not a highly effective technique anyway, especially if the goal is willing long-term commitment. That tactic certainly played no part in convincing me.

Moreover, these conceded emotional comforts of religion tempt me as well, whenever I personally contemplate death. Given that life is an exceptional configuration of matter and not a separate substance that "inhabits" it, death is many other configurations of that matter. My consciousness requires a blood flow of necessary materials to operate; when this flow stop working normally, my consciousness will also halt. Life's fragility is staggering. Without the promise of an afterlife independent of the vagaries of complex biological systems which can forestall entropy solely for a limited period, the inestimable value of healthy living should be obvious. Responding to death's finality by living dangerously is nonsensical. I should think it obvious that the saying "you only live once" doesn't imply "risk and/or waste the only life you have". To an atheist, "you" are your body.

But if every human is a body and no more, then the fact of the terrifying impermanence of one's own life comes with discomforting companions: every dead human, in all of his or her uniqueness, must be irretrievably gone. The precise configuration of bodily matter that constituted him or her shall never arise again. It's no more likely, and to the contrary far, far less, than the chance of a thorough shuffle of a pack of 52 playing cards producing the exact same order as before the shuffle. Of course biological reproduction perpetuates a lot of the mere genetic bases, but a perfect clone would still develop differently than the source. Indeed, without the clone retaining an identical set of lifelong memories, it couldn't fool any observers. This is why the question of "legacy", i.e. the lasting consequences "left behind" by a human's actions after death, is extremely pertinent to atheists. In a highly literal sense, someone's legacy is the only "part" that can justifiably be termed eternal (although "his" or "hers" separated particles of matter and energy are conserved, but that's scant consolation at the human scale).

I understand if people choose to question my unflinching claim that bodily death entails total death. How can I certainly pronounce that all of the deceased, whether family, friends, or martyrs, haven't moved on to an unexperienced "plane", since I clearly haven't gone through it? Without direct evidence, isn't it more fair and conciliatory to postpone discussion? Well, my first, curt reply is the suggestion that everybody else postpone discussion, too. If they aren't, then I won't. My second, earnest reply is the blunt admission that evidence, as commonly defined, isn't the solitary foundation of my thoughts about reality. Evidence has a strong tendency to be patchy and/or conflicting. Therefore judgment is indispensable, and based on my judgment of the overall array of evidence, including the pieces of evidence that appear to be absent, death is the end of humans. This statement is the honest reflection of my outlook, as opposed to something comparatively half-hearted like "due to lack of positive evidence, I don't know". I profess a materialistic universe; so there's simply no room for anything supernatural, much less the person-centered afterlife usually described. I readily affirm the incompleteness and uncertainty embedded in my knowledge, but I don't waver on the central axioms.

Odds are, the open mention of the role of judgment/selection provokes objections from two diverging groups: 1) from people who see themselves as pure empiricists, because they say that evidence always "speaks" for itself and any evidence that isn't perfectly unambiguous is invalid, 2) from people who subscribe to a particular revelation of a supporting supernatural realm, because they say that people who use individual and error-prone ability to define ultimate truth will instead substitute their own relative, subjective, and quick-changing preferences. But neither objection adequately captures a holistic and authentic viewpoint of actual human activity. Perception, interpretation, goals, experiences, and actions, etc., feed and affect one another. Personal desires and idiosyncratic mental processing are intermingled throughout mental events. No matter which concepts people use to anchor their thoughts, the coronation of those concepts is not passive but active. Regardless of what they say in debates, the pure empiricist decides how to categorize and evaluate his or her valuable evidence, and the devotee of a supernatural dogma decides how to extrapolate and apply it to the modern situations thrust upon him or her. And the confident atheist is no different...

Except during an interval in which transforming thoughts rise in power sneakily like an unfamiliar tune one can't shake off, and the final remaining task is to put it into words: I am a Cylon.

Wednesday, July 14, 2010

persistence of private by the Nucleus pattern

The encapsulation of data by an object's methods is one of the foremost goals of effective OOP. Restricting exposure of the object's private information prevents other code from accessing it. The inaccessibility ensures that the other code can't depend upon or otherwise share responsibility for the private information. Each object has a single responsibility: a sovereign private realm of information and expertise.

However, this ideal conflicts with the reality of the need to give objects persistence because most programs require data storage in some form. And the required interaction with the storage mechanism clearly isn't the responsibility of the objects that happen to correspond to the data. Yet how can the objects responsible, often known as repositories or data mappers, mediate between external storage and other objects while obeying encapsulation? How can information be both private and persistent without the object itself assuming data storage responsibility?

The "Nucleus" design pattern, very similar to an Active Record, addresses this issue. According to the pattern, a persistent object, similar to a eukaryotic cell, contains a private inner object that acts as its "nucleus". The nucleus object's responsibilities are to hold and facilitate access to the persistent data of the object. Therefore its methods likely consist of nothing more than public "getters and setters" for the data properties (and possibly other methods that merely make the getters and setters more convenient), and one of its constructors has no parameters. It's a DTO or VO. It isn't normally present outside of its containing object since it has no meaningful behavior. Since the nucleus object is private, outside objects affect it only indirectly through the execution of the containing object's set of appropriate information-encapsulating methods. The containing object essentially uses the nucleus object as its own data storage mechanism. The nucleus is the "seed" of the object that contains no more and no less than all the data necessary to exactly replicate the object.  

Naturally, this increase in complexity affects the factory object responsible for assembly. It must initialize the nucleus object, whether based on defaults in the case of a new entity, or an external query performed by the storage-handling object in the case of a continuing entity. Then it must pass the nucleus object to the containing object's constructor. Finally, it takes a pair of weak references to the containing object and nucleus object and "registers" them with the relevant stateful storage-handling object that's embedded in the execution context.

The object pair registration is important. Later, when any code requests the storage-handling object to transfer the state of the containing object to external storage, the storage-handling object can refer to the registration list to match the containing object up to the nucleus object and call the public property methods on the nucleus object to determine what data values to really transfer.

Pro:
  • The containing object doesn't contain public methods to get or set any private data of its responsibility.
  • The containing object has no responsibility for interactions with external storage. It only handles the nucleus object.
  • Since the nucleus object's responsibility is a bridge between external storage and the containing object, design compromises for the sake of the external storage implementation (e.g. a specific superclass?) are easier to accommodate without muddying the design and publicly-accessible "face" of the containing object.
Con:
  • The nucleus object is one additional object/class for each persistent original object/class that uses the pattern. It's closely tied to the containing object, its factory object, and its storage-handling object.
  • The original object must replace persistent data variable members with a private nucleus object member, and the containing object's methods must instead access persistent data values through the nucleus object's properties.
  • The containing object's constructors must have a nucleus object parameter.
  • The factory must construct the nucleus object, pass it to the containing object's constructor, and pass along weak references to the storage-handling object.
  • The storage-handling object must maintain one or more lists of pairs of weak references to containing objects and nucleus objects. It also must use these lists whenever any code requests a storage task.
  • The code in the storage-handling object must change to handle the nucleus object instead of the original object.

Wednesday, July 07, 2010

explicit is better than implicit: in favor of static typing

Right now, static not dynamic typing more closely fits my aesthetic preferences and intellectual biases. And the best expression of the reason is "explicit is better than implicit". The primary problem I have with dynamic typing, i.e. checking types solely as the program executes, is that the type must be left implicit/unknown despite its vital importance to correct reasoning about the code's operation. The crux is whether the upsides of a type being mutable and/or loosely-checked outweigh the downside of it being implicit.

Dynamism. I'm inclined to guess that most of the time most developers don't in fact require or exploit the admittedly-vast possibilities that dynamic typing enables. The effectiveness of tracing compilers and run-time call-site optimizations confirms this. My experiences with C#'s "var" have demonstrated that, for mere avoidance of type declarations for strictly-local variables, type inference almost always works as well as a pure dynamic type. Stylistically speaking, rebinding a name to multiple data types probably has few fans. The binding of a name to different data types is more handy for parameters and returns...

Ultimate API Generality. As for the undeniable generality of dynamically-typed APIs, I'm convinced that most of the time utterly dynamic parameters are less accurate and precise than simple parameters of high abstraction. This is seen in LINQ's impressive range of applicability to generic "IEnumerable<T>" and in how rarely everyday Objective-C code needs to use the dynamic type id. With few exceptions, application code needs to implicitly or explicitly assume something, albeit very little, about a variable's contents in order to meaningfully manipulate it. In languages with dynamic typing, this truth can be concealed by high-level data types built-in to the syntax, which may share many operators and have implicit coercion rules. Of course, in actuality the API may not react reasonably to every data type passed to it...

"Informal Interfaces". According to this design pattern, as long as a group of objects happen to support the same set of methods, the same code can function on the entire group. In essence, the code's actions define an expected interface. The set of required methods might differ by the code's execution paths! This pattern is plainly superior for adapting code and objects in ways that cut across inheritance hierarchies. Yet once more I question whether, most of the time, the benefits are worth the downside in transparency. Every time the code changes, its informal interface could change. If someone wants to pass a new type to the code, the informal interface must either be inferred by perusing the source or by consulting documentation that may be incomplete or obsolete. If an object passed to the code changes, the object could in effect violate the code's informal interface and lead to a bug that surprises users and developers alike. "I replaced a method on the object over here, why did code over there abruptly stop working?" I sympathize with complaints about possible exponential quantities of static interface types, but to me it still seems preferable to the effort that's required to manually track lots of informal interfaces. But in cases of high code churn, developers must expend effort just to update static interface types as requirements and objects iterate...

Evolutionary Design. There's something appealing about the plea to stop scribbling UML and prod the project forward by pushing out working code regardless of an anemic model of the problem domain. In the earliest phases, the presentation of functioning prototypes is a prime tool for provoking the responses that inform the model. As the model evolves, types and type members come and go at a parallel pace. Isn't it bothersome to explicitly record all these modifications? Well, sure, but there are no shortcuts around the mess of broken abstractions. When the ground underneath drops away, the stuff on top should complain as soon as possible, rather than levitating like a cartoon figure until it looks down, notices the missing ground, and dramatically plummets. Part of the value of explicit types lies precisely in making code dependencies not only discoverable but obvious. This is still more important whenever separate individuals or teams develop the cooperating codebases. The other codebase has a stake in the ongoing evolution of its partner. A "handshake" agreement could be enough to keep everyone carefully synchronized, but it's more error-prone compared to an enforced interface to which everyone can refer. During rapid evolution, automated type-checking is an aid (although not a panacea!) to the task of reconciling and integrating small transforming chunks of data and code to the overall design. Types that match offer at least a minimum of assurance that contradictory interpretations of the domain model haven't slipped in. On the other hand, unrestricted typing allows for a wider range of modeling approaches...

Traits/Advanced Object Construction. No disagreement from me. I wish static type schemes would represent more exotic ideas about objects. Still, most of the time, applied ingenuity, e.g. OO design patterns, can accomplish a lot through existing features like composition, delegation, generics, inheritance.

I want to emphasize that my lean toward static typing for the sake of explicitness isn't my ideal. I direct my top level of respect at languages and platforms that leave the strictness of the types up to the developer. I like a functioning "escape hatch" from static types, to be employed in dire situations. Or the option to mix up languages as I choose for each layer of the project. I judge types to be helpful more often than not, but I reserve the right to toss 'em out when needs demand.

foolish usage of static and dynamic typing

Whenever I read the eternal sniping between the cheerleaders of static and dynamic typing, I'm often struck by the similarity of the arguments for each side (including identical words like "maintainable"; let's not even get started on the multifarious definitions of "scalability"). Today's example is "I admit that your complaints about [static, dynamic] typing are valid...but only if someone is doing it incorrectly. Don't do that." Assuming that sometimes the criticisms of a typing strategy are really targeted at specimens of its foolish usage, I've therefore paired common criticisms with preventive usage guidelines. The point is to neither tie yourself to the mast when you're static-typing nor wander overboard when you're dynamic-typing.

Static
  • "I detest subjecting my fingers to the burden of type declarations." Use an IDE with type-completion and/or a language with type inference. 
  • "The types aren't helping to document the code." Stop reflexively using the "root object" type or "magic-value" strings as parameter and return types. Y'know, these days the space/speed/development cost of a short semantically-named class is not that much, and the benefit in flexibility and code centralization might pay off big later on - when all code that uses an object is doing the same "boilerplate" tasks on it, do you suppose that just maybe those tasks belong in methods? If a class isn't appropriate, consider an enumeration (or even a struct in C#).
  • "Branches on run-time types (and the corresponding casts in each branch) are bloating my code." Usually, subtypes should be directly substitutable without changing code, so the type hierarchy probably isn't right. Other possibilities include switching to an interface type or breaking the type-dependent code into separate methods of identical name and distinct signatures so the runtime can handle the type-dispatch.
  • "I can't reuse code when it's tightly coupled to different types than mine." Write code to a minimal interface type, perhaps a type that's in the standard library already. Use factories, factory patterns, and dependency injection to obtain fresh instances of objects.
  • "I need to change an interface." Consider an interface subtype or phasing in a separate interface altogether. Interfaces should be small and largely unchanging.
  • "When the types in the code all match, people don't realize that the code can still be wrong." Write and run automated unit tests to verify behavior. Mocks and proxies are easier than ever.
Dynamic
  • "It's impossible to figure out a function's expected parameters, return values, and error signals without scanning the source." In comments and documentation, be clear about what the function needs and how it responds to the range of possible data. In lieu of this, furnish abundant examples for code monkeys to ape.
  • "Sometimes I don't reread stuff after I type it. What if a typo leads to the allocation of a new variable in place of a reassignment?" This error is so well-known and typical that the language runtime likely can warn of or optionally forbid assignments to undeclared/uninitialized variables.
  • "Without types, no one can automatically verify that all the pieces of code fit together right."  Write and run automated unit tests that match the specific scenarios of object interactions.
  • "Beyond a particular threshold scripts suck, so why would I wish to implement a large-scale project in that manner?" A language might allow the absence of namespaces/modules and OO, but developers who long to remain sane will nevertheless divide their code into comprehensible sections. Long-lived proverbs like "don't repeat yourself" and "beware global state" belong as much on the cutting-edge as in the "enterprise".
  • "My head spins in response to the capability for multiple inheritance and for classes to modify one another and to redirect messages, etc." Too much of this can indeed produce a bewildering tangle of dependencies and overrides-of-overrides, which is why the prudent path is often boring and normal rather than intricate and exceptional. Mixin classes should be well-defined and not overreach.
  • "In order to read the code I must mentally track the current implied types of variables' values and the available methods on objects." Descriptive names and comments greatly reduce the odds that the code reader would misinterpret the intended variable contents. Meanwhile, in normal circumstances a developer should avoid changes to an object's significant behavior after the time of creation/class definition, thus ensuring that the relevant class definitions (and dynamic initialization code) are sufficient clues for deducing the object's abilities.

Wednesday, June 30, 2010

peeve no. 260 is factional prejudice in IT

Faction: a group or clique within a larger group, party, government, organization, or the like
I don't know what it is about IT that results in professionals forming factions. Use text editor X! Align your punctuation thusly! Avoid all software from company Q! Programming language R is your cool friend that all the popular kids like! Every problem should be flattened via solution U! Your VCS is DOA!

I understand that people develop attachments to their favorite tools and passion for their work; that's fine. What drives me nuts is the all-too-common over-exaggeration of these feelings. Acting as a cheerleader for your chosen IT factions can be fun, but if you ground your personal identity in it then you've officially gone mental. An IT professional should make technological decisions based on actual requirements and well-understood trade-offs, not based on which option fits your personal "style". Of course, usability and even nebulous subjective appeal are nevertheless legitimate factors in the decision simply because a difficult and hated option is detrimental to productivity and morale. The difference is a decision-maker who critically weighs all applicable criteria instead of just always "voting with the faction". Emotional investment is present without overpowering every other fact. Someone doesn't say "I'm a BLUB programmer". Rather, "I know the most pertinent portions of the syntax and semantics of BLUB, and I like BLUB because of BLURB".(I've complained before about grouping programmers by language.) 

Although I'm ranting against factional prejudice, I caution readers to not interpret my opinion as a case of "binary" thinking (i.e. thinking characterized by a simplistic division into only two categories). Some factional prejudices are quite beneficial and practical, when kept within reasonable and specific bounds. For example, past behaviors of a company or open source project are good reasons to be wary of current behavior. A platform's reputation for lackluster efficiency is a worthwhile justification to avoid it unless the platform demonstrates improvement (or the efficiency is sufficient for the task). Evaluate factional prejudice according to its motivation and relevance and realism. 

For businesses at least, technology isn't an accessory or an avenue of personal expression. It serves a purpose beyond the satisfaction of one's ego. The One True Way might not be what's right for the situation and the client.

Friday, June 25, 2010

watch out for a rebase of a merge

So, a while back I tried to publish some detailed foolproof instructions for using named branches and hgsubversion. I've been using Mercurial as a Subversion client for a little while now, and I figured that if I posted my discoveries and practices then others with an Internet connection could benefit as well. After revising the post multiple times, I finally decided to pull it (HA!) from the blog because it clearly wasn't as foolproof as anticipated - as proven by the mishaps of this fool, anyway. At this point I redirect you to the Stack Overflow question "How to handle merges with hgsubversion?" and parrot the advice there: don't bother trying to reconcile named branches to Subversion!  To fork development, clone the repository instead.

(Normally, my personal inclination is toward branches since clones feel like a hack solution. Given that I'm doing the right thing by using a VCS, why should I need to have separate directories for separate lines of development? It's true that environment variables or symbolic links make switching between the two directories fairly easy, but having to do those manipulations outside the VCS then feels like a hack of a hack. More and more frequently I wonder whether I'll settle on git, which has its own well-known set of warts, in the end.)

One of the primary shatter-points of my confidence was a rebase of a merge. This is the situation in which the default branch was merged into the other branch in the past, and the time arrives to rebase the other branch onto the default branch. (During work on a branch that will be ended by a rebase, the better course of action would have been to update it by a rebase onto default with --keepbranches rather than merging in the default branch.) The root problem is that a merge duplicates changes on the default branch by re-applying to the other branch, so to rebase this merge is to attempt to apply the default branch's past state to its present state, although the default branch may have changed in numerous ways after that merge!

As you might expect, a rebase of a merge often produces conflicts, and the likely desired resolution is to choose the version in the tip of the destination branch, "local" or "parent1". I can only write "likely" because, as usual with conflict resolution, only the developer's judgment can determine the right mixture whenever significant feature changes are involved in the conflict too.

While all these conflicts can be irritating, hair-pulling may be a more proper response to the instances of no conflict; Mercurial dutifully sends one or more files in the default branch backward in time without comment. I know of two causes of this behavior. 1. After the merge from default into other, a later change in default undoes one of the merged changes, and through the rebase of the merge the undoing is undone (reviving the original mistaken change). 2. After the merge from default into other adds a new file, a later change in default modifies the added file, and through the rebase of the merge the added file's later modifications vanish.

Therefore, after a rebase, it's a good idea to run a diff --stat in the range of the default's tip before the rebase and default's tip after the rebase. And for any of the unexpected changes that have no relevance to the feature just rebased, revert those files to the version of the default tip before the rebase and check in. Constant vigilance!

Monday, June 21, 2010

my turn to explain type covariance and contravariance

The Web has a wealth of resources about type covariance and contravariance, but I often find the approach unsatisfying: either code snippets or abstract-ish theoretical axioms. I prefer an additional, intermediate level of understanding that may be called pragmatic. Pragmatically understood, any pattern or concept isn't solely "standalone" snippets or logical axioms. A learner should be able to connect the concept to his or her prior knowledge and also to the question "When would I use this?" I'll attempt to remedy this shortcoming.

For code reuse, a pivotal issue is which data type substitutions in the code will work. Can code written for type Y also function properly for data of type Z? Well, if the definition of data type Z is strictly "Y's definition plus some other stuff", then yes, since the code can treat Z data as if it were Y data. Z is said to be derived from Y. Z is a stricter data type relative to Y. We then say that Z is covariant to Y but Y is contravariant to Z. Code that acts on Y can also act on Z because Z encompasses Y, but code that acts on Z may not be able to act on Y because the code may rely on the "other stuff" in Z beyond Y's definition. In languages that support it, the creation of a data type that's covariant to an existing type can be easily accomplished through inheritance of the existing type's language artifacts. (When someone uses the language feature of inheritance to create a type whose data can't be treated by code as if it were of the original type, confident code reuse is lost, which is the goal behind type covariance/contravariance!)

Thus far I've only mentioned the reuse of one piece of code over any data type covariant to the code's original type. The data varies but the code doesn't. A different yet still eminently practical situation is the need for the code to vary and nevertheless join up with other pieces of code without any of the code failing to work (e.g. a callback). What relationship must there be between the data types of the code pieces in order for the whole to work?

All that's necessary to approach this question is to reapply the same principle: a piece of code that works with data type Y will also work with data type Z provided that Z is covariant to Y. Assume an example gap to be filled by varying candidates of code. In this gap, the incoming data is of original type G and the outgoing data is of original type P.

Consider the code that receives the outgoing data. If the candidate code sends data of type P, the receiving code will work simply because it's written for that original type. If the candidate code sends data of a type covariant to (derived or inherited from) P, the receiving code will work because, as stated before, covariant types can "stand in" for the original type. If the candidate code sends data of a type contravariant to P, the receiving code won't work because it may rely on the parts of P's definition that are included in P alone. (In fact, this should always be true. Whenever it isn't, the receiving code should have instead targeted either a less-strict type or even just an interface. A "snug" data-type yields greater flexibility.) So the rule for the candidate code is that it must be written to send data of type P or covariant to P.

Meanwhile, the code that sends the gap's incoming data has committed to sending it as type G. If G is the original type that the candidate code is written to receive, it will work simply because it's written for that type. If a type covariant to G is the original type that the candidate code is written to receive, it won't work because it may rely on the parts of that type that aren't in G. If a type contravariant to G is the original type that the candidate code is written to receive, it will work because G is covariant to it, and once again covariant types are substitutable. So the rule for the candidate code is that it must be written to receive data of type G or contravariant to G.

Hence, the rules for covariance and contravariance in various programming languages (delegates in C#, generics in Java or C#) are neither arbitrarily set nor needlessly complex. The point is to pursue greater code generality through careful variance among related data types. Static data types are "promises" that quite literally bind code together. Pieces of code may exceed those promises (legal covariance of return); however, the code may not violate those promises (illegal contravariance of return). Pieces of code may gladly accept what is seen as excessively-generous promises (legal contravariance of paramaters); however, no code should expect anything that hasn't been promised (illegal covariance of parameters).

Thursday, June 10, 2010

agile's effect on productivity

Someone needs to state the obvious for those who miss it. Agile software development is not a silver bullet for productivity. Agile is not something you "mix in" and yield the same result faster. Applying agile practices to the humongous project won't result in massive software at lightning speed. That's not the aim of agile.

Agile's main effect is a decrease in project latency, not an increase in throughput. Agile development means delivering minimal but user-confirmed business value sooner, rather than delivering maximal but doubtful business value once at the end. The hope is that by going to the trouble of more frequent releases, the software will grow gradually but surely, as actual usage not guessing motivates what the software needs to be. The economy of agile doesn't result in a greater quantity of software features per development time period, but it's certainly intended to result in a lesser number of wasteful software features per development time period.

This shift in perspective affects everybody's expectations, so agile development is more than a set of new habits for developers. Project managers need to break their world-changing ambitions into much smaller chunks. Users need to become more involved. Analysts need to laser-focus their requirement-gathering. Architects and modelers (may) need to recalibrate the balance between resource costs and the pursuit of perfection.

If a plane flight is like a software project, then agile development won't decrease the time it takes to go from Seattle to Boston non-stop. But it will change the non-stop flight into a series of short connecting flights, and by the time you reach Chicago you might realize that you didn't really need to go to Boston in the first place.

Wednesday, June 09, 2010

DDD and intrinsic vs. extrinsic identity

Ask someone for an introduction to domain-driven design, and it will likely include descriptions of "the suggested building blocks". Two of the blocks are known as entities and value objects. The distinction between these two may appear to be pointless and obscure at first, but it's invaluable to a clearheaded and thoughtful model.

The difference turns on the meaning of identity, which philosophers have long pondered and debated, especially in regard to individual humans (i.e. the question "Who am I really?"). The answers fall into two rough categories: intrinsic and extrinsic. Unsurprisingly, the dividing-line can be fuzzy.
  • In the extrinsic category, a thing's identity is tied to sensations and ideas about that thing. This would be like identifying my phone by its shape, color, model, and so on. Based on extrinsic identity, if any of these "extrinsic" propositions about my phone ceased to be true, my phone would therefore no longer have the same identity. The phone would not only appear but be a different phone. Its identity is dependent. When someone meets a new acquaintance, his or her identity is generally rather extrinsic. "I don't know this person, but his name is Fred and he works in Human Resources."
  • The opposite of extrinsic identity is intrinsic identity, which is independent of sensations and ideas about a thing. A thing with intrinsic identity "wears" sensations and ideas but is "itself" regardless. This would be like disassembling my precious phone piece by piece and then continuing to call the pile of pieces my phone. Transformations of all kinds don't change intrinsic identity. When someone has maintained a long-term connection to another person, his or her identity is generally rather intrinsic. "She's changed her residence, weight, politics, and career over the last several years, but she's still Betty to me."
Neither category of philosophical answers is "right", of course, but merely more or less appropriate depending on situational factors like the thing to identify and the goal to meet by distinguishing identity. In DDD, the choice takes on added importance when modeling a domain object's identity.
  • If the object's identity consists of a particular combination of attributes, then its identity is extrinsic. Two objects of the class with identical attribute values are indistinguishable (for the purpose of the domain). These are known as value objects. Each instance is a "value" similar to a number or a string of characters. Since extrinsic identity is by definition fully dependent on a thing's parts, a value object never changes. But someone often creates a new value object using the attribute values of preexisting value objects. This is just like adding two numbers to yield a third number. Modelers and developers can exploit the unalterable nature of value objects for various advantages, such as avoiding the need for storage. Possibly, there could be a highly reusable "mathematics" of the value object in which all methods are "operations" that 1) operate on and return value objects and 2) are completely stateless (free of side effects).
  • Instead, a domain object's identity could be persistent throughout arbitrary transformations, which indicates intrinsic identity. Instances of the class probably have vital "history" and might not represent the same physical domain item despite identical or near-identical attribute values. These are known as entities. Normally a nontrivial entity will aggregate one or more value objects but along with a mechanism of unique identification. That mechanism could be a GUID or something else as long as no two instances can contain the same identifier by accident. Unlike value objects, (long-lived) entities change and correspond more directly to physical domain items, so a storage strategy for the entity and its associated value objects is necessary. (The advice of DDD is to keep the entities' code as purely domain-driven as possible by abstracting the storage details in a repository object.)

Tuesday, June 08, 2010

a dialogue about abstract meaning through isomorphism

This dialogue discusses some possible objections to meaning through isomorphism for abstractions. It follows a prior dialogue that delved deeper into the counter-claim that understanding of meaning requires more than symbols and syntax and algorithms, which is the point of the Chinese Room argument.

Soulum: When we last met, you alleged that the reduction of meaning to isomorphism is sufficient for the abstract parts of human mental life. Some examples of these parts are logic and mathematics, ethics and religion, introspection and philosophy. How can meaning of that level of abstraction arise from trifling "isomorphisms" with human experience?
Isolder: Would you admit that the words, and in some cases formalized symbols, that communicate abstract meanings have definitions? A definition is an excellent specimen of isomorphism. It expresses relations between the defined word and other words. Since the listener then knows which parts of his or her knowledge match up to the words in the definition, the listener can apply the corresponding relations to those parts of knowledge and thereby match up the defined word to another part of his or her knowledge. And at that point the definition has succeeded in its aim of enlarging the language-knowledge isomorphism. As I tried to say before, all that's necessary is that the isomorphisms be cumulative. The level of abstraction is incidental.
Soulum: Aren't you mistaking a symbol's definition in words for the symbol's actual and original meaning? It's undeniable that a symbol's definition is often communicated in terms of other symbols, but you're not addressing the fundamental difference of abstract meaning, which is the abstract symbol's creation. Non-abstract symbol creation is nothing more in theory than slapping a name on something in plain view. Making new combinations of existing abstractions is not much harder; tell me two numbers and I can mechanically produce a third. But from where do these numbers come? What is the "isomorphism" for the number one? The abstractions I speak of are more or less essential to human lifestyles, so the abstractions must be meaningful and the meaning must go beyond mere definitions. Your answer "Anything with a definition therefore has an isomorphism-based meaning" is far too broad for my taste. I could play with words all day long, assigning new definitions right and left, but my newly-defined words could still be self-evidently fictional and useless.
Isolder: Of course. I asked about definitions in order to show that abstract meaning is communicated via isomorphisms. That doesn't imply that every possible definition communicates a valid isomorphism to reality. Nor does it imply that abstractions are created by purely chaotic symbol combinations. Rather, a human forms a new abstraction by isomorphic mental operations: comparisons, extensions, generalizations, analyses. Each of these mental operations might result or not in a new abstraction of significant meaning. Isomorphisms also function as the verification of the new abstraction's significant meaning.
Soulum: It's abundantly clear that you have a one-track mind and its track is isomorphism. Instead of verbally treating every issue as a nail for your isomorphism hammer, why not put it to a specific test by returning to my question about the isomorphisms for the number one?
Isolder: Fine. The number one is isomorphic to a generalization of human experiences, in the category of quantities. In their lives, probably motivated by social interactions such as trades, humans developed a generalized way to specify quantities. It would've been greatly inefficient and bothersome to use individual symbols for "one stone", "two stones", "three stones". And once the quantity symbol had been broken apart from the object symbol, it would've been similarly tedious to use individualized sets of quantity symbols for each object symbol; hence the symbol for "one" stone could be reused as the symbol for "one" feather. The mental isomorphism of quantity between these situations thus became a linguistic isomorphism between the situations. The number one is abstract and mathematicians throughout the centuries have assigned it logically rigorous definitions in terms of other numbers and functions, but its isomorphic connection to real scenarios ensures that its meaning is much more relevant than the free wordplay you mentioned a moment ago.
Soulum: You're describing hypothetical historical events, and you give the impression that the number one is dependent on language usage. I believe that you continue to be purposely obtuse about the essential difference of my point of view. While one is useful in many contexts, its existence is independent and verifiable without reference to fleeting bits of matter. Humans discovered one. Isn't it an astounding coincidence that so many cultures happened to include symbols for one?
Isolder: Is it also an astounding coincidence that so many cultures happened to include symbols for any other concept? You may as well be impressed by the preponderance of circular wheels. We shouldn't be surprised by the independent invention of useful abstractions; think of how many times separate mathematicians have simultaneously reached the same conclusion but stated it using distinct words and symbols that are isomorphic to one another. Moreover, note the history of the number zero, the number "e", the number "i". Cultures got along fine without these luxuries for centuries (although practical "pi" has been around for a while). The pace of mathematical invention sped up when it started to become an end in itself. Yet even "pure" mathematical work is somewhat mythical. Mathematics has always been motivated, whether to prove abstruse conjectures or solve engineering problems. One of the spurs for growth was the need for more sophisticated tools just to describe scientific discoveries. Can a student know physics without knowing calculus, tensors, and vector spaces? You could say that a physics student with a "conceptual" understanding only has approximate mental isomorphisms for the movements of reality.
Soulum: Again, the specific human history of the abstractions is a digression. No matter the history, no matter the degree of usefulness, no matter the isomorphic resemblance to anything whatsoever, these abstractions are provably true forever in an intellectually appealing fashion. A quintessential blank-slate hermit could conceive of it all; some artists and writers and mathematicians and moralists have in fact created their masterpieces in almost-total isolation. Any time someone communicates one of these abstractions to me, I don't need to "just accept it" in the same way I must when someone communicates the result of an experiment that I don't have the resources to duplicate. I can prove or disprove the abstraction using the capabilities of my mind, guided perhaps by the sort of finely-honed intuition that escapes artificial imitation.
Isolder: The all-knowing hermit you speak of certainly has supernatural intelligence and creativity, not to mention long life! Setting the hermit aside, I readily acknowledge that individuals can figure out the validity of abstractions for themselves. And you may groan when I assert how they do it: isomorphism. The processes of logic and rigorous proof are repeated forms of isomorphism. "All men are mortal" is obviously a relation between the two ideas "all men" and "mortal". "Aristotle is a man" matches "Aristotle" to "all men". If the match is a true isomorphism then the relation is preserved and "Aristotle is mortal". However, I seriously question the amount of emphasis you place on personal testing of abstractions. Wouldn't you concede that for the most part humans really do "just accept it" in the case of abstractions? A teacher can flatly preach that zero multiplied by another number is always zero, or the teacher can list the other multiples of a number to show that according to the pattern the zeroth multiple "should" be zero. What is the likelihood of the teacher using "algebraic ring structure" to prove that, due to distributivity, the product of the additive identity and any other element is the additive identity?
Soulum: I don't maintain that all abstractions are taught with proofs but only that whoever wishes to perform a check on the abstraction will always get the same answer. Call the written justifications "isomorphisms" if you insist, but in any case the abstractions have sublime logical certainty.
Isolder: Indeed, some abstract questions always get the same answer, and some others like the "purpose of existence" seldom do. I'd go so far as to say that the long-term success of an abstraction stems directly from its degrees of determinism, verifiability, and self-consistency. Without these properties, the feeling of sublime logical certainty isn't worth a lot. Whenever someone learns of an abstraction, the abstraction should come with an isomorphism-based checking/consistency procedure to assure that the abstraction is applied correctly. In short, once the abstraction is set loose from isomorphisms with reality, its meaningfulness can only reside in isomorphisms to yet other abstractions. It's no accident that there's one "right" answer for many abstractions; chances are, the abstractions were designed to be that way. The best abstractions contain few gaps between the internal isomorphisms.
Soulum: I disagree. "Best" is subjective and depends on the particular abstraction's goal. Subtlety and difficulty, interpretation and symbolism, are hallmarks of good fiction, for instance. Where are the isomorphisms there?
Isolder: The isomorphisms are whatever the fiction draws out of its audience. Besides ambiguity, good fiction has relatability, which is why outside of its intended audience it can be unpopular or mystifying. An isomorphism doesn't need to be verbalized to be active. Emotional "resonance" is in that category. I'd argue that some of the most behaviorally-powerful isomorphisms are either genetic or unintentionally imprinted from one generation of a culture to the next. Given the level of instinctive reactions seen in animals, ranging all the way from fish to primates, the presence of passionate and involuntary impulses in humans is an evolutionary cliché. Human brain complexity can eclipse the simplicity of the impulses, but humans continue to sense the primitive isomorphisms behind those impulses. For example, facial expressions and body movements are hugely important to the overall effect of a conversational "connection" because the isomorphisms are largely subconscious.
Soulum: To define "isomorphism" that broadly is a few steps too far, in my opinion. It'd be futile to advance other counterexamples, which you would duly treat as fuel for further contrived isomorphisms. It seems that I can't convince you that the soul fathoms meanings too richly intricate for your crude isomorphism-engine.
Isolder: And I can't convince you that, no matter how deep a perceived meaning is, isomorphism is ultimately the manner in which matter exhibits it. No soul is required, but simply a highly-suitable medium for adaptable isomorphisms such as the human brain.