Anthropomorphy in algorithms
When I was 21, I was wondering how to improve programming languages, for I felt that there were certain wearisome obstacles, when formulating an algorithm in MC68000 assembler. It so happens I just did that recently, so we can ponder that as a very simple example.
I was playing around with some self-invented macros yesterday, and eventually that algorithm took on the following form:
main feed %101111,d0
qfeed -1,d1
keep d0,d2
.srch count d1
lsr.l #1,d0
search cc
pstop
double d0
oadd d0
bfall +2
.end rts
where the following macros are included:
keep MACRO
move.l \1,\2
.o\1 SET '\2'
ENDM
oadd MACRO ; old add
IFEQ .o\1-'d0'
IFC \2,''
add.l d0,\1
ELSE
add.l d0,\2
ENDC
ENDC
IFEQ .o\1-'d1'
IFC \2,''
add.l d1,\1
ELSE
add.l d1,\2
ENDC
ENDC
IFEQ .o\1-'d2'
IFC \2,''
add.l d2,\1
ELSE
add.l d2,\2
ENDC
ENDC
IFEQ .o\1-'d3'
IFC \2,''
add.l d3,\1
ELSE
add.l d3,\2
ENDC
ENDC
IFEQ .o\1-'d4'
IFC \2,''
add.l d4,\1
ELSE
add.l d4,\2
ENDC
ENDC
IFEQ .o\1-'d5'
IFC \2,''
add.l d5,\1
ELSE
add.l d5,\2
ENDC
ENDC
IFEQ .o\1-'d6'
IFC \2,''
add.l d6,\1
ELSE
add.l d6,\2
ENDC
ENDC
IFEQ .o\1-'d7'
IFC \2,''
add.l d7,\1
ELSE
add.l d7,\2
ENDC
ENDC
ENDM
feed MACRO
move.l #\1,\2
ENDM
qfeed MACRO ; quick feed
moveq.l #\1,\2
ENDM
adjust MACRO
move.l #\2,\1
ENDM
qadjust MACRO ; quick adjust
moveq.l #\2,\1
ENDM
radjust MACRO ; relative adjust
lea \2(pc),\1
ENDM
track MACRO
move.l \1,(\2)+
ENDM
btrack MACRO ; back track
move.l \1,-(\2)
ENDM
count MACRO
addq.l #1,\1
ENDM
bcount MACRO ; back count
subq.l #1,\1
ENDM
double MACRO
add.l \1,\1
ENDM
astop MACRO ; ante stop
bcs.s .end
ENDM
pstop MACRO ; post stop
beq.s .end
ENDM
search MACRO
IFC \1,'cc'
bcs.s .srch\2
ENDC
IFC \1,'ne'
beq.s .srch\2
ENDC
IFC \1,'cs'
bcc.s .srch\2
ENDC
IFC \1,'eq'
bne.s .srch\2
ENDC
ENDM
bfall MACRO ; back fall
bra.s .srch\1
ENDM
handle MACRO
cmp.l \1,\2
beq.s .case\3
ENDM
shandle MACRO ; short handle
cmp.b \1,\2
beq.s .case\3
ENDM
lhandle MACRO ; lower handle
cmp.w \1,\2
beq.s .case\3
ENDM
bhandle MACRO ; bit handle
btst \1,\2
bne.s .case\3
ENDM
nhandle MACRO ; no bit handle
btst \1,\2
beq.s .case\3
ENDM
catch MACRO
b\1.s .case\2
ENDM
This is of course somewhat pathetic, owing to the fact that you can only equate but not set aliases for registers, but let us concentrate on the general transformation of the algorithm and the purpose of the invented macros.
1. Replacing general notions by notions that contain assertions regarding the use of the object of the operation.
The main problem with assembler programs is the ubiquity of the move command. Much is gained by more specifically pointing out, what the object is being set for, that is by breaking down the move command into adjust, feed, keep and track as done above, where adjust is used, when its first argument is not being changed in the dynamic part of the algorithm, i.e. the variably repeated part, or if the value it is being adjusted to doesn't influence the outcome of the algorithm, in which case we speak of a technical adjustment, and where feed is used otherwise.
2. Creating references to values during the formulation of the algorithm.
When we come across the command count d0, we think d0 is now one more, that is we create a new reference during the execution of the algorithm. This a compiler cannot do, only an interpreter could, but a compiler can do the next best thing and create a new reference during the formulation of the algorithm, in the above example through the SET directive, and oftentimes the order of formulation is the same as the order of execution, although by no means necessarily so, but one can after all see to it that the two agree.
Of course, using a pair of commands like keep and oadd sets you up for some really weird bugs, owing to the possibility of jumping into a code segment with changed settings, but the imperfection of the method is not the issue here, the pursued ideal is, and that is the anthropomorphy of the algorithm.
We define references in real time and we remember references that we have used before, and when we want to single out a specific memory, we again define a reference in real time: this memory, this association is the old association. And hence we expect a computer language to understand these kind of declarations and the keep-oadd-pair exemplifies what that means for MC68000 assembler.
3. Bundling several commands in a single one.
This case really is a special case of the previous one, namely when the referenced value is a command. This too, we declare in real time, but here we hardly ever change our declarations. So whenever we want to do this, we can simply define another macro.
Conclusion.
A perfectly anthropomorphic programming language must be processed by an interpreter. It has to allow the definition of rules for the further use of the objects of its commands and control the observation of those rules during the processing of its commands, for instance make sure that the keep command really keeps the old value until a designated time or that adjust and feed are used properly or that astop and pstop occur, when they're supposed to (to claim that that is equivalent with checking the carry, respectively the zero flag is a bit audacious, of course, yet probably in some 98% of all cases true). The object of these jumps is the pc, the program counter, for which the defined rules have to hold.
Commenting should not be necessary, all truly relevant comments should be contained in the notions of the commands of the programming language.
And if that is achieved, we have found a formalism that captures our thoughts on the formulation of algorithms. This is a point worth noting:
I was playing around with some self-invented macros yesterday, and eventually that algorithm took on the following form:
main feed %101111,d0
qfeed -1,d1
keep d0,d2
.srch count d1
lsr.l #1,d0
search cc
pstop
double d0
oadd d0
bfall +2
.end rts
where the following macros are included:
keep MACRO
move.l \1,\2
.o\1 SET '\2'
ENDM
oadd MACRO ; old add
IFEQ .o\1-'d0'
IFC \2,''
add.l d0,\1
ELSE
add.l d0,\2
ENDC
ENDC
IFEQ .o\1-'d1'
IFC \2,''
add.l d1,\1
ELSE
add.l d1,\2
ENDC
ENDC
IFEQ .o\1-'d2'
IFC \2,''
add.l d2,\1
ELSE
add.l d2,\2
ENDC
ENDC
IFEQ .o\1-'d3'
IFC \2,''
add.l d3,\1
ELSE
add.l d3,\2
ENDC
ENDC
IFEQ .o\1-'d4'
IFC \2,''
add.l d4,\1
ELSE
add.l d4,\2
ENDC
ENDC
IFEQ .o\1-'d5'
IFC \2,''
add.l d5,\1
ELSE
add.l d5,\2
ENDC
ENDC
IFEQ .o\1-'d6'
IFC \2,''
add.l d6,\1
ELSE
add.l d6,\2
ENDC
ENDC
IFEQ .o\1-'d7'
IFC \2,''
add.l d7,\1
ELSE
add.l d7,\2
ENDC
ENDC
ENDM
feed MACRO
move.l #\1,\2
ENDM
qfeed MACRO ; quick feed
moveq.l #\1,\2
ENDM
adjust MACRO
move.l #\2,\1
ENDM
qadjust MACRO ; quick adjust
moveq.l #\2,\1
ENDM
radjust MACRO ; relative adjust
lea \2(pc),\1
ENDM
track MACRO
move.l \1,(\2)+
ENDM
btrack MACRO ; back track
move.l \1,-(\2)
ENDM
count MACRO
addq.l #1,\1
ENDM
bcount MACRO ; back count
subq.l #1,\1
ENDM
double MACRO
add.l \1,\1
ENDM
astop MACRO ; ante stop
bcs.s .end
ENDM
pstop MACRO ; post stop
beq.s .end
ENDM
search MACRO
IFC \1,'cc'
bcs.s .srch\2
ENDC
IFC \1,'ne'
beq.s .srch\2
ENDC
IFC \1,'cs'
bcc.s .srch\2
ENDC
IFC \1,'eq'
bne.s .srch\2
ENDC
ENDM
bfall MACRO ; back fall
bra.s .srch\1
ENDM
handle MACRO
cmp.l \1,\2
beq.s .case\3
ENDM
shandle MACRO ; short handle
cmp.b \1,\2
beq.s .case\3
ENDM
lhandle MACRO ; lower handle
cmp.w \1,\2
beq.s .case\3
ENDM
bhandle MACRO ; bit handle
btst \1,\2
bne.s .case\3
ENDM
nhandle MACRO ; no bit handle
btst \1,\2
beq.s .case\3
ENDM
catch MACRO
b\1.s .case\2
ENDM
This is of course somewhat pathetic, owing to the fact that you can only equate but not set aliases for registers, but let us concentrate on the general transformation of the algorithm and the purpose of the invented macros.
1. Replacing general notions by notions that contain assertions regarding the use of the object of the operation.
The main problem with assembler programs is the ubiquity of the move command. Much is gained by more specifically pointing out, what the object is being set for, that is by breaking down the move command into adjust, feed, keep and track as done above, where adjust is used, when its first argument is not being changed in the dynamic part of the algorithm, i.e. the variably repeated part, or if the value it is being adjusted to doesn't influence the outcome of the algorithm, in which case we speak of a technical adjustment, and where feed is used otherwise.
2. Creating references to values during the formulation of the algorithm.
When we come across the command count d0, we think d0 is now one more, that is we create a new reference during the execution of the algorithm. This a compiler cannot do, only an interpreter could, but a compiler can do the next best thing and create a new reference during the formulation of the algorithm, in the above example through the SET directive, and oftentimes the order of formulation is the same as the order of execution, although by no means necessarily so, but one can after all see to it that the two agree.
Of course, using a pair of commands like keep and oadd sets you up for some really weird bugs, owing to the possibility of jumping into a code segment with changed settings, but the imperfection of the method is not the issue here, the pursued ideal is, and that is the anthropomorphy of the algorithm.
We define references in real time and we remember references that we have used before, and when we want to single out a specific memory, we again define a reference in real time: this memory, this association is the old association. And hence we expect a computer language to understand these kind of declarations and the keep-oadd-pair exemplifies what that means for MC68000 assembler.
3. Bundling several commands in a single one.
This case really is a special case of the previous one, namely when the referenced value is a command. This too, we declare in real time, but here we hardly ever change our declarations. So whenever we want to do this, we can simply define another macro.
Conclusion.
A perfectly anthropomorphic programming language must be processed by an interpreter. It has to allow the definition of rules for the further use of the objects of its commands and control the observation of those rules during the processing of its commands, for instance make sure that the keep command really keeps the old value until a designated time or that adjust and feed are used properly or that astop and pstop occur, when they're supposed to (to claim that that is equivalent with checking the carry, respectively the zero flag is a bit audacious, of course, yet probably in some 98% of all cases true). The object of these jumps is the pc, the program counter, for which the defined rules have to hold.
Commenting should not be necessary, all truly relevant comments should be contained in the notions of the commands of the programming language.
And if that is achieved, we have found a formalism that captures our thoughts on the formulation of algorithms. This is a point worth noting:
We don't have to teach a computer how to understand us, we only have to remove the obstacles on the way of the translation of our thoughts into a form that a computer can process by choosing a form for which there are no such obstacles.
Labels: 22, computer design, formalisierung, institutionen, intelligenz, persönliches, ἰδέα, φιλοσοφία