Object Orientation, Recursion and Paradigms: Naturalized Metaphysics
In his seminal work The Structure of Scientific Revolutions, Thomas Kuhn introduced us to the concept of a paradigm - an overarching structure of science, a framework for thinking about a field, such that new discoveries are incorporated into a theory only when they accord with the paradigm. In the normal scheme of things, discoveries that do not accord with the current paradigm (or as Kuhn puts them "anomalies") are more or less ignored, shunted off into a corner of scientific thought. If and when the anomalies become too numerous to ignore, a revolution of scientific thought occurs, and a new paradigm is the result.
This paper attempts to take Kuhn's theories one step further. His theories said only that paradigms control the structure and direction of scientific thought. I go further in two ways. The first is that I believe that much more of human thought works along the same or similar lines. Whenever we learn something new, we fit it into an existing paradigm. There are areas where we do not require the use of a paradigm, but they are few, far between, and limited in scope.
The second argument I intend to make is that a paradigm is in large part1 a method of grouping. For example, a small child quickly learns to group the class of things intended for one person to sit on, and that the term for such a thing is "chair". When we accumulate new information regarding the structure of the world, our paradigm tells us how to group that input.
My theories should not be construed as endorsing some of the literature surrounding Kuhn's original work. My theoretical debt to Kuhn's work begins and ends with the concept of paradigm - my interpretation of which I have detailed above. It is not my intent to make any strong statements about the methods of adoption of these paradigms; I wish to discuss exclusively their nature and prevalence.
In the past, from Plato among others, these sorts of groupings have been recognized - but most previous theories dealing with them have assumed or stated that they are a natural feature of the world. These "Platonic Ideals" are theorized to be perfect versions of objects we see in the world, real objects being merely flawed instances of the ideals. Although I believe my descriptions of paradigms to be fairly similar to this concept of ideals, I differ completely in my account of their origins.
Plato would have us believe our notions of grouping come from our own appreciation of natural kinds - we do not create these groups, we perceive and appreciate them from the world. I would have us believe either that we learn these systems from our parents and teachers, or learn them on our own, and further that we have innate systems for generating paradigms in just the way we have innate systems for generating grammars.
Such groupings are a ubiquitous part of our cognition and study, and when one looks for them the groupings show up clearly in every area of human endeavor. At a macro scale, this is fairly obvious, even conscious. Biology is the study of things that are alive. Mineralogy is the study of rocks. At a micro scale the grouping is sometimes less obvious, even when the predominant descriptive term for a subject is "paradigm". Although most programmers tacitly understand the general natures of programming paradigms, few programming texts discuss them explicitly in any robust way.
This paper will attempt to begin to rectify this situation by examining four major subject areas. In some cases is to establish that the study can even be discussed as a paradigm, in the sense that it might simply be a natural feature of the world as Plato thought everything was. When necessary, I will attempt to establish that the paradigm is a feature of our perceptions instead of the world as a preliminary.
Having established the subjectivity2 of the study, I will then attempt to describe the study in terms of what groups it establishes within the subject, and what flaws this grouping might have as compared to others. In some cases, I will then suggest routes of investigation - directions that other scholars in the field might take their thoughts to develop a new improved paradigm.
I will conclude the paper with a discussion of some of the broader implications of the adoption of this theory, in particular in education and communication.
In some sections of the discussion, I may use the term "division" interchangeably with "grouping". This is not to be understood as to be something different - A division of a large mass is placing the mass into smaller groups. Although the emphasis in words is slightly different, no significance is to be put on the choice of words.
The first general area I will discuss is that of computer science, and programming specifically. I will examine three major programming paradigms. For each paradigm I will give a sample of the paradigm in action. Given the sample, I will attempt to extract what about the code exemplifies the paradigm, and then I will explain how my definition of paradigm fits in.
In some of my discussions of programming paradigms, I make it seem as though C and Lisp are "pure" examples of their respective paradigms. This is inaccurate, to say the least. I do show an example of a piece of functional programming in C, but it is just as possible to write imperative Lisp. It is perhaps easier in to write functional Lisp and imperative C, but both languages have the capability of existing in the other format.
In order to most evocatively display the differences between the computer science paradigms, I have written the same program in all. I wanted to choose a sample task that would clearly show the differences between paradigms. Often, in the writing of real programs, it is necessary to in some manner accomplish some small operation on each element in a list. It is important to note that the size of the list is not known when the program is written, only when it is run. The operation in a real program might be to update each entry in a list of patients for a daily change. In my examples, each entry will contain an integer, and the operation will be to square it, and keep the result.
The imperative paradigm is perhaps the simplest to understand for a new programmer. It is largely an extension of the way we tend to instruct each other - most cookbooks are written in something approaching the imperative paradigm. The standard method of describing a recipe is to tell the cook what to do, step by step. In just the way that a cookbook might tell you to repeat a circular motion with a whisk in some egg whites until they are "firm but still moist", a program written in the imperative paradigm might tell the computer to spin a sphere on the screen until the user pushes a key. In both cases, there is a clearly defined loop, and a clearly (or vaguely, if you don't know what "firm but not moist" looks like) defined end condition.
The imperative is also most likely the most widespread of the three I discuss, and often underlies or supports the other paradigms. To some extent, this is a result of the architecture of computer processors, which are closer to the imperative paradigm than to any other.
The programming language that most often represents the imperative paradigm is C. I will present my example of an imperative C program, and then dissect it for the enjoyment of the reader, in an attempt to extract the key elements of the imperative paradigm. It is very important to note that the imperative paradigm and the C language are not at all the same thing. The paradigm is a theoretical description of a method of writing programs. The language is a formal specification, a "contract" between the language designers and compiler designers. C includes a number of features that have no bearing on paradigm whatsoever, and even has support for paradigms that are not the imperative.
What follows is an almost3 complete C program that - using the imperative paradigm - constructs a list of 25 random integers, and then squares each integer in the list.
By way of explanation, I will walk through the code, and try to highlight items that have importance, and gloss over items that have less.
The first section is, for the purposes of this discussion, merely overhead. The typedef struct section is just setting up a data structure and naming it for later use as part of a list. The closest thing C has to a list data structure is the array. I personally find arrays unpleasant to deal with, and tend to write programs using this sort of syntax. This said, anything that I say in this paper about the imperative paradigm would be equally applicable to a program that used an array structure.
The next section is the main function. The main function is where the program will start when it is actually run. In this case the main function is fairly brief, only three lines. Although it may seem odd to use makeList and squareList before I have defined them, this is the way I have been taught to write my programs, for the sake of readability. It is quite possible to put the main function last.
Following the main function are the two functions I use in the program. The first, makeList, makes the list and returns a pointer to it. Ignoring the syntax and overhead, the important part of the function is the loop:
Simply put, for each value of n from 0 to 24 (it stops when it hits 25, before it goes through the loop), this code snippet creates a new node in the list, and generates its contents. You can begin to see the shape of the paradigm here. The program goes through the loop once for each value of n, and each time sets up a new node. The program is dividing the task of setting up the list into 25 equal parts. The parts are demarcated by the variable, and are iterated through.
The squareList function has almost nothing but a very similar loop. Although the conditional that determines the stopping point is different, the basic pattern of control is exactly the same - do a set of operations to each element in the list. The grouping here is the same as in the makeList loop. I divide the task into the same 25 equal parts, and again iterate through them.
The imperative paradigm dictates that a task should be divided into equally sized pieces that could be "pointed"4 at by a loop, with each trip through the loop pointing at a different element. The while syntax is merely an example of this syntax, and should not be confused with the actual paradigm.
Another paradigm with a long and venerable history is the functional. In fact, the oldest language that people still use by choice (not because of legacy programs) is primarily designed around the functional paradigm. This language is Lisp. Lisp is old enough that there are a number of variants of it, and the version that I will use here is Common Lisp. Just as C and the imperative paradigm are two completely different things, Lisp and the functional are very dissimilar. Just like C, Lisp is a living language that many people use every day, and just like C Lisp includes support both for things that are not paradigmatic, and for other paradigms.
Again, I will give a sample program, and then attempt to extract the elements of said program that most exemplify the paradigm, as opposed to mere syntax.
Unlike the imperative paradigm, the functional paradigm is taken to mean something very specific in computer science literature. These definitions are largely concerned with the mathematical properties of "pure functional" programming. The most important aspect of pure function is that assignments are not made5 - there are no variables. This allows for some clever mathematical tricks to determine the efficiency of the algorithm. Also important is freedom from side effects - all that a function does is return a value.
From a practical "How do you write functional programs" perspective, the trick is to think of functions as more significant entities than you might think of a C function. In C, much of the time when you separate a function from another, the difference is to an extent cosmetic. With no major changes to the code, it is usually fairly straightforward to simply drop the function into the calling section of code. In fact, when tracking a program through its steps, I think of the program in just such a way. There are some syntactic issues, but conceptually it works fine.
When programming functionally, this is an inappropriate way to think about functions. Functions are properly thought of as coherent pieces - you pass them whatever they need to know, and they return what you needed.
The second conceptual leap a budding functional programmer must make is an appreciation of recursion. The best way I can determine to explain recursion is actually walk through a recursive function. The above Lisp program uses recursion, the below is an examination of it.
The first line
is merely overhead. This line of code doesn't actually accomplish anything other than telling the interpreter6 that the function is called square-list and that it will take one argument, which will be called lst.
(if (null lst) '()
A fairly odd feature of recursive programming is that when writing the program, you usually think about the end of the program first. In this case, the end of the line is when there is nothing left in the list argument, and if that is true, then I want to return an empty list.
(cons (square (car lst)) (square-list (cdr lst)))))
Most of the meat of the program is in this line. This little scrap of code does three semi-unrelated things. For reasons soon understood, I will start with the last: (square-list (cdr lst). The preceding code (as will be seen soon) takes care of the first element in the list, this takes care of the rest of the list. The scrap (cdr lst) returns a list that is lst without the first element, and that return is then the argument for another run-through of square-list. That run-through of square-list then does the same for the rest of the list. The middle bit (square (car lst)) does the actual squaring. (car lst) gets the first element of the list, and the (square returns the square of that element. The last piece of the puzzle, (cons, takes the return of second bit and the return of the last bit, sticks them together, and returns them both as one list.
Learning this way of looking at programming is often difficult for beginners. We do not tend to think about day-to-day tasks in this manner, so this paradigm is non-intuitive for many. However, as detailed later, many tasks that might be extremely difficult using the imperative paradigm are relatively simple using the functional paradigm. On a personal note, one of the biggest reasons I love the functional paradigm is that a tightly written recursive function appears quite beautiful.
The division of macro-tasks in the functional paradigm often ends up looking the same or similar to an imperative eye. In fact, particularly in simple examples such as the ones above, the two paradigms are straightforward to translate into each other. The method of arriving at the division is different, however. In the imperative paradigm, the question a programmer asks herself is how to split the task up into n even parts. Thinking about the imperative paradigm is like building a ladder one rung at a time - you build the bottom rung, then the second from the bottom, and so on.
The functional paradigm is much more like drawing a fractal. A programmer using the functional paradigm thinks about a large task as having two parts - the first section of the task, and the rest of the task, arranging it such that the rest of the task is congruent to the task as a whole, just as a major part of a fractal can be manipulated to be identical to the whole. The empty task is simply viewed as the smallest (and thus terminating) "fractal" version of the macro-task. To build a ladder, you build the bottom rung, and add it to a ladder. This sort of form is the typical recursive form of functional programming, making heavy use of self-referential definitions.
On a side note, this sort of self-referential definition has exerted a pervasive influence on hacker culture, resulting in such organizational acronyms as GNU (GNU's not UNIX), and program names such as WINE (WINE Is Not an Emulator), FINE (FINE Is Not Emacs), and THIEF (THIEF Isn't Even Fine). The New Hacker's Dictionary, aka the New Jargon File, (version 4.2.0) has the following definition of recursion:
See recursion. See also tail recursion.
This entire discussion would lack much of a point if it were to turn out that different paradigms for understanding the same sorts of things are exactly equivalent, only cosmetically different. Fortunately for me, this is not the case. By way of explanation, I would like to turn your attention to the area of binary trees.
A binary tree is a data structure template with some attractive properties. Properly used, it can make searches and replacements a great deal faster. The template is fairly simple - a binary tree is simply a set of nodes that connect to each other only through parent-child connections. Further, each node can have at most two children, and can have no fewer than none. This results in a structure looking roughly like this:
/ + \
O + O
/ + \ + + \
O + O + O
/ ++ / + \ ++ \
O + O + O + O
In an actual implementation of this, each node would have some content as well as constituency in the tree. The content might be a medical record, a name and number, or any sort of data. Take my word for it that such things are in use, and that there are good reasons for their use.
Now consider the case in which the programmer wishes to do an operation on each of the nodes in the tree. It is important to note that when one is programming, one cannot see the whole tree at once. When it is written on a page, it seems straightforward to simply do the same operation to each node in the tree. But when I actually write a program that uses a tree in C, what I actually have is a single pointer to the base (top, ultimate parent) node of the tree.
To break it down to specifics, let us say we have a binary tree of unpredictable size, each node containing an integer, and we wish to square each integer. We now know that we have two major paradigmatic choices.
Accomplishing this task using the imperative paradigm is quite difficult. Answering the question of how to do this in any robust way requires sketching out the form of the answer in an actual imperative language. As mentioned above, C is an extremely widespread language that is to a large extent imperative. Naturally, I then asked the question in terms of the C language.
The difficulty here is that C is anything but pure in the imperative paradigm. The entire point of this example is to show that this task is a great deal easier in the functional paradigm. Naturally, C being a language designed (more or less) to make programming actual applications, C includes support for the easier way to accomplish this task. Thus, somewhat predictably, whenever an experienced C programmer attempts to accomplish this task, their first impulse is to use the functional paradigm.
Forcing my compatriots7 and myself to think of this without resorting to functional solutions resulted in two possibilities. The basic issue is that when the program first encounters a node, both possible sub-branches of it must be checked. Looking at a drawing of a structure of this sort, the easiest way to do this as a human is just to go row by row - first square the top node, then the next two if they exist, then whatever is in the third row, and so on.
Unfortunately, this is not possible for the program. Given the top node, all that the program can do is get a pointer to either the left child node or the right child node. Since the program can only follow one of these at a time, a mechanism for emulating how a human would do it is not obvious.
After a considerable amount of back and forth discussion about this problem - discussion characterized by whoever I was asking saying "Hey, that's easy", and then "no wait, that's functional", we came up with an imperative solution. In a nutshell, it is to add both children (if they exist) of each node it finds to queue of nodes to be checked eventually as the program finds it, then to square whatever node it is currently considering. In pseudo-code, the solution would look like this:
If there is a left child, add it to the queue
The major disadvantages of this method are that it is extremely difficult to think about, and that it requires the construction of a second data structure.
The functional solution is a great deal more elegant. Although there is no clear way to divide the tree into equally sized chunks that can be iterated through, there is an easy way to divide the tree into unequally sized chunks that are congruent. In pseudocode the solution looks like this:
Do the top node.
With a bit of thought, it becomes clear that the left and right children of a node can be viewed as trees in their own right, and the same function can be used to square both of them. In actual code, the solution looks like this:
It seems clear that the functional solution is a great deal easier to understand and think about, and certainly is easier to write.8 This illustrates the fact that there are important differences in the paradigms. The functional is superior for this particular case, but there do exist situations in which the imperative is better.
When given a particular problem, a programmer must consider which paradigm will be best. In many situations different programmers might select differently, but in some (such as the above example), there is a general consensus. Some of the time, it is a question of which paradigm the individual programmer feels solves the problem most clearly and elegantly.
Object Orientation (OO) is at a different level of thought than the imperative and function paradigms. In fact, OO is built on top of either of the preceding two, lending outside structure to the way the code is organized, both for the programmer and for interaction with other pieces of code. Object orientation is not so much a way to write code, as it is a way to organize code you already know how to write. Again, I will walk us through a sample object oriented program, in the Java9 programming language.
Object orientation declares that the programmer should organize functions around the data that they operate on. The way that this is instantiated in Java is by only declaring functions within a class. A class is basically a more elaborate version of the structs we saw in C. If you imagine that instead of merely being able to have fields that contain data in the struct as in C, we can also have fields for functions, you are not too far off the mark.
Defining a class is only the first half of the operation in this case. Just like when we declare a struct we don't actually have one, just defining a class doesn't actually give you one. In order to actually do anything in Java, (or any completely object oriented system) you need to actually make an instance of the class. One proviso: As discussed above, few useful languages are paradigmatically pure, and Java is no exception. Java is not purely and exclusively object oriented. It is possible to make things in Java not be objects. Anything tagged with the static keyword is probably not really object oriented. The only thing in this particular example that has the static keyword is the main function - you have to start somewhere, so the main function is required to be static.
The first two lines of the program declare that this class is going to be called NumberList, and declare that this class is going to have a single data field. This data field will be of type Vector, called list, and will be private. The next piece of code says that the first function will be the main function. Although it explicitly stating it is not required, it is implicit that any class that does not inherit from anything else inherits from the object class. (If this doesn't make sense now, that's okay.) The first interesting line of the program is
NumberList numberList = new NumberList(25);
As mentioned above, in order to do anything meaningful in Java, you first have to create an instance of the class (in general, any instance of any class is an object). This line does so. It creates an instance of the class NumberList (the one we are currently defining), and assigns it to be called numberList. Although these may seem to be the same word, they are in fact not. The name of this particular instance has a lower-case first letter, and the name of the class has an upper-case first letter. Confusing as it may be, this is the convention (standard way of doing things).
It may be somewhat confusing to be creating an instance of the class before we so much as define it. This is partially because the main function is somewhat separate from the rest of the code. Although it is perfectly acceptable (and even less confusing) to put the main function in a separate class (and a separate file), it is also acceptable to put the main function in the same class. Unless you are trying to learn Java from this document, don't worry about it. Hopefully, by the end of the section the explanation will become either clear or superfluous.
The first part of the line (before the = sign) doesn't actually create the instance. That first part merely creates a variable name called numberList, which will be of type NumberList. The second part of the line creates the actual instance, using the soon-to-be-defined Constructor. The = sign then assigns the newly constructed NumberList to be the variable. Stated less technically, this line makes a NumberList.
The next line, while somewhat tangential to the main program, illustrates an important piece of the object oriented paradigm. System.out.println is Java's built-in function to print out data to the screen. Println only takes a String as an argument. A NumberList is not a string. It is to be noted that by putting numberList in the parentheses, I am passing it to println. This is made possible by a number of features of the paradigm/language:
1) Every class in Java inherits eventually from object, including this one.
These four items are actually all derived from two major features of the object oriented paradigm: Inheritance and Polymorphism. Inheritance is pretty simple. All classes in Java are required to inherit from another class. Inheriting from another class means that all of the data fields and all of the methods (method is Java's fancy word for function) from the old (parent) class are included in the new (child) class. Any class that does not explicitly inherit from some other class inherits from object by default. This is another way of grouping methods - we liken the child class to the parent class, and can then conceptualize the child class as a subset of the parent. In practical terms, we can say that an instance of class apple is also an instance of class fruit.
Polymorphism is also pretty simple, and is entirely dependent on Inheritance for its meaning. Polymorphism means that while I'm defining a new class, I am allowed to define a method that has the same name as a method my new class inherited, and whenever I (or, say, println) call that method, it is the new one that is used. Taken together, these two features, (and some work on my part), allow the println call to be simpler than it might otherwise have been. Less of a grouping than a convenience, this language feature allows inheritance to be a great deal more useful and meaningful.
The next line squares our just-printed list. Astute observers will note that while in the C program I called the function and passed it the data, here I appear to call the data and pass the method to it. This is an entirely accurate way of looking at it. A third feature of object orientation is that all methods are associated with a class, and can only be called by an instance of that class.10 This is yet another way of grouping functions, by requiring that all methods be associated intimately with an object
From there, all that is left to the main method is to print out the list again, simply to show that the squaring happened.
The next two methods are the Constructors, one of which is not actually used in the main method. In Java, as in most object oriented languages, every time the programmer creates a new instance of a class, he or she does so by calling a Constructor. In fact, there is no way to create an object without calling a Constructor. While important and very useful, Constructors are not integral to the object oriented Paradigm, so I will spend slightly less discussion on them. In this case, there are two Constructors, one of which takes no arguments, one of which takes a single argument, an int. All that the no-argument Constructor does is call the other constructor with an argument of 25. Although this does not add any functionality whatsoever, it is polite to include it, for reasons soon to be divulged.
The one-argument constructor is also fairly simple. The first line initializes the data Vector in this instance. A Vector is a built-in Java data type, and it supports most of the operations that any programmer could ever want to do to a list of data, in a fairly efficient way. Vectors are the basic data structure of Java, just as linked lists are the basic data structure of C11. In this case, I initialized the Vector called list that is in this class to have a capacity of length. The rest of the method is just generating and adding the actual elements.
The no-argument Constructor and the next two methods were written for the same reason. Although two out of the three are not used, it is standard practice in Java (and object oriented languages in general) to hide the inner workings of the class from the outside world (including the classes own main function, if it has one). The only way to manipulate the class should be a well-defined set of methods that provide a documented interface to the class. To accomplish this goal, the data field(s) are made private, (only accessible from inside the class) and accessor methods are written. This is the third major principle of object orientation, and is called Encapsulation. Instead of grouping the functions, this principle groups the data. Objects are not able to access data except through the good offices of the owning object.
When I am writing a program that draws a square on the screen, I simply have to create a Square object and tell it to draw itself. If I have to write the Square object, when I write it I will worry about the positioning of the square, the methods for drawing and creation, and all the other details inherent in a Square. But when I want to draw a square, I needn't think about any of the details. All of those details are cognitively grouped with the Square object, and I can dismiss them from my thoughts. This is the great strength of object oriented programming.
An actual example of this principle in my code is the toString method. The toString method, as described above, is the standard method that returns the String representation of the instance. The toString method for this particular class just goes through each element and adds it to a String Buffer, which gets transformed into a String and then returned. The getVector method returns the Vector equivalent of the NumberList. While in the current implementation of NumberList the internal representation is a Vector, encapsulating the data in this way could allow me to completely rewrite the internal workings of the class, and not change either my main function, or any other program I write that uses a NumberList.
From a programmer's pragmatic perspective, this is one of the most important features of OO, but from a paradigmatic perspective it is relatively minor - as facilitated by Java, using this, or any other generated class, in any other piece of code, is as straightforward as using a Vector.
Finally, the last method in the NumberList class is the one that does the task that I set out to do, square the list. Note here that the actual control structure bears a strong resemblance to the structure I used in C, the major differences being in syntax.
Having object orientation to organize our functions is much better than not having it. In my personal experience, and from my conversations with others I have no reason to believe I am unusual, OO has allowed me to much more easily create larger programs, while maintaining my own sanity. However, it seems to me that just as the functional paradigm is better in some situations than the imperative, there may be a potential paradigm that would be more helpful for understanding certain areas, or there may be an improvement to the OO paradigm.
The next general area I discuss is that of cognitive science. Cognitive science is generally concerned with the study of human thought, but one of the basic assumptions of the science is under debate. This debate regards how cognitive science should conceive of cognition at an abstract level. When philosophers wish to discuss the basic nature of thought, there must be a basic framework - a paradigm of discussion, a base theory. The disagreements on this level of discussion begin with the question of whether or not humans have internal representations of the world.
As important as it is, this question is not the subject of this section of this paper. Both sides of the debate examined herein agree that human cognition involves internal representation. And for fear of leading the reader's intuitions in undesired directions, I shall now move directly on to the debate that I am concerned with. There are two basic theoretical stances in this debate - the connectionist, and the classical.
The connectionist theorist thinks that any description of cognition should take its inspiration from the physical make-up of the brain. According to neuroscientist's current understanding, at a physical level our cognition is composed of the firing of interconnected neurons. Starting there, our cognition is best understood as a network of nodes, connected with links of varying strength. All of our thoughts are built up out of agglomerations of these nodes.
The classical theorist starts with an entirely different inspiration. The classical theorist starts with the work of Alan Turing, and bases all of her theories on symbol-processing systems. A proper theory of cognition, according to the classical theorist, has rules for manipulating symbols, comparing them to each other, and setting up relations among them.
At a first reading these two theories seem to be entirely compatible. They simply are not discussing the same level of cognition. But Fodor and various partners all seem to think connectionism has serious problems, and Smolensky on the other hand thinks the same of classical theory. So where is the disagreement?
Fodor summed up the difference for me best in the following passage: (p97)
Connectionist theories acknowledge only causal connectedness as a primitive relation among nodes; when you know how activation and inhibition flow among them, you know everything there is to know about how the nodes in a network are related. By contrast, Classical theories acknowledge not only causal relations among the semantically evaluable objects that they posit, but also a range of structural relations, of which constituency is paradigmatic.
As is soon made clear by Fodor, the sorts of structural relations of which he speaks are the same sorts of structural relations that pertain in linguistic theory - "a combinatorial syntax and semantics", and "structure sensitivity of processes". A classical theory is not just a system for manipulating symbols according to Fodor; it is in fact a grammar, with all of the implications that carries.
Now that I have established how some proponents of these theories view them, it appears to be time to examine them through the lens of my definition of a paradigm. This argument seems to have two levels. The first level is of paradigm sorting. The classical theorists seem to be saying that a new paradigm is unnecessary. To understand thought processes, all we need to do is apply the lessons we have learned in linguistics. There may be some cosmetic differences, but thought and language are largely the same. Just as we do not need two different paradigms to study two different varieties of apple, we do not need two different paradigms to study thought and language.
The connectionist has two primary points. The first point is what I believe to be the true wellspring of disagreement - that the connectionist believes that we do need a new paradigm to understand cognition. Linguistics is still a valuable science, but something that a cognitive scientist can say about language is not necessarily something a cognitive scientist can say about cognition. They may be strongly related, linguistics may well be a subset of our greater cognition12, but they are not the same thing. It cannot be disputed that a connectionist also believes that the correct paradigm for studying cognition is described well by the sort of networks discussed above, but this is in fact a separate sort of argument.
Fodor and Smolensky's arguments can then be understood in a different light. Smolensky and other connectionists approach the study of cognition and argue that we should study it in a different sort of way than has previously been attempted. Fodor looks at this theory, and instead of seeing the argument that thought does not equal language, sees the argument that connectionism is a good way of understanding cognition. The debate is then better understood as Fodor arguing that thought is language, and Smolensky arguing that thought is a network. But underlying it all is another thread of disagreement over whether a new paradigm of understanding is required at all. Whether that paradigm ought to be connectionist or not is a separate issue.
My feeling on this discussion is that neither view is entirely invalid. Fodor on the one hand has pointed out some issues he believes irreparably flaw connectionism. On the other hand, many others including Smolensky have pointed out different flaws in the classical approach. I do not wish to espouse either view strongly over the other; my intent is to point out that neither is a "natural" description of our cognition. I would encourage theorists in this area to acknowledge that both views have flaws, and that both views have strengths. Having made that acknowledgement, they can move on to characterize the strengths and flaws of each, and armed with that knowledge, make intelligent choices about which paradigm is appropriate for what studies.
The next area up for consideration is one that is seen is one that is widely acknowledged to be of high importance. Many learned scholars, including Plato, Hume, and Kant have spent much of their lives attempting to discern what makes an action Just, or Moral. I believe that if we consider morality as just another paradigm of thought, a way of dividing and grouping, then certain things become more clear.
This may seem on first blush to be a tremendous mistake. Ethics does not at most times seem to be an arbitrary grouping assigned by our perceptions. In the interest of leading the Reader into admitting the possibility that morality is so relative, we will go on a slight tangent.
In his essay Why it is Impossible to be Moral13 Steven Schwartz posits that morality is impossible. His argument runs along the following lines: the principle of equality (as he terms it) is widely acknowledged as an important underpinning of the structure of morality. The principle of equality states that if two or more situations are indistinguishable, a moral system (or person) will treat them indistinguishably.
Having a moral system that completely satisfies the principle of equality, Schwartz continues, is not an attainable goal. The first building block we need to show this is the nontransitivity of similarity (NTS). The most intuitive example I have seen is that of color. It is simple to imagine a spectrum of color, starting at red, and ending at orange. Now imagine that two consecutive microns of color (that is, the color from two consecutive microns of color) are extracted and magnified such that each covers an area the size of the complete spectrum. It is a proven fact of perception that a spectrum can be drawn in which any two consecutive colors cannot be distinguished by the naked eye. On consideration, this is not very surprising - movies and television work on similar (though not the same) principles, fooling the eye and the mind behind it.
This NTS issue is not limited to perceptual systems. Schwartz quotes Max Black:
One can imagine an exhibition in some unlikely museum of applied logic of a series of 'chairs' differing in quality by least noticeable amounts. At one end of a long line, containing perhaps thousands of exhibits, might be a Chippendale chair: at the other, a small nondescript lump of wood. (Language and Philosophy)
In such a series each item may be distinguishable overall from its neighbours, but no item is distinguishable in terms of chairness from its neighbors. So each item in the series is just as much of a chair as its neighbours, and yet the item at one end, the Chippendale, is a chair, unquestionably and absolutely, and the item at the other end is not a chair at all. Put in contemporary techno-speak: a Chippendale chair could be seamlessly morphed into a nondescript lump of wood.
Although, as Schwartz points out, "The principle of equality does not, of course, imply its converse - 'Items that differ in morally relevant respects must be treated differently'", it seems to me to be fairly trivial to prove that this is so. Again, Schwartz: "...if the principle of differential treatment is not recognized by morality, then rocks would require the same moral consideration as people."
These three taken together lead quite quickly to contradiction. One can easily imagine a spectrum of moral situations, at one end of which action A is quite clearly mandated, at the other end of which action A is quite clearly unethical.14 Schwartz asserts that this leads us irrevocably to reject the principle of equality as a deciding moral factor.
My interpretation of this finding is different than Dr. Schwartz's. Although it does seem that perfect morality is beyond reach, some approximation seems possible. On the other hand, this fuels an intuition that the concepts of Right and Wrong are just another arbitrary grouping of a continuous world. I argue that the above argument, rather than showing that morality is impossible, shows that morality is somewhat arbitrary - there is not, and by the above can not, be an absolute justification for any moral division.
Just as in programming and cognitive science, when we as humans are attempting to be moral, we are grouping. Where in programming we group tasks with the intention of facilitating their accomplishment and in cognitive science with the intention of determining how they are accomplished, in ethics we attempt to discern whether they should be accomplished.
The ramifications of this are perhaps less than earth shattering. That morality is subjective has been theorized for many years, so though this perhaps lends an additional bit of weight to that theory, it does not originate it. The main conclusion we may draw from this is a direction to look in for new theories of ethics, should they become desired. Although the words and concepts for more categories than Right and Wrong do not as of yet exist, ethicists may take it as their imperative to develop a more comprehensive set of descriptions for actions.
An appropriate analogy (bizarrely enough) may be found in the Catholic dichotomy between venal and mortal sin. Mortal sin is irredeemable, venal sin is merely... bad. Contemporary American ethics does not include this sort of "wiggle room" in the definition of right and wrong. Much as it pains me personally to say it, perhaps we should take hint from the Catholics.
Where our current vocabulary only includes two categories, an improved paradigm could group actions by morality in a more discerning way. This paradigm might include more of a continuum, or it might be something that we currently cannot even conceive of.
I do not intend to espouse a lack of morality - to say or imply that simply because our system of division has problems we should not strive to be the most moral people we can. My intent is quite the opposite in fact - it seems to me that the mere fact of morality's dependence on our own humanity to give it existence does not make it the less important. But our intuitions of what is right and wrong may be better served by a different paradigm, one that leaves room for the proverbial "shades of gray". A perennial difficulty in discussing ethics is a lack of proper vocabulary to describe ambiguous situations. A new paradigm could go quite some distance to cover this problem.
For the rest of this paper, I will be less careful with how well I support my points. There are a number of things that, though I believe them, and though I wish to discuss them and perhaps convince readers of their truth, I cannot in good conscience say that I have truly robust arguments. Take of them what you will.
Species: A fundamental category of taxonomic classification, ranking after a genus and consisting of organisms capable of interbreeding. - American Heritage Dictionary
There is an island in the Galapagos where birds live. These birds are interesting to us because of their peculiar mating abilities. If you were to walk around the circumference of the island, and every fifty or a hundred feet choose a random bird of a particular type, it would be able to mate with the next bird that you picked. However, were you to choose the very same bird and walk halfway around the island, on the other side you would not find a single bird that the one you chose would be able to mate with.
To restate in a way that may be clearer, there are a whole bunch of birds living on the periphery of an island, and all of them can mate with their neighbors. On the other hand, once you go a certain distance around the island, a non-native bird can no longer find a mate.
The question of how many species of bird there are on the island then becomes a great deal more difficult. If there were only one species of bird, then one's expectation would be that they could all mate with each other regardless of location on the beach. Contrariwise, if we wish to divide the birds into multiple species, the place of division would by necessity place a dividing line between birds that could produce viable offspring.
Lions and Tigers are clearly separate species. They have entirely different habits of hunting; they do not physically resemble each other in any strong way. In fact, although they are physically capable of producing offspring (and even occasionally do so in the wild,) the offspring they produce are themselves incapable of successfully mating. But we can easily imagine a world in which scientists discover an easily ingested drug that renders Ligers and Tions capable of mating with others of their kind. Are Lions and Tigers still then separate species? Does the introduction of this drug change their status, such that they are separate species as of this writing but would not be after the invention and use of the drug?15
This seems to me to be a case of an arbitrary paradigm failing to be of a great deal of use. The paradigm of studying organisms by species began long ago, before any biologists had any appreciation for the interconnectedness of ecosystems. It seems to me that dividing organisms by what they can mate with, while often useful in non-scientific discussions, should give way to a biology paradigm that groups organisms by other relationships that hold between them. This paradigm deceptively leads to the dangerous assumption that individual species can be isolated from their ecosystem and preserved or suppressed.
In this case, my criteria for determining what paradigm is best are political in nature - it is my belief that maintaining ecosystems is a valuable thing to do, and thus we should choose a paradigm that encourages that preservation. Though I would disagree most vituperatively with such an individual, I can imagine a biologist who actively wishes to use some other sort of paradigm based on different priorities.
I think explicit understanding of paradigms is an incredibly valuable thing that many sciences should pay much more attention to. The above cases are only examples - intended to argue that this particular method of describing paradigms is valuable. Every science, and I believe most spheres of human understanding, could benefit from paying greater attention to their paradigmatic systems.
One of the reasons paradigms are important is illustrated by the biology section just above. Methods for understanding exert a pervasive and subtle influence over how we view the science. If that influence is unwatched, it can end up yielding highly undesirable results.
The original reason I (and I imagine Kuhn as well) became interested in this area is much closer to the hearts of teachers than of actual scientists. Whether they are aware of them are not, skilled scientists understand implicitly the paradigms of their field - the only reason they would benefit from a greater appreciation of them is to consider changing them. The situation is quite different from a neophyte's perspective. Fitting new pieces of knowledge into a structure is often very difficult for a new student, particularly if said student is in an environment where all the teacher does is encourage students to memorize facts, with no effort on the part of the teacher to connect them to each other. The field of education could thus benefit enormously from greater attention to paradigms. Additionally, anyone wishing to communicate abstract ideas would no doubt benefit from making clear their groupings.
My final word concerns both the past and future of discussions of paradigms. The title of this paper, thus far unexplained, includes the words "naturalized metaphysics". I view the subject of this paper as the natural successor to traditional metaphysics, though I imagine most metaphysicians would object most heartily. In the same way the contemporary epistemology has moved away from discussions of Truth, and into discussions of rationality, thus becoming naturalized, I think of this subject as an attempt to move the subject of metaphysics from a study of Universals to a study of our understanding of universals.
1 It is possible that there is nothing else, but I do not wish to rule out the possibility of there being more