Ketil Malde said:
I keep hearing this, but I guess I fail to understand it. How
"partly-wrong" do you require the program to be?
This conclusion is false. To be "wrong", whether partly or fully,
a program needs to specifically not conform to the requirements that
the programmer gives it. You are assuming that only a completed
program can be right. Let me give a counterexample:
Consider this part of a CL program:
CL-USER(1): (defun foo (x)
(declare (optimize speed (safety 0) (debug 0))
(fixnum x)
)
(bar (the fixnum (1+ x))))
FOO
CL-USER(2):
This is of course not a complete program, because the function BAR is
not yet defined. If I try to run it, it will of course get an error.
But does that require then that I call this a "partly wrong" program?
No, of course not; it is not a program; it is a function, and for my
purposes it is completely correct (I've debugged it already
So what can we do with an incomplete program? Nothing? Well, no. Of
course, we can't run it (or can we?). But even before talking about
running the program, we can at least do some reasoning about it:
1. It seems to have some static type declarations (in a dynamic
langiuage? oh my
.
2. The 1+ operation limits the kinds of operations that would be
acceptable, even in the absence of static type declarations.
3. The declarations can be ignored by the CL implementation, so #2
might indeed come into play. I ensured that the declarations would
be "trusted" in Allegro CL, by declaring high speed and low safety.
4. I can compile this function. Note that I get a warning about the
incompleteness of the program:
CL-USER(2): (compile 'foo)
Warning: While compiling these undefined functions were referenced: BAR.
FOO
NIL
NIL
CL-USER(3):
5. I can disassemble the function. Note that it is a complete
function, despite the fact that BAR is undefined:
CL-USER(3): (disassemble 'foo)
;; disassembly of #<Function FOO>
;; formals: X
;; constant vector:
0: BAR
;; code start: #x406f07ec:
0: 83 c0 04 addl eax,$4
3: 8b 5e 12 movl ebx,[esi+18] ; BAR
6: b1 01 movb cl,$1
8: ff e7 jmp *edi
CL-USER(4):
6. I can _even_ run the program! Yes, I get an error:
CL-USER(4): (foo 10)
Error: attempt to call `BAR' which is an undefined function.
[condition type: UNDEFINED-FUNCTION]
Restart actions (select using :continue):
0: Try calling BAR again.
1: Return a value instead of calling BAR.
2: Try calling a function other than BAR.
3: Setf the symbol-function of BAR and call it again.
4: Return to Top Level (an "abort" restart).
5: Abort entirely from this (lisp) process.
[1] CL-USER(5):
7. But note first that I have many options, including retrying the
call to BAR. What was this call to BAR? I can reason about that as
well, by getting a backtrace:
[1] CL-USER(5): :zoom
Evaluation stack:
(ERROR #<UNDEFINED-FUNCTION @ #x406f3dfa>)
->(BAR 11)
[... EXCL::%EVAL ]
(EVAL (FOO 10))
(TPL:TOP-LEVEL-READ-EVAL-PRINT-LOOP)
(TPL:START-INTERACTIVE-TOP-LEVEL
#<TERMINAL-SIMPLE-STREAM [initial terminal io] fd 0/1 @ #x40120e5a>
#<Function TOP-LEVEL-READ-EVAL-PRINT-LOOP> ...)
[1] CL-USER(6):
8. If I then define BAR, with or without compiling it, and then take
that option to retry the call:
[1] CL-USER(6): (defun bar (x) (1- x))
BAR
[1] CL-USER(7): :cont
10
CL-USER(8):
Hmm, I was able to complete the program dynamically? Who ever heard
of doing that?
During development, I frequently sprinkle my (Haskell) program with
'undefined' and 'error "implement later"' - which then lets me run the
implemented parts until execution hits one of the 'undefined's.
Why do it manually? And what do you do when you've hit the undefined?
Start the program over?
The "cost" of static typing for running an incomplete program is thus
simply that I have to name entities I refer to. I'd argue that this
is good practice anyway, since it's easy to see what remains to be
done.
Good practice, yes, but why not have the language help you to practice
it?