Path: utzoo!utgpu!news-server.csri.toronto.edu!rpi!usc!cs.utexas.edu!bcm!rice!bbc From: bbc@rice.edu (Benjamin Chase) Newsgroups: comp.lang.misc Subject: Re: Dynamic typing (part 3) Message-ID: Date: 8 Apr 91 16:55:05 GMT References: <1593@optima.cs.arizona.edu> Sender: news@rice.edu (News) Reply-To: Benjamin Chase Distribution: na Organization: Center for Research on Parallel Computations Lines: 111 In-Reply-To: gudeman@cs.arizona.edu's message of 8 Apr 91 00:41:22 GMT gudeman@cs.arizona.edu (David Gudeman) writes: > OK, try this. In mathematics you don't have to describe the types > of objects unless the type is important. In statically typed > languages, you have to declare the type whether it has any relevance > or not (or fix it at compile time whether it is really knowable or > not). As we shall see below, delaring the type may not be quite as painful as you suggest. > In mathematics or dynamically typed languages you can describe the > concept of a set or a sequence, and operations over those data > structures, without refering to the "type" of elements. For example > in math you could say > If f is a function and s is a sequence, then map(f,s) is the > sequence t such that for all i . t[i] = f(s[i]). > In a Icon (a dynamically typed language) you could define > procedure map(f,s) > local t,i > t := list(*s) > every i := 1 to *s do t[i] := f(s[i]) > return t > end > Try to write this function in a statically typed language so that it > has all the generality of the math and Icon versions. I think you can write "map" in Russell, which is statically typed. My Russell is a little rusty, so I could be wrong. There might be a hitch with the declaring the result type of "map"; I'm not really certain either way. This works by using a different notion of types and polymorphism than you were probably expecting... The type declarations for "map" look sort of parameterized in Russell. Informally, they'd run something like map(f:S->T; s:list of S) : list of T; S has {V} /* value of (ie. rvalue of an lvalue, basically) */ T has {V,:=} /* value of, assignment operator */ { } I'm certain that I've got the syntax wrong. Anyhow, the gist is that I require the types S (type of elements of s) and T (types of elements of t) to support certain operations. This definitely has an o-o feel to it, yes, and it might be helpful for purposes of your comprehension to rephrase it as "classes S and T must support certain methods, named V and :=". > ... Furthermore, when you do use something like a type declaration > in mathematics, it is to disambiguate or add clarify, and the nature > of the declarations reflects that. In statically typed programming > languages the purpose of the types is to let the compiler generate > better code, and the nature of the declarations reflects that. Such an efficiency-minded purpose is not so apparent in Russell. I found the feel of type declarations for a polymorphic function to be more of a flavor of "to use the function, your argument types must support the following operations: ...". That seemed like a very natural requirement to me, one that would hold for most languages, either at compile time or at run time. I liked it from a software-engineering aspect, too. > I only have to show one example of "dynamic typing" in mathematics > to show that mathematics is not "statically typed" in the universal > sense of statically typed programming languages (and I did so > above). Careful. You may have instead shown a narrow view of what is actually possible. Now, having said all this, I will say that I am not an unflagging fan of static typing nor of Russell. There are hard problems in Russell regarding arrays, to which I never quite found a good solution. I recall it being difficult to write a completely general-purpose matrix math package, without having explicit (ie. run-time) checks that the sizes of arrays matched appropriately. In particular, if you want to code up a matrix multiply operation: mult(A: array[m,n] of T; B: array[o,p] of T) : array [m,p] of T ... { /* Ooops, need to basically do a run-time type check */ if (n != o) { print("Hey, these arrays can't be multiplied!"); print("Their sizes don't match!"); } } (Of course, Russell's polymorphism over the element type T does work like a champ. Oh, yeah, and it supports operator overloading and precendence, so the above function was actually called "*", and you'd say "A*B", like you'd want.) So, I guess I'd like something like Russell, but maybe with a dynamically typed loophole. I'd tell the compiler what I know, or what I care to let it know, about my code. It would figure out what it can (and perhaps informs me of what is left unchecked), and then puts off the rest of the checking until run-time. I wish there had been a way for the above run-time array size check to be done automagically (I suppose you'd declare the arrays mxn and nxp then, and unify the two n's?), and for the handling of such an error to be done in some standard fashion (Modula-3 style raises/exceptions?). -- Ben Chase , Rice University, Houston, Texas