Lots of stuff to do

Computers, Programming Languages and Operating Systems

Thursday, July 20, 2006

Names, again

Ah, names, they have fascinated people for ages (literally). In the past, some people believed that knowing the true name of something would give you power over it. In a sense, that is what we would like to have. We name our files, we name our resources, we name our objects. We name because we need to refer. So what are the ways that we can name something?

I am really confused about names. Maybe because I cant think well enough, but obviously names are first class things - you can have a name inside a file, you can have a list of names in a file, you can even have a database of names in a file. However, when we "dereference" a name, we get something that relates to the name. Interesting enough, a name->information (i.e. loading a file) is a function, since we would not like to have multiple information; while information->name (i.e. naming a file) is not a function, since the same information might be under many names.

For convention, we shall use N to be a name, which can be of any type (usually we think of it as a string like "this is a name.txt"). X shall be the information held in the name. Bold type is functions
  1. Dictionary D: this is when we have a function that dereferences a name - i.e. takes a name and then returns the actual information. X = D N .
  2. Relational Dictionary R: this is when we relatively define a name N with respect to another name (the target T). So we get something like X = (R T) N .
    An example is ownership - where we say something like "Jack's bank account". We get (R "Jack") is a function that takes a name and gives a dictionary function that will give you Jack's possessions. So (R "Jack") "Bank Account" is Jack's Bank Account information
    This can be either seen as R being a curriable function or a second order function. Dunno anything about this type of stuff though (research research ^^). Interestingly enough, currying seems like a constraint on a function, rather than generating a new function.
    We can also probably make a dictionary file, such that D N = (R d) N. This means something like a dictionary is just a relational dictionary applied on a dictionary file. Notice that our English vocabulary is just a dictionary, with an (arbitrary defined by the English speaking population) dictionary.
  3. Constructive Approach: this is when we say how the information is constructed - i.e. as X = F Argument1 Argument2... . For example if we have original file name "Bank Account", and we added a cashflow of $10, we might represent this as: X = AddCashFlow "Bank Account" "+10". Obviously this is no different really, than a dictionary, except it can take many inputs, and it is not a dictionary lookup function. On a deeper level, this is really just a relational dictionary - we can envisage any function which takes many arguments a1 a2 a3... as X = ((((R a1) a2) a3) ... an). Darn, nothing new here.
    However, this implies that we can structure the entire file system around a log of every function that has ever been applied (assuming the function is deterministic. If not, we might have to memoize the results - e.g downloading a file off the internet would have to be cached). Brain over load here.
  4. Pure name: this is when N = X, and it is our target. So we have our original name X1, and we under go repetitive transformation and eventually get to a pure name, which is basically the information.
If any of this seems slightly reminiscent, this is because it seems like by considering naming information, we have simply returned to functional languages. Makes you wonder what a functional OS would be like (pun intended ^^) ^^;

Wednesday, July 19, 2006

Character Model v1





Well, here is my first version of a character model. Basically, this is what I envision a person (standing alone, not in context with anythng else) would be like. This is almost ripped off a textbook for artificial intelligence, except it has been modified to represent a human, rather than an emotionless intelligent machine.

Sunday, July 09, 2006

Time to Save - What are Files?

Files as time-evolvers and Orthogonal persistence
If you think about it, all a file does is sit there. It does nothing. A file is there to allow you to save things between visits, and while it sits there, it does nothing. No wonder people are suggesting to use orthogonal persistance to replace files. What does that mean? Well, orthogonal persistance is two things:
  1. persistance - where all information will persist, even after you close the computer
  2. orthogonal - where the programmer does not have to do any special coding to take advantage of this
So that would make an operating system not have a close button for programs, and programs not have a save button (although they might have a "version" button to allow you to create versions such as final, draft, editing etc). Why? Well, when you close a program, the OS will automatically "remember" the program for you, and when you restart the program, you get everything that was ever opened. So that way, you never actually start a program, you just unfreeze it, or freeze it. And the programmer has the ease of not actually moving a muscle to get this added bonus - the operating system gives you the orthogonal bit too. In fact, tunes.org wiki says that "IBM evaluated overhead of explicit fetching and storing of data to 30% of total program code, not taking into account check, conversion, and recovery of data".

Of course, this might get a little silly if you have, say, a thousand documents, and you dont actually want to mess around with the other nine hundred that are not important to you at the moment. A more serious problem, it is the difficulty of making such a system both stable and efficient. The efficiency trouble is easy to see - we might end up loading what is not required, or loading memory that contains uninitalised garbage. The stability is a bit less obvious - it is caused by the fact that the context of the computer has changed. That is like freezing an accountant in the 1950s and then unfreezing them now in 2005 and expecting them to work properly. They will not understand their context - names might have changed, technologies will have changed, people have changed, interfaces and more have changed. This would lead to a very unstable accountant, or program for that matter.

We can solve these problems if we tried hard enough. For example, we can make environments where all contexts are essentially constant. However, that is too hard for me to discuss ^^

Files as naked information
Well, on anothe hand, lets see what exactly we put into data structures. Here is a quick list
  1. We have plain information into what we call data
  2. We have information describing what that plain information is into what we call metadata
  3. We have information describing relationships between information into what we call pointers
  4. We have juxtapositions (non-alike information put together) of these information into what we call types
  5. We have concatenations (alike information put together) into what we call arrays or lists
  6. We have juxtapositions of these types with functions into what we call objects
  7. We have juxtapositions of related information and we call it a file when it is on a hdd
  8. We have juxtapositions of files onto what we call a file system.
So there, we have a nice slice-of-life of information in its local context - the array, type, object, file, file system. Of course, this goes well with my idea of viewers, where you simply give the information (whether it be files or otherwise), and metainformation about this information, and the computer can generate an interface. There has been an interesting movement in the smalltalk community to create a visual information display system (to my limited reading of it) called morphic. There has also been another interesting movement called naked objects, along similar lines.

The next step now that we have a nice street directory of information, is to create names for the streets of information. That is, how do we assign names to this endless jungle of information that is apparently there. Well, the current way is to make a file path. An analogy would be that this file path is a street address. To access what is inside the house, you have to look up who owns the house (i.e. what program owns the filetype), ring them up (execute the program), and then ask the owner to access the information inside it (navigate the program). It would be better if what happened was that the owner gave you a map of the house (programs extract or find metadata about the information), and allow you direct access via another resource locator.

In this way, the arbitary limits of files will be removed, and replaced with what I call transform points. For example, suppose you wanted to access your music. All your music is located inside a file called music. When you open that file, there is a transform point, where a program comes along and reads the file, and gives you a description of the file. Without opening another program, your file explorer has entered into inside the file. Sort of like having a hardlink to a folder, but much more powerful. Sorry about the bad wording of this paragraph. Needs reworking.

What else can I do wrong?

There are so many ways to make wrong decisions in designing software that it seems like often there is never going to be an easy and clean way to implement things. Large scale mistakes can cause lots of the code to become unreusable, inflexible, and eventually legacy code that has to be rewritten. Here some of the antipatterns that we must avoid. (sources include wikipedia, c2.com wiki, and other sites). Note, these antipatterns are my interpertation, and thus may not match exactly with other ways of saying it. I recommend wikipedia or c2 for more information.
  • copy and paste programming. errors and bad programming mechanicsms get propagated to the copied code. We should make the concept of copy and paste either impossible, or make a mechanism, where copied code is automatically segmented from the main program and turned into a more general function outside of the original function, in other words, languages where auto-refactor is of low complexity. This will be easy if the programming language is designed to work with "semi-anonymous" name references. Functional languages are good, and so is stuff like subtext which acutally requires first class copy and paste.
  • Abstraction Inversion, where in short, the uses of the components are reduced at the cost of complicating the use of the components. A tradeoff is made between the complexity of the interface, and using the components. A more complex interface will allow fast and easy doing of intent but at the cost of a huge learning curve, while a simple interface is easy to learn and flexible, but forces the user to use the interface many times and at a cost to the speed. A solution is to make all interfaces simple, and make all complex interfaces just scripts - in other words, doing a complex action is through a script that allows complex things to be broken down into the simple steps.
  • Gas factory, where the design is unnecessarily complex. One way to force away this trend is to force an architecture onto the operating system and all programs. While restrictive and possibly unefficient, the resulting harmony and clear design may offer human benefits - i.e. to users.
  • Ball of mud, where the design may be ok in areas, but the way they are joined together is complicated and has no real unifying point.
  • Interface bloat, where the interface is way too complicated to make. This can be solved if we separate the interface from the actual stuff that the interface does, allowing us to design any interface we want

Antilazyness - When Precalculation and Prefetching Counts

If you can look into the seeds of time, and say which grain will grow and which will not, speak then unto me. --Shakespeare

If only computers were smart enough to precompute what information you could possibly ask for, and then store it all. With today's memory costs decreasing this might even be possible.

Possible places to predict where you can read info:

  • Internet: you can predownload files before they are needed. However that costs extra money ^^
  • Database: databases can be recorded (i.e. store only the functions applied to database this is O(1) insertion time, O(n) lookup time). In the spare time, it might consolidate these logs into the actual database file
  • Clocks: have a clock file that the clock program reads off (i.e. the system writes the time to a "file", all programs that need access to the time merely read off that file - file probably should belong in ram). In spare time, make a list of times ^^
  • Lazy operations: any operation that is put on lazy (i.e. you do not evaluate the function until you apply a viewer on the information) will work in spare time.

Wednesday, July 05, 2006

Speeding up small programs

The use of small functions that do specific jobs well is an important way to make flexibility. However, one of the troubles of working with such small programs the efficiency loss that is accompanied by having pipelines that write to memory. There are a few sources:
  1. Program instantiation is much slower than function calls - the OS has to create new thread space, allocate/page memory
  2. Programs need to pass information, and the only way would be to either write to a shared memory space, or something that emulates such a space (e.g. a temporary file etc)
  3. Programs might need specific information, and discard the rest of the information, thus making it a waste to compute all the information.
  4. Programs that run on a list of inputs would need to be instantiated for each element of the list
  5. Programs might be called several times for the same information, and optimisations that rely on repeated program calls cannot be made
  6. Programs that I am proposing do not do any variety of input/output. They are very much just processing boxes hexagon architecture. They will all have to use the same input/output "ports".
The reason that normal programs do not suffer these problems are:
  1. Function calls are fast, as long as they are to the same block of memory.
  2. Functions that are compiled can use registers to store information. Reading/writing to register is faster than to memory, and avoids caching problems.
  3. Functions can have callback hooks
  4. Functions can be designed to have input-output loops. Also, the coder could recognise which variables need to be reset to starting values.
  5. Functions results can be stored, and the way the function responds can be individually optimised
  6. Functions can do anything - i.e. they are not restricted in any way. That means that they can be optimised for whatever they are doing.
My idea for making functions work faster is to allow:
  1. work at a bytecode level
  2. lazyness, ranging from extreme to novice (explained later)
  3. fusion
  4. stitching
The primary way that the programs/functions are all written at a byte code level. This of course offers us the benefits of portability - like that of java. However, the type of bytecode I am interested in is at a slightly higher level - it must allow lazy bytecoding, and fusion to occur. This leads to our concept of lazyness, that is, you don't do anything until you need to. Lazyness will basically work by allowing whatever needs the information to query the function for information, rather than execute the function. There are several levels of lazyness, as some functions can be made to process only a little information to answer a specific query, while others need to process all the information before returning results. The levels of lazyness are:
  1. a program that takes a file, and gives a file of the same structure, but with different information in some places, and you know exactly what places the information will be different in. for example a program that changes an element in an array.
  2. you can ask for a specific set of information at (almost) no overhead cost. for example a program that will tell you the value of an element in an array.
  3. you can ask for information in a linear way. for example a program that will give running sums of an array, a fibonacci function.
  4. you can only ask for all the information. for example floyd's algorithm.
Now depending on the level of lazyness, it is possible to fuse the bytecode together (remember, we are using high level bytecode), and then turn this bytecode into some form of native assembler code. This would thus give you what appears to be a fully optimised program composed of many small blocks that may be slow if not for lazyness.

As to how this will fix many of our problems, in the same order:
  1. Program are stitched together in one space
  2. Shared information are handled via lazyness, and since we are using information querying, it is possible to store the results of each query in registers or in smaller temporary memory areas
  3. Lazyness removes problems of discarded information. What does not need to be discarded will not
  4. It is possible for the bytecode to be high level enough (like J functions are), to be automatically ranked, and thus work on lists without extra manipulation
  5. Memoising bytecode? (doubtful)
Basically one interesting idea is that we can have the ideal world, where everything is reflected and all, and then we write optimised code for special cases, and in most cases where we do not use any of the reflecive power of the computer, the optimised code is run instead of the reflected bytecode etc. So we do not loose out from our flexibility-speed tradeoff.

Here is an interesting similar idea to mine. They use a functional language with precompiled functions. Type checking is done and then the precompiled programs are literally stitched next to each other. This provides the best of both worlds. It is called Esther. It is fast because it involves no interpretation nor compilation. That makes it possibly faster than c in terms of compiling (^^), and faster than hugs in terms of running (^^).

Tuesday, July 04, 2006

Operating Systems as Authorship Systems

hmm... I have no real intent to make this post larger

Authorship as Recombination and Synthesis

One rather empirical view that I hold, is that the majority of Authorship is merely an act of recombination and synthesis - very little is original. That is, we take elements from our memory, and recombine in increasingly complex and finer-grained ways.

There are many granualities of recombination. At the rudimentary level, we take large chunks of what we remember and do some small level of combining. As we get increasingly granulated, we start using more elementary building blocks for the process of authorship.

There are also many levels of recombination. This depends on the insight, which is the ability to see deeper into constructs - learning not just the surface structures, such as paragraphs and phrases, but into the techniques, and even the intent, that construct other works of authorship. While this might be subconscious, it is this insight that gives us what seems to be creativity.

I am trying to express that we can learn to be more adept at a technique, or more meta in our techniques. By "more meta" I mean learning meta-techniques, meta-meta-techniques, and so forth to a deeper level. Another way to express this is that we can deepen our insight, or widen our knowledge base, upon which all recombination and synthesis lie.

The key to my idea is that if we can write an operating system that is deeper rather than wider, we can generate more powerful actions, and actions that allow us to not descend from our intent into the practical component of our work. Instead, such a deeper operating system will allow us to express our ideas at the intent level, and allow the depth of stored experience to ensure the computer does the appropriate operations.

Antiauthorship and Readership

Roughly speaking, readership is the inverse of authorship. It is the process whereby a creation (human or otherwise) is examined and then understood, explored and synthesised into the reader's mind. It is taking the linear structure, and then finding out what techniques were used. And then taking these sets of techniques and finally finding out the intent that was behind these techniques. Of course, that is where subjectivity will become important, as we are searching for clues of techniques and then reconstructing the techniques used, and we are searching for clues of the intent, and then reconstructing these intents. What we are doing is similar to looking at the outside of a building and guessing the supporting structures or the framework that is within.

In some way, literary theory is an attempt to bring a more orderly discussion into the ways that the interpretation of a text is created. However, it is important to notice that because we are given what is, in essence, the outside of a complex object (known by artsy fartsy people as a text), whatever we do in the interpretation will only provide distorted revaluations of text in a context that might be widely diverging from the original - reader response theory. The paradox is that in many ways the internal workings of a text are no longer relevant, and yet the surface appearance still matters in different contexts creating a metaphorical "death of the author", where the author as the absolute source of the text is replaced with the reader. In this sense, it is the fact that we can reinterpret a text that gives rise, in some ways, to the overall complexity of a text, as since it is a "three dimensional" structure, and we can take many "slice" or "angles of view" of the text.

In some other way, code reading techniques are also an attempt to bringing a more orderly process to interpreting and understanding the essential architecture underneath a software collection. Now these complex objects (known by scisy fartsy people as code/oop), are much like the texts of the literary kind. In many ways, old functions may be used for totally different things, as provided through abstraction, which allows us to reuse the same code, even in widely different contexts. The "death of the author" paradox now reflects the process of creating code libraries that provide services that might never even have been imagined by the author.

Authorship

Authorship is a funny thing. There are so many types of authorship, yet among them, there is no easy type of authorship. Authorship includes writing novels, writing non-fiction, writing computer programs, using martial art stances, playing chess and even conversations, yet they are all complex actions. Authorship, as I define it, is the process of creating something for a purpose. The reason it is complex is because we want to create something good. And so, a lot of thought has to go into it. This thinking is not the usual linear thinking we are accustomed to using, but a more complicated non-linear process, that takes into account a diverse range of information and tries to optimise a diverse range of criteria.

Yet in spite of this non-linear source of material, the end product, whether a novel or a chess game, will always be just a linear result. A novel will simply becomes some words tied together on a piece of paper. A program will eventually turn into a set of commands and names listed out. An idea invariably becomes written out and a game of chess will end up as a short script of moves. But what is below this trivial surface show of words, moves or stances, is the essential motions that are often unseen, but are critical to the success of the creation.

The starting of the process is intent. Intent is the purpose for even creating. This intent comes in layers - consider my intent to write this blog article. The first layer of intent is to express this idea. But beyond this intent, I also wish to interest you, to make you want to read more (the success of which is debatable), to make my ideas well known. At an even more fundamental level, it is my intent to just express anything. Not only does this intent come in layers, but it comes in shards - I may have many intents that are manifest in different ways throughout the article. The slightly formal tone is used to create a sense of knowledge, the conversational tone is used to engage. It is here that we are limited by the originality of our concepts.

The next level is the skill used to move the intent into plans. This skill determines how I convert the complex layer of intent, full of its internal contradictions, mutual exclusions and multiple aims into something that is essentially linear. It is also how I leverage my knowledge to best achieve my intent. In terms of computer programs, this step is the process of algorithm creation which is a balance between time-space complexities, between accuracy-complexity. In terms of conversations, we must decide on a presentation, on an ordering, on how to present seemingly objective facts to hide the true subjectivity of anything. It is here that we are limited by the granuality that we are able to recombine and synthesise techniques.

The final level is the carrying out, the implementation, where we take this plan and we realize it. It is here where all the physical limitations and problems that come with having an imperfect world comes into play.

The Power of Names

When we have some object, we always give it a name. Words such as books, cars, planes, people, tree, they are all names for a concept, rather than a actual object. We understand that "car", "a car", "that car"and "the car" to mean different things, the first is an idea of something with four wheels, the second is any one instance of a car, while the other two examples are instances of a car that is specified via relationships.

Diverging a little, in computing, what we are manipulating can be split into two types - math, and referencing. When we do something, all we are really doing is:
  1. select what we are doing it to (e.g. this bank statement, this picture, this x)
  2. select what we are doing (e.g. adding a bill, bluring a picture, function y)
Sure there are control flow structures (if, while, for...), and other more esoteric structures (map, fold, expand...), and there are data structures (structs, classes, types...), but they are distractions away from the point. They are part of the transformation between intent and the realised computations. While the creation of what lies underneath the bonnet is important to a computer, the central goal is to move the authorship away from being the dominant factor, and replace it with the intent. The intent of an action is simply to carry out that action on some information. That is why naming is so important, because we need to be able to refer to something precisely.

Saturday, July 01, 2006

Too much to Viewer

One of my key ideas is the generalised universal viewer. What it does is very simple. It views things. It views anything.
  • If you give it text, it will view text.
  • If you give it pictures, it will view pictures.
  • If you give it sound it will view sound through speakers.
So far so good, but how about something more complicated? Well, if you consider it, the generic system output terminals must interface with our human sensory perceptions. That is, the five (main) senses
  1. Sight
  2. Sound
  3. Touch
  4. Taste
  5. Smell
Next, we have to consider what we can realistically manipulate - we cannot manipulate smell, taste or touch with a simple physical instrument. That means that we are limited to sight and sound.

Next, we have to consider what types of sight and sound we can get:
  • sight: 3d, 2d, 1d, 1+1d (i.e. lines that change length) 2+1d (i.e. animation/movies), 3+1d (i.e. moving 3d images)
  • sound: 1d (i.e. pure note), 1+1d (i.e. music)
Finally, we have to consider what instrumentation we have currently:
  • monitors: 2d, projected 3d
  • speakers: 1d, 1+1d
To facilitate the +1 dimension (time) or the 3d, we can have extraction functions that take a 3d or a 2+1d or a 3+1d and then extracts a 2d slice from it. So we have a 2+1d projector, which takes a slice from a movie. We have a 3d projector, which takes a view from a 3d scene. Since our systems should be all lazy, it is possible to get away with only doing what is necessary to display what we are interested in.

Now, given a certain amount of information, which we want to display, the easiest way would be to convert it all to text, and then display it as text. At a more complicated level, we would like to be able create a view of this information more graphically. What we can do is to create segments in the information, and then render each component, and then juxtapose them. In fact, the juxtapose meta-container is only one of a set of useful meta-containers:
  • Graphs - given a set of relationships and a set of entities, we can render the entities, and then draw in the relationships as a graph - use when there are explicit relationships
  • Juxtapose - given some information, you specify where to place each element in separate metadata, that might reside as a file, or be generated from the information, and even be edited at run by a user, such as via dragging and dropping - use when there are implicit relationships
  • Tree - given a tree, you can display it like in the form of trees, or sets, or zoomable interfaces - use when there are relationships of ownership
  • Lists - given a list, we can turn it into a horizontal or vertical list. If you make a vertical list of horizontal lists, then you get a table for free - use when order is important
  • Grid - given some elements, the grid display will automatically arrange it into a grid to maximise the size of each grid tile. this can be used for toolbars and the such - use when order is unimportant
So by using these meta-containers, you should be able to specify roughly whatever types of display you would like. If the use requires a more complicated view, then it is possible to implement a custom viewer, however, usually a combination of viewers and metaviewers, it is possible to render anything.

See the idea of Morphic, which is an addon to Smalltalk. (recently discovered these interesting ideas)