Python from Wise Guy's Viewpoint

Dirk Thierbach dthierbach at gmx.de
Sun Oct 26 16:52:10 EST 2003


Pascal Costanza <costanza at web.de> wrote:
> Dirk Thierbach wrote:

> Sidenote: The code above has a bug. Here is the correct version:
> 
> (defun f (x)
>   (if (< x 200)
>     (* x 2)
>     (progn
>       (cerror "Type another number"
>               "You have typed a wrong number")
>       (print '>)
>       (f (read)))))
> 
> (Noone spotted this bug before. All transliterations I have seen so far 
> have silently corrected this bug. 

Because all people who did transliterations guessed what the program
does, instead of testing it directly. I don't have a Lisp system here,
so I couldn't try it out.

> It is interesting to note that it probably weren't the type systems
> that corrected it.)

While doing the transliteration, I actually had quite a few attempts
that didn't work, and all of them had type errors. Once the typing
was right, everything worked. (And I had to put the continuation in,
because, as I said, non-local control transfers don't translate
one-to-one.)

>>>>The type system might test too many cases.

> I think you have a restricted view of what "type" means. 

Maybe. It sure would help if you'd tell me your view instead of having
me guess :-) For me, a type is a certain class of values, and a static
type is given by a limited language describing such classes. A
dynamic type is a tag that is associated with a value. Arbitrary
classes of values (like "all reals less than 200") are not a type.

> Here is the same program written in a "type-friendly" way.

I'm not sure what's "type-friendly" about it. It uses a dynamic check
with a mix of dynamic type checking and value checking, yes. (I guess
"(real * 200)" means "a real number below 200", does it?)

> (Again, in standard ANSI Common Lisp. Note that the type is defined
> inside of f and not visible to the outside.)

You do not define any type, you do a dynamic check.

> (defun f (x)
>   (check-type x (real * 200))
>   (* x 2))

[...]
> You have used CPS - that's a nice solution. However, you obviously 
> needed to make the type declaration for f :: Integer -> IO (Integer)

In this case, you're right, but I am not sure if you know the reason
for it :-) (I usually make type annotations for the same reason you
write unit tests, i.e. almost everwhere unless the function is
trivial) So why do I have to make it here? Is the type annotation for
cerror also necessary?

> What you actually did is: you have assigned a "broad" type statically, 
> and then you revert to a manual dynamic check to make the fine-grained 
> distinction.

If you mean by "broad" type the static integer type, yes. I have to
assign some type. If I don't want to restrict it to integers, I assign
a more general type (like the Number type I used in the other example).

Dynamic type checks either can be dropped because static type
checking makes them unnecessary, or they translate to similar dynamic
checks in the other language.

Since you cannot statically verify that a user-supplied value is less
than 200, this check becomes a dynamic check (what else should it
translate to?)

> In both Lisp versions, you have exactly one place where you check the 
> (single) type. 

You have one place where you do the dynamic check, I have one place where
I do the dynamic check. The static type is only there because there
has to be a type. It really doesn't matter which one I use statically.
The static type check does not play any role for the dynamic check.

> A static type system cannot provide this kind of granularity. 

I am sorry, but this is nonsense (and it isn't really useful either).

This is like saying "I don't write unit tests in Lisp, because I have
a single place inside my program where I can check everything that
can go wrong."

> I regard the distinction between dynamic type errors and runtime errors 
> to be an artificial one, and in fact a red herring. I would rather call 
> the former type _exceptions_. 

Fine with me. I would go further and say that without dynamic
type checking, there are in fact only exceptions.

> Both (correct) code snippets I have provided are considerably smaller 
> than your solution and both are more general. And my second solution 
> still provides a way to correct a wrong parameter at runtime at no 
> additional cost. I regard this a feature, not a problem.

I would debate both points, but this is about static typing, not about
comparisons a la "in my language I can write programs that are
two lines shorter than yours."

> P.S.: Please always remember the original setting of this thread. The 
> original original questions was something along the lines of "why on 
> earth would one not want a static type system?" 

The answer to this is of course "You use what you like best. End of
story."

What I wanted to do was to show that the claim "There are things that
are easy to do in a dynamically typed language, but one cannot do them
conveniently in a statically typed language, because they won't
typecheck. Hence, statically typed languages are less expressive" is
wrong in all but very very rare situations. Of course the amount of
convenience with which you can do something varies from language to
language, and with the available libraries, but static typing (if done
properly) is never a show-stopper.

> Yes, you can clearly tell from my postings that I prefer dynamic typing 
> over static typing. 

And that's fine with me. What I am a bit allergic to is making
unjustified general claims about static typing (or any other things,
for that matter) that are just not true.

> But why on earth should anyone want to prove that their own preferred 
> _personal_ style is generally superior to all others?

I don't know. As far as I am concerned, this thread was never about
"superiority", and I said so several times.

- Dirk




More information about the Python-list mailing list