gudeman@cs.arizona.edu (David Gudeman) (03/06/91)
In article <1991Mar5.182130.15091@engage.enet.dec.com> grier@marx.enet.dec.com writes:
] Sorry for the confusion, your "definition" wasn't clear about what
]you meant.
The term "weak typing" has been used for this feature, but it has also
been said that Pascal and C are weakly typed languages, so I didn't
want to used that word. The word "untyped" has also been used, but I
don't like that term for two reasons. First, it tends to confuse
people when you say "Icon is an untyped language. It has ten
different types...". People assume "untyped" means that a language
has no types, but it really just means that syntactic elements don't
have types. Another reason I don't like the term "untyped" is that it
implies a sort of scale: (strongly typed ... weakly typed ... untyped)
and this is deceptive. Weakly typed languages (when they are
distinguished from untyped languages) still have this notion that you
can assign types to identifiers and other (syntactic) expressions.
Languages with runtime polymorphism (or runtime typing) do not have
this concept at all.
] Over the weekend, I was thinking about it and realized
]that I might have misinterpreted you. I'd never seen a definition
]of anything called "runtime polymorphism" before, and you didn't
]include any additional information or examples which made what you
]meant clear.
Well, my definition could have been clearer. I'll make the excuse
that everyone I talk to around here has enough experience with these
sorts of languages that a quick explanation is enough to let them know
what I mean. I didn't make allowance for different "programming
language cultures". I used the word "polymorphism" to mean "multiple
types for the same expression", not "multiple functions with the same
name". However, the term has enough baggage associated with it that
it was a poor choice. I'm going to arbitrarily change my term...
First:
Static typing: a programming language feature where identifiers and
other syntactic expressions are assigned types based on information
that is present in the text of the program -- usually involving
variable declarations. Functions and operators always assume that
the values passed to them are the expected type. Strong typing and
weak typing are relative terms that describe the amount of
information available from the text of the program.
Now instead of "runtime polymorphism":
Dynamic typing: a programming language feature where identifiers and
other syntactic elements cannot be assigned types. Any expression
may (in principle) produce a value of any type when it is evaluated.
Functions and operators must (in principle) always examine the types
of their arguments and do something reasonable for any possible type
(where "something reasonable" includes the possibility of aborting
the program with an error message).
] Your definition of "runtime polymorphism" doesn't really make
]sense in a type-safe system. so clearly what you're talking about
]can only occur in a weakly typed language
Although my suggestion in Part 2 does give a language with dynamic
typing that is type safe in the following sense: syntactic expressions
can be assigned types; and in an expression f(expr) where expr has
type top and f expects some fixed type T, it is guaranteed at runtime
that either (1) f is called with an argument of the correct type, or
(2) a runtime error occurs. This is just as "type safe" as if expr
was a tagged union and you had to explicitly test the type of the
union before calling f. It's just that the test is built in to the
language.
--
David Gudeman
gudeman@cs.arizona.edu
noao!arizona!gudeman
grier@marx.enet.dec.com (03/06/91)
In article <584@coatimundi.cs.arizona.edu>, gudeman@cs.arizona.edu (David Gudeman) writes: |> |> The term "weak typing" has been used for this feature, but it has |> also |> been said that Pascal and C are weakly typed languages, so I didn't |> want to used that word. That's interesting. I'd certainly agree that C is weakly typed, but with Pascal requiring name compatibilty of types (rather than structural, as in C and Modula-2,) I'd put it in the "strong typing" category. (Strong typing is not necessarily "good." Pascal's a neat language, but it lacks tons of features for modern software construction...) |> <stuff about untyped languages> This is besides the point, but what's an untyped language then? If a type defines a collection of values, by definition, every object in the language has a type, since every value is (reflexively) a value. I can't relate to your Icon example, I'm sorry to say it's a language which is a hole in my readings and experience. |> |> Well, my definition could have been clearer. I'll make the excuse |> that everyone I talk to around here has enough experience with these |> sorts of languages that a quick explanation is enough to let them know |> what I mean. I didn't make allowance for different "programming |> language cultures". I used the word "polymorphism" to mean "multiple |> types for the same expression", not "multiple functions with the same |> name". However, the term has enough baggage associated with it that |> it was a poor choice. I'm going to arbitrarily change my term... Ah! I've always seen polymorphism with respect to functions/ operators/arrows, not necessarily the types themselves. I couldn't reconcile your definition, it's much clearer now... :-) |> <definitions of Static and Dynamic Typing> I'm reading a book on Functional Languages right now, and the language being used as a model is Hope, which is strongly typed, but where an inference system is used to determine the static typing of the dynamically typed expression. Does this fall into your category? It seems to me that with global optimization, you can turn any seemingly weakly typed expression into a strongly typed one (and thus eliminate most if not all the inefficiencies which we're concerned with here.) The notable exception is what you mentioned: an operator like "read" where the type of the return value cannot be determined by static analysis of the program. My cut on that is that the value returned from something like "read" *does* fall into some type, and the return should be treated by the client as a "refany" is in CLU or Modula-3: you have to apply a type-case to the value in order to narrow the type of the expression. Is this contrary to your goals? I'm suggesting something (in a CLU/Modula-3 like procedural syntax) like: x : REFANY; BEGIN x := read(); TYPE_CASE x OF Integer : BEGIN /* integer-dependent operations and optimizations */ END; . . . END; END; Presumably the type_case statement can have an else arm where an exception could be raised or appropriate actions taken. (It's exactly this case of an opacity which can't be determined by any amount of compile-time static analysis where the inflexibility vs. inefficiency comes in. I've never used a "type inferred" language like Hope myself, but with the exception of this case, it seems usable and eliminates your worries.) The flexibility you desire is/can be obtained by having a type at the root of the hierarchy (which some consider artificial, others essential,) which can be used to represent values of any type, and then also a language operator to perform a modicum of run-time type checking, permitting significant optimizations within the narrowed scope, which I believe is the source of your concern. (There's been a lot of talk about adding in run-time accessable per-class tags to C++ objects lately, and it seems to me that if you can perform global analysis, you can make the run-time checking occur in linear time, costing space n^2 where n is the number of types/classes.) Or was it just academic interest in the topic in general? |> David Gudeman |> gudeman@cs.arizona.edu |> noao!arizona!gudeman |> ------------------------------------------------------------------------ I'm saying this, not Digital. Don't hold them responsibile for it! Michael J. Grier Digital Equipment Corporation (508) 496-8417 grier@leno.dec.com Stow, Mass, USA Mailstop OGO1-1/R6
new@ee.udel.edu (Darren New) (03/06/91)
>|> <stuff about untyped languages> > This is besides the point, but what's an untyped language then? Not to put words in anybody's mouth, but the post you are responding to says >People assume "untyped" means that a language >has no types, but it really just means that syntactic elements don't >have types. (Notice how I cleverly delete all "he wrote/she wrote" headers to avoid getting this wrong :-) Anyway, having a strong Smalltalk background, I interpret this to essentially to be saying that values have types but not variables. The declaration of a variable is just mentioning its name at the correct place within the method, without any formal declaration of the values that variable is allowed to hold. Values in Smalltalk most definitely have types, and types are even first-class objects. Also, in an `untyped' language, expressions return values (which have types) but since the same static expression can return values of different type, the expression itself cannot be siad to have a specific type. For example, Smalltalk allows arrays to have values of different types in different positions of the array. Hence, x := (array at: 5) is not a specific type, even though at any given time that expression will return a value of a given type and assign that value to x. x will nevertheless be untyped. Contrast with C, wherein one says (x = array[4]). Depending on the declaration of `array', I can tell the type of the value array[4]. I can also know the type of the variable x. Well, I hope that clears that up, and that I haven't misrepresented what was being said. -- Darren -- --- Darren New --- Grad Student --- CIS --- Univ. of Delaware --- ----- Network Protocols, Graphics, Programming Languages, Formal Description Techniques (esp. Estelle), Coffee, Amigas ----- =+=+=+ Let GROPE be an N-tuple where ... +=+=+=
icsu8209@khan.cs.montana.edu (Glassy) (03/07/91)
In article <1991Mar6.024730.19073@engage.enet.dec.com> grier@marx.enet.dec.com () writes: >In article <584@coatimundi.cs.arizona.edu>, gudeman@cs.arizona.edu >(David Gudeman) writes: >|> The term "weak typing" has been used for this feature, but it has >|> also been said that Pascal and C are weakly typed languages, so I didn't >|> want to used that word. > > That's interesting. I'd certainly agree that C is weakly typed, but >with Pascal requiring name compatibilty of types (rather than >structural, as in C and Modula-2,) I'd put it in the "strong typing" >category. (Strong typing is not necessarily "good." Pascal's a neat >language, but it lacks tons of features for modern software >construction...) Um, I don't think so. Pascal (at least ISO-something-something-6000) did not state which form type equivalence would take (structural vs. name equivalence). Vendors thus are free to do what they like, in this regard, which yields us an interoperability disaster. Modula-2, in Wirth's definition, uses name equivalence. All the Modulas I've used conform to this. Modula-3 by DECWRL, on the other hand, mandates structural equivalence. IRT the definitions of 'typing', discussions such as this thread are useful for helping all of us to communicate on the same (set of?) wavelength(s). The definitions of typing I've seen (in the literature) hew fairly closely to David Gudeman's. Interesting to note the variety of languages that can fall under a single category, WRT typing: static typing: FORTRAN ;all vars/funs declared explicitly ABC ;types determined implicitly at text-entry time by textual context and by example ML ;types determined at compile time via type inference. dynamic typing: Lisp ;"we'll find out (what type it has) when we get there." others??? typeless (aka untyped): BCPL ; everything's a machine word, so don't bother declaring stuff. "strong" typing: Modula, Euclid: static typing with tight controls on what may be coerced to what. "weak" typing: C: static typing with with loose/no controls on coercions. ------------------------ -- -- Lou Glassy </// "O Tempora!" icsu8209@caesar.cs.montana.edu \\\> --Cicero
grier@marx.enet.dec.com (03/07/91)
In article <46686@nigel.ee.udel.edu>, new@ee.udel.edu (Darren New) writes: |> <stuff deleted> |> Also, in an `untyped' language, expressions return values (which have |> types) but since the same static expression can return values of |> different type, the expression itself cannot be siad to have a specific |> type. For example, Smalltalk allows arrays to have values of different |> types in different positions of the array. Hence, x := (array at: 5) is |> not a specific type, even though at any given time that expression will |> return a value of a given type and assign that value to x. x will |> nevertheless be untyped. Contrast with C, wherein one says (x = |> array[4]). Depending on the declaration of `array', I can tell the type |> of the value array[4]. I can also know the type of the variable x. Ok, that makes sense. (I confess to not doing much in Smalltalk, probably a cardinal sin among OO folk.) But, at any given instant, the variable does represent a value which is a member of a given class. Even in expressions which are "untyped" (such as x := (array at: 5),) a globally scoped optimizer *could* infer the type of x. (Sounds to me like it's similar to a computability/halting- problem type of issue when you get into nontrivial recursive situations, but I haven't read that part of my func. lang. book yet... :-) In fact, it can always infer the type of the expression, although there are probably cases where a more general type is computed than the actual class of the value. (For instance, you could always punt and say "it's an Object". That's not very useful in aiding efficiency though... :-) Something that occurred to me is "what's the point here?" If the issue is to see what you *can* and *can't* do efficiently in an untyped/dynamically typed language, like I said, it seems related to the complexity and difficulty of the halting problem. There's no algorithm which is going to be able to infer the result type of an expression in all instances, but it seems like for many important ones, there are solutions. (The article posted to either comp.arch or comp.lang.c about optimizing Scheme, I believe it was, is a good example of what CAN be done, even if a single solution can't cover all cases. The point about the halting problem comes from some postings by Dan Bernstein where he claims a solution to the halting problem. What he *does* is solve a significant and useful portion of it, which is interesting. What's been proved is that every algorithm which will definitely produce an answer in finite time will have to make one of three decisions - halts, doesn't halt, can't determine. The "can't determine" seems very similar to "ummm... it's an Object!" in inferring the type of an expression.) I was approaching the issue with my engineering hat on, and looking at what problem was trying to be solved. My preference is towards strongly typed languages with mechanisms like I've outlined where the type can be strengthened within a given scope and so additional inferences about operations can be made. But if the desire is for discussion and academic interest, my ideas have little bearing, except that perhaps it's not a very fruitful or useful branch of study. (Of course that's what a lot of people have said about different branches of Mathematics which end up turning up in the strangest places explaining observed phenomonea.) |> |> Well, I hope that clears that up, and that I haven't misrepresented |> what was being said. -- Darren |> |> -- |> --- Darren New --- Grad Student --- CIS --- Univ. of Delaware --- |> ----- Network Protocols, Graphics, Programming Languages, |> Formal Description Techniques (esp. Estelle), Coffee, Amigas ----- |> =+=+=+ Let GROPE be an N-tuple where ... +=+=+= |> ------------------------------------------------------------------------ I'm saying this, not Digital. Don't hold them responsibile for it! Michael J. Grier Digital Equipment Corporation (508) 496-8417 grier@leno.dec.com Stow, Mass, USA Mailstop OGO1-1/R6
gudeman@cs.arizona.edu (David Gudeman) (03/10/91)
In article <1991Mar6.024730.19073@engage.enet.dec.com> grier@marx.enet.dec.com writes:
]
] This is besides the point, but what's an untyped language then?
The term is usually used to refer to dynamically typed languages. I'd
just as soon drop the term completely, since it is misleading if a
language has any built-in types at all.
]... Hope, which is strongly typed,
]but where an inference system is used to determine the static typing
]of the dynamically typed expression.
]
] Does this fall into your category?
Hope is statically typed since you can give types to syntactic
expressions. The definition does not specify how the types are
derived (I said types _usually_ determined by declarations).
] It seems to me that with global optimization, you can turn any
]seemingly weakly typed expression into a strongly typed one
Do you mean "turn any dynamically typed language into a statically
typed one"? If so, it has been my experience that given a program in
a dynamically typed language, a human can determine unique types for
85% to 99% of the names in the language. Type inference generally is
a little weaker, but is getting pretty good.
] ...the value returned from something like
]"read" *does* fall into some type, and the return should be treated
]by the client as a "refany" is in CLU or Modula-3: you have to apply
]a type-case to the value in order to narrow the type of the
]expression.
]
] Is this contrary to your goals?
Yes, it is. I don't want to have to write extra code to express
something that should be obvious. If I write "3 + read()" it should
be obvious that I want the correct sort of addition to be done
depending on whether read() returns an int or a float. I shouldn't
have to say it explicitly. Another reason I don't want to write
type-case expressions for things like this is that I may later change
the definition of read() so that it always returns a float. I don't
want to have to go find every place I used it and remove all the
type-case stuff.
--
David Gudeman
gudeman@cs.arizona.edu
noao!arizona!gudeman
pcg@cs.aber.ac.uk (Piercarlo Antonio Grandi) (03/11/91)
On 10 Mar 91 00:21:37 GMT, gudeman@cs.arizona.edu (David Gudeman) said:
gudeman> In article <1991Mar6.024730.19073@engage.enet.dec.com>
gudeman> grier@marx.enet.dec.com writes:
grier> This is besides the point, but what's an untyped language then?
gudeman> The term is usually used to refer to dynamically typed
gudeman> languages. I'd just as soon drop the term completely, since it
gudeman> is misleading if a language has any built-in types at all.
A point of order: I would not use 'dynamic types' here. I would say
'latent types'. The idea is that dynamically typed should be reserved
for the (currently rare!) case where the language allows the type of a
*value* to change dynamically, not for the case where the type of the
value of a *variable* is not manifest at compile time.
I would propose the following terminology:
<type> The equivalence class of all <values> to which
a given set of operations can be meanigfully
applied.
<typing> The ability to give an explicit name to a
<type>, and to have a 'typeof' operator (either
explicit or implicit) that returns the appropriate
name given a value.
<manifest typing> Being able to do <typing> for the <values>
denoted by <references> (i.e. variables) at
compiletime.
<latent typing> When the <value> denoted by a <reference> has a
<type> that cannot be determined at compiletime.
<static typing> When the <typing> of a <value> cannot change.
<dynamic typing> When the <typing> of a <value> can change during
program execution.
Examples (contrived, unfortunately):
We have the values from 0 to 2^32-1, and the operations +,-,* modulo
2^32 on these values. They form a type. We add an operation, 'typeof'
that given one such value returns a type name, say 'unsigned'.
We have typing. We write 'unsigned i'. This is manifest typing, as we
know by the inpesction of the program source that the values that the
reference bound to the 'i' identifier can only have typing 'unsigned'.
Now we switch to Scheme and we say '(define i 0)'. We have latent static
typing, because the typing of the value involved may change at runtime,
but whatever the value is, its typing will *not* change during the
execution of the program.
Now we switch to some more advanced language and we add while the
program is executing a new operation, say '/', with two arguments, one
is 'unsigned' and the other is 'positive' (nonzero). Then we can
dynamically chang the typing of the value '1', because now it can be
*both* an 'unsigned' and a 'positive'.
So, latent typing is when 'typeof' applied to a *variable* is not a pure
function, in that given the same variable it may give different results
at different times; dynamic typing is when this happens also when it is
applied to *values*, not just to variables.
Note that the distinction I make above between <type> and <typing> is
artificial but necessary, and it causes trouble.
Suppose that we have the <type> defined by the class of values to which
operations +,-,* modulo 32 can be applied as above; we can call it the
'unsigned' class in some nice contemporary language and we have
Suppose that we now also define the % operation to the unsigned class;
we have a *different* type, but the typing remains the same, because
'typeof' will continue to return 'unsigned' when applied to values
tagged as belonging to the 'unsigned' class.
This is not very dangerous in this particular situation, but inb others
it can be highly inconvenient especially when the definition of a class,
that is the <typing>, is allowed to change dynamically. For example it
can be very dangerous if we change the encoding for the values of that
class, e.g. from binary to BCD.
--
Piercarlo Grandi | ARPA: pcg%uk.ac.aber@nsfnet-relay.ac.uk
Dept of CS, UCW Aberystwyth | UUCP: ...!mcsun!ukc!aber-cs!pcg
Penglais, Aberystwyth SY23 3BZ, UK | INET: pcg@aber.ac.uk
bbc@rice.edu (Benjamin Chase) (03/11/91)
>I would propose the following terminology: ><type> The equivalence class of all <values> to which > a given set of operations can be meanigfully > applied. Make certain that you don't proscribe certain views of the world through too narrow a definition. Consider the language Russell, where types are viewed not as data, but rather as collection of operations on data. You may consider this to be an argument over a glass of water being "half-empty" vs. "half-full", but I found the Russell outlook on the question of "what are types, really?" to be very pleasant. ><latent typing> When the <value> denoted by a <reference> has a > <type> that cannot be determined at compiletime. ><manifest typing> Being able to do <typing> for the <values> > denoted by <references> (i.e. variables) at > compiletime. Of course, the obvious name for manifest typing is really blatant typing, the obvious meaning being that the types of things are obvious enough that even a compiler can deduce them :-). That way, we have "latent" vs. "blatant", a very pleasant sounding pair. Of course, the invitation to incorrectly spell them either (or both?!?) as blatent (bleah!) or latant (fooey!) is too tempting for some... -- "it is perfectly safe to eat the yogurt under the green fuzz, and I do it fairly often" - brian reid Ben Chase <bbc@rice.edu>, Rice University, Houston, Texas
gudeman@cs.arizona.edu (David Gudeman) (03/12/91)
In article <PCG.91Mar10181749@aberdb.cs.aber.ac.uk> Piercarlo Antonio Grandi writes:
]
]A point of order: I would not use 'dynamic types' here. I would say
]'latent types'. The idea is that dynamically typed should be reserved
]for the (currently rare!) case where the language allows the type of a
]*value* to change dynamically,...
]<type> The equivalence class of all <values> to which
] a given set of operations can be meanigfully
] applied.
...
]Now we switch to some more advanced language and we add while the
]program is executing a new operation, say '/', with two arguments, one
]is 'unsigned' and the other is 'positive' (nonzero). Then we can
]dynamically chang the typing of the value '1', because now it can be
]*both* an 'unsigned' and a 'positive'.
This only happens in a statically typed language. In a dynamically
typed language you don't declare the types that an operator works on,
so this doesn't happen.
This definition of types originated with the implicit assumption that
the set of operations are fixed. When operations can be added at
runtime, bizarre situations arise where the type of a value can
change. This doesn't shouldn't prompt us to add complexity to the
language, trying to distinguish between <type> and <typing>. Rather
we should drop this notion of <type> altogether and define
<type> a set of values.
Then no seperate notion of <typing> is needed. In fact, this is what
is done in most dynamically typed languages. It makes it trivial to
do useful things like define hierarchies of types (subsets) and to
have values that are of several different types (membership in
different sets). It also gives you powerful machinery for defining
complex types and for deciding type membership. It also gives you a
fairly simple way to define the result of the 'typeof' function(s)
even though a value may have may types: you just define some arbitrary
(but useful) partition of the set of all values, and give each set a
name.
(There are people who complain about using the word "set" here since it
makes it problematic to make <type> a value. (eg: T is the type that
contains all types that do not contains themselves. Is T in the type
T?) However, that is a problem cause by defining <type> to be a
value. It is not inherent in defining a type as a set.)
--
David Gudeman
gudeman@cs.arizona.edu
noao!arizona!gudeman
brnstnd@kramden.acf.nyu.edu (Dan Bernstein) (03/12/91)
I really think we need a Frequently Used Definitions list here... In article <601@optima.cs.arizona.edu> gudeman@cs.arizona.edu (David Gudeman) writes: > <type> a set of values. Yeah, this is the definition I'm familiar with. Also: <reference> a section of program text that refers to a value. Any language lets you express certain types. In general the expressible types are a small, fixed set of basic types, plus the transitive closure of a fixed set of type composition operations upon those basic types. In C, for example, the basic types are int, double, etc., and the type composition operations are array, struct, function, etc. In C, every reference has a value with a fixed, expressible type, known to the compiler. The value of that reference may change during program execution, but it is always in that compile-time type. I believe this is what most people mean by static typing. Note: If a statically typed language gives you ``the set of expressible types'' and ``the set of all values'' as basic types, and struct as a type composition operation, then you can implement runtime polymorphism in that language. I.e., the set of pairs (type,value) is a type, and you can restrict yourself to using those pairs. The typing is still static: in fact, every value in your program has the same type, namely the set of all (type,value). Typechecking in this context means making sure that the value inside (type,value) is a member of the given type. Given this, I fail to see how dynamic typing can be regarded as more than a syntactic feature. If you're given a program that uses dynamic typing, you can just convert every reference in the program to refer to a (type,value) pair, and poof! you have a statically typed program. (That doesn't mean I have no appreciation for dynamic typing; syntactic features are often quite worthwhile, and can represent quite a lot of work. In C, for example, the set of all types is not a basic type, so you have to create the type for yourself and arrange that every type definition be accompanied by creation of a member of the type of all types. Then you have to carry (type,value) pairs everywhere, and handle all the typechecking. This is still a syntactic conversion from the original program. It requires no support from the compiler.) ---Dan You are in a maze of twisty little types, all alike.
pcg@test.aber.ac.uk (Piercarlo Antonio Grandi) (03/12/91)
On 11 Mar 91 20:35:48 GMT, gudeman@cs.arizona.edu (David Gudeman) said:
gudeman> In article <PCG.91Mar10181749@aberdb.cs.aber.ac.uk> Piercarlo
gudeman> Antonio Grandi writes:
pcg> A point of order: I would not use 'dynamic types' here. I would say
pcg> 'latent types'. The idea is that dynamically typed should be reserved
pcg> for the (currently rare!) case where the language allows the type of a
pcg> *value* to change dynamically,...
pcg> <type> The equivalence class of all <values> to which
pcg> a given set of operations can be meanigfully
pcg> applied.
pcg> Now we switch to some more advanced language and we add while the
pcg> program is executing a new operation, say '/', with two arguments,
pcg> one is 'unsigned' and the other is 'positive' (nonzero). Then we
pcg> can dynamically change the typing of the value '1', because now it
pcg> can be *both* an 'unsigned' and a 'positive'.
gudeman> This only happens in a statically typed language. In a
gudeman> dynamically typed language you don't declare the types that an
gudeman> operator works on, so this doesn't happen.
No, no, we have *two* misunderstanding here. I have never said that in
this example one need declare *explicitly* the types of the arguments of
an operator; simply, that a type is defined by the operators that can be
meaningfully applied to the values of that type. Since / cannot be
meaningfully applied to zero, adding it to the language means that one
implicitly is distinguishing between the <type> 'unsigned' and
'positive', because the second argument to / cannot be zero.
gudeman> This definition of types originated with the implicit assumption that
gudeman> the set of operations are fixed. When operations can be added at
gudeman> runtime, bizarre situations arise where the type of a value can
gudeman> change. This doesn't shouldn't prompt us to add complexity to the
gudeman> language, trying to distinguish between <type> and <typing>.
But changing <type> is of the essence. It happens all the time. Every
time you use an interactive lanfguage like Scheme and you define a new
function. Also, when you edit the definition of a C++ struct and
recompile.
Consider the latter example; the <typing> has not changed, but the
<type> has. This is dangerous; for example all the values created under
the old definition may well be no longer meaningful. Data restructuring
is needed then.
This si for example well understood in the capability architecture
business; a capability architecture only has <typing>, and tags each
value with a type code. Each type code is associated with a type
manager, the module that implements all the operations on values with
that type code (this will be well known to you, but I repeat just to
provide context). Now if the type manager changes, often one has to
assign a new type code to it, because changes in the <type> should be
reflected in changes in the <typing>. Not necessarily, for example we
can keep the same <typing> if the new <type> is a superset (or even
subset) of the old, of course, as when we don't change the
encoding/decoding functions for composite values. Example in C++:
struct Complex { float re, im; };
Complex operator +(const Complex &a, const Complex &b) { ... };
If we change this to
struct Complex { float real, imaginary; };
Complex operator +(const Complex &a, const Complex &b) { ... };
We need not change the <typing> of complex <type> values; we just have
to edit and recompile 'operator +' with the names of the new
decomposition functions.
But if we change it to
struct Complex { float im, re; }
Complex operator +(const Complex &a, const Complex &b) { ... };
The <typing> has not changed, but the <type> has changed in a way such
that the <typing> chould change.
Naturally most people are blinkered in that they ignore the issue of
persistency, but unfortunately in the real world almost all data are
persistent, and the difference between <type> and <typing> is thus
terribly important.
An example of this is the protocol revision level field in many
protocols; each protocol is really a type specification, where each
packet header is a value in the type defined by the protocol operations
that it triggers.
I will venture as far as saying that the difference between <type> and
<typing> which is inherent in most all languages, operating systems,
databases out there is regrettable and is only really an efficiency
hack, just like the one between 'eq' and 'equal', and it generates much
the same class of problems.
gudeman> Rather we should drop this notion of <type> altogether and
gudeman> define
gudeman> <type> a set of values.
This is wrong, as far as I can see, because then there is no way to know
the <type> or <typing> of a value. As soon as you instead have a
'typeof' operation, you create equivalence classes. Please note: in my
porposed definitions, <value> is just a number, and absolutely nothing
else, and it does not carry any other information than that number.
I think you are wrongly assuming, in other words, in your definition
above, that there is some magic way to know which set a value belongs
to, without encoding it into the value itself. This is not possible, I
am afraid. On the other hand if you encode the set into the value
itself, then you are defining types as equivalence classes, and your
definition and mine are exactly equivalent, except maybe that yours
hides the difference between <type> (a concept) and <typing> (an
economical implementation of that concept).
gudeman> Then no seperate notion of <typing> is needed.
Quite the opposite... The end result of your proposal is to drop the
notion of <type> and keep only that of <typing>, not viceversa.
gudeman> In fact, this is what is done in most dynamically typed
gudeman> languages.
In this you are right. Indeed in most languages, whatever their typing
discipline, one can only handle <typing>, not <types>. This is a gross
limitation, and one that is simply unacceptable when you have persistent
values. The limitation can be gotten around using gross hacks; for
example when the <type> changes but not the <typing>, e.g. when a new
release of Unix switches the implementation of /etc/passwd from a text
file to an hash file, you need a program to convert formats, whose role
is essentially that of converting one <type> to another to maintain ther
illusion that <typing> has not changed.
gudeman> It makes it trivial to do useful things like define hierarchies
gudeman> of types (subsets) and to have values that are of several
gudeman> different types (membership in different sets).
Actually it makes it incredibly opaque. I know no language that IMNHO
does a satisfactory job of reconciling the gross rigidities of doing
<typing> with the reality that <types> are equivalence classes. For
example, to repeat my usual party line, there ar eno languages that have
any decent algebra on the various aspects of <typing>; they all limit
the <typing> algebra to an awakward subset of the possibilities demanded
by applications.
gudeman> It also gives you powerful machinery for defining complex types
gudeman> and for deciding type membership. It also gives you a fairly
gudeman> simple way to define the result of the 'typeof' function(s)
gudeman> even though a value may have may types: you just define some
gudeman> arbitrary (but useful) partition of the set of all values, and
gudeman> give each set a name.
This is just <typing>, according to my terminology. You have thrown away
the notion of <type> entirely.
gudeman> (There are people who complain about using the word "set" here
gudeman> since it makes it problematic to make <type> a value. (eg: T
gudeman> is the type that contains all types that do not contains
gudeman> themselves. Is T in the type T?) However, that is a problem
gudeman> cause by defining <type> to be a value. It is not inherent in
gudeman> defining a type as a set.)
While a <type> is an equivalence class, and thus not a value, a <typing>
can be represented as a value, e.g. by assigning to each <typing> class
an arbitrary code. Then these codes *do* form a <type>. This is like the
solution to paradoxes in Russell's (the mathematician, not the like
minded functional language :->) Principia...
--
Piercarlo Grandi | ARPA: pcg%uk.ac.aber@nsfnet-relay.ac.uk
Dept of CS, UCW Aberystwyth | UUCP: ...!mcsun!ukc!aber-cs!pcg
Penglais, Aberystwyth SY23 3BZ, UK | INET: pcg@aber.ac.uk
gudeman@cs.arizona.edu (David Gudeman) (03/13/91)
In article <19728:Mar1208:45:2291@kramden.acf.nyu.edu> Dan Bernstein writes: ] ]Given this, I fail to see how dynamic typing can be regarded as more ]than a syntactic feature. If you're given a program that uses dynamic ]typing, you can just convert every reference in the program to refer to ]a (type,value) pair, and poof! you have a statically typed program. Alternatively: I fail to see how static typing can be regarded as more than a syntacitic feature. If you're given a program that uses static typing, you can just check the types at every reference in the program to make sure they are correct... BTW, you can't just change the program to refer to a (type,value) pair, you also have to insert code to do the right thing based on the type. For "x + y", you have to generate: switch (x.type) { case int_type: switch (y.type) { case int_type: return x.val.int + y.val.int case float_type: return x.val.int + y.val.float; default: type_error(y); } ... } and so on, for each combination of types. For more general expressions like "x = y", you have to give code that works for any possible combination of types -- not just numbers. -- David Gudeman gudeman@cs.arizona.edu noao!arizona!gudeman
brnstnd@kramden.acf.nyu.edu (Dan Bernstein) (03/13/91)
In article <608@optima.cs.arizona.edu> gudeman@cs.arizona.edu (David Gudeman) writes: > Alternatively: I fail to see how static typing can be regarded as more > than a syntacitic feature. If you're given a program that uses static > typing, you can just check the types at every reference in the program > to make sure they are correct... That is correct. Since most people, most of the time, seem to want to use statically typed values, and since compilers have an easier time optimizing statically typed programs than dynamically typed programs, it makes sense to provide static typing as the default. You can implement dynamic typing on top of this if you think people will find it useful. What is the argument here? > BTW, you can't just change the program to refer to a (type,value) > pair, you also have to insert code to do the right thing based on the > type. Didn't I say that? Yes, you have to do this, and it can represent a significant amount of work (about as much as writing a C++ translator). It's still a syntactic transformation. ---Dan
gudeman@cs.arizona.edu (David Gudeman) (03/13/91)
In article <25293:Mar1221:01:4491@kramden.acf.nyu.edu> Dan Bernstein writes:
]... Since most people, most of the time, seem to want to
]use statically typed values
This is open to debate. And in any case, I claim that most people who
say they want static typing are ignorant of the alternative. I'll bet
that programmers who have programmed equally in both types of language
will overwhelmingly prefer dynamic typing (after correcting for the
stupid syntaxes that these languages seem prone to). I know of many
people who started out programming in C or Pascal and fell in love
with Icon when they first tried it (me for example). I doubt there
are many cases of programmers who started out with a dynamically typed
languages and grew to prefer static typing (yes, there _are_ some
places where the first programming class uses a dynamically typed
language).
] and since compilers have an easier time
]optimizing statically typed programs than dynamically typed programs, it
]makes sense to provide static typing as the default.
Only if you are designing the language to be implemented. If you
are designing the language to be used, then this argument fails.
--
David Gudeman
gudeman@cs.arizona.edu
noao!arizona!gudeman
gudeman@cs.arizona.edu (David Gudeman) (03/13/91)
I'm not entirely sure I understand what you are getting at, but that's
never stopped me from talking before...
In article <PCG.91Mar12135254@aberda.test.aber.ac.uk> Piercarlo Antonio Grandi writes:
...
]No, no, we have *two* misunderstanding here. I have never said that in
]this example one need declare *explicitly* the types of the arguments of
]an operator; simply, that a type is defined by the operators that can be
]meaningfully applied to the values of that type.
What does "meaningfully applied" mean? In a dynamically typed
language, any function can be legally applied to any value. So are
you suggesting that every value has the same <type>? Or do you want
"meaningfully applied" to mean that the function doesn't produce an
error message when called with that value? Then what do you do with
something like
procedure foo(x1,x2)
if number(x1) then return x1 + x2
else return x2
end
where x2 must be a number if x1 is, but otherwise can have any type?
I think the whole concept is on shakey ground.
]... is regrettable and is only really an efficiency
]hack, just like the one between 'eq' and 'equal', and it generates much
]the same class of problems...
An aside, 'eq' and 'equal' are semantically distinct. I would
consider the language to be lacking an important operation if it were
missing either one. The same applies to 'eql'. They all serve
different purposes.
]This is wrong, as far as I can see, because then there is no way to know
]the <type> or <typing> of a value...
]I think you are wrongly assuming, in other words, in your definition
]above, that there is some magic way to know which set a value belongs
]to, without encoding it into the value itself...
I wasn't making any assumptions about the representation of values.
Frankly, I'm surprised to see representation come up at all, since I
don't see what it has to do with the subject at hand (unless you are
thinking of <type> as a particluar way of interpreting a group of
bytes as a value. That doesn't fit your definition though.)
]... On the other hand if you encode the set into the value
]itself, then you are defining types as equivalence classes, and your
]definition and mine are exactly equivalent.
Not at all. Your definition has the <type> of a value changing when
new functions are added. Mine doesn't.
]gudeman> Then no seperate notion of <typing> is needed.
]
]... The end result of your proposal is to drop the
]notion of <type> and keep only that of <typing>, not viceversa.
I'm throwing away your concept of <type>, as I said.
]... when a new
]release of Unix switches the implementation of /etc/passwd from a text
]file to an hash file, you need a program to convert formats, whose role
]is essentially that of converting one <type> to another to maintain ther
]illusion that <typing> has not changed.
The job of the program is to convert from one representation to
another. The set of operations on the file has not changed, so the
the <type> hasn't changed, by your definition. Again though, it looks
like you mean <representation> when you say <type>.
]... I know no language that IMNHO
]does a satisfactory job of reconciling the gross rigidities of doing
]<typing> with the reality that <types> are equivalence classes.
Are you familiar with Common Lisp? What sort of type do you need that
you can't define in that language? How is an equivalence class
different from a set? And what equivalence relation are you refering
to? It looks at times as though you mean $equiv(x,y) iff typeof(x) =
typeof(y)$ -- which makes <type> and <typing> identical.
]gudeman> ... It also gives you a fairly
]gudeman> simple way to define the result of the 'typeof' function(s)...
]
]This is just <typing>, according to my terminology. You have thrown away
]the notion of <type> entirely.
Yeah, that's what I said. (I get the feeling we aren't
communicating...)
--
David Gudeman
gudeman@cs.arizona.edu
noao!arizona!gudeman
pcg@test.aber.ac.uk (Piercarlo Antonio Grandi) (03/14/91)
On 12 Mar 91 08:45:22 GMT, brnstnd@kramden.acf.nyu.edu (Dan Bernstein) said: Incidentally, I hereby announce my defeat; I have realized that too many people are using dynamic and static typing to indicate when the type of *variables* is known. So I will use these terms like that myself. I propose the use of mutable/immutable typing for the case where the typing of *values* can or cannot be changed at runtime. brnstnd> <reference> a section of program text that refers to brnstnd> a value. Normally this is called the scope, except in Algol 68 where there is a distinction between range and reach. In all texts that I know reference is more or less synonymous with pointer. People use reference after the Algol 68 revised report, usually, or after Simula 67, which both use reference in the way indicated above. The normal definition of reference is something like "a value that may refer to another value or to no particular value". brnstnd> Note: If a statically typed language gives you ``the set of brnstnd> expressible types'' and ``the set of all values'' as basic brnstnd> types, and struct as a type composition operation, then you can brnstnd> implement runtime polymorphism in that language. [ ... ] Of course... But after you say: brnstnd> Given this, I fail to see how dynamic typing can be regarded as brnstnd> more than a syntactic feature. If you're given a program that brnstnd> uses dynamic typing, you can just convert every reference in brnstnd> the program to refer to a (type,value) pair, and poof! you have brnstnd> a statically typed program. This is entirely uninteresting! Recursion is merely a "syntactic feature" too, by the same argument, as it can always be obviated by the use of stacks or by iteration. Yet a lot of people think that recursion should be provided as a "syntactic feature". What we are interested here is in *architecture*, that is designing boundaries between layers. In our particular case the boundary between what the language provides directly and what the programmer can achieve building on top of that. Dynamic typing as a concept is simply necessary in a wide class of applications; one has four choices, with respect to it and static typing: 1) Don't provide dynamic typing as a language primitive and make it prohibitive to implement it on top of the provided language primitives. 2) Don't provide dynamic typing as a language primitive, and make it possible to implement it more or less grossly on top of the provided language primitives. 3) Provide dynamic typing only as a language primitive, and make it possible to implement static typing by explicit checks. 4) Provide *both* dynamic and static typing as language primitives, with no solution of continuity, as one becomes the other depending on which variable's values are known at compiletime. I reckon that Gudeman thinks that Bernstein advocates 1), while Bernstein really advocates 2); Gudeman himself advocates 3), and I advocate 4). While I advocate 4), I think that there are good reasons to think that 3) is acrtually a more tnable proposition than 2): an efficient implementation of dynamic typing as language primitive is not much worse (thanks to caching and hinting) than one of static typing, or at least the difference is not large for many complex *applications*, while a clean implementation of dynamic typing given static typing primitives is much harder to do, as the X windows example demonstrates so clearly. -- Piercarlo Grandi | ARPA: pcg%uk.ac.aber@nsfnet-relay.ac.uk Dept of CS, UCW Aberystwyth | UUCP: ...!mcsun!ukc!aber-cs!pcg Penglais, Aberystwyth SY23 3BZ, UK | INET: pcg@aber.ac.uk
augustss@cs.chalmers.se (Lennart Augustsson) (03/14/91)
In article <614@optima.cs.arizona.edu> gudeman@cs.arizona.edu (David Gudeman) writes: > ... And in any case, I claim that most people who >say they want static typing are ignorant of the alternative. I'll bet >that programmers who have programmed equally in both types of language >will overwhelmingly prefer dynamic typing (after correcting for the >stupid syntaxes that these languages seem prone to). I don't agree. I think that a lot of people who prefer dynamically typed languages are ignorant of the alternatives. Exactly as you I dislike to have to type all the extra type information that you have to do in Pascal, C etc. I also dislike not being able to make polymorphic functions (without cheating). But with languages like ML and Haskell with polymophic type systems and type inference you get static typing without the extra baggage. (Sure, there are still things you can do with dynamic typing that can't be done as easily with static typing; you can't get it all.) Taking your example and translating it to Haskell you'll get Icon: record tree(data,left,right) Haskell: data Tree a = Node a (Tree a) (Tree a) | Null Icon: # insert x into tree t and return the new tree procedure tree_insert(t,x) if t === &null return tree(x) if x <<< t.data then t.left := tree_insert(t.left,x) else t.right := tree_insert(t.right,x) return t end Haskell: (well, a side effect free version of the procedure above) tree_insert Null x = Node x Null Null tree_insert (Node y l r) x | x < y = Node (tree_insert l x) r tree_insert (Node y l r) x = Node l (tree_insert r x) The compiler infers the type (i.e. you don't have to write it!) tree_insert :: (Ord a) => Tree a -> a -> Tree a which means that tree_insert will work on any type of data that is a member of class Ord (i.e. has < defined). -- Lennart Augustsson [This signature is intentionally left blank.]
gudeman@cs.arizona.edu (David Gudeman) (03/14/91)
In article <1991Mar13.195039.22587@mathrt0.math.chalmers.se> Lennart Augustsson writes: ]In article <614@optima.cs.arizona.edu> gudeman@cs.arizona.edu (David Gudeman) writes: ]I don't agree. I think that a lot of people who prefer dynamically ]typed languages are ignorant of the alternatives. I doubt there are many people who are only experienced with dynamically typed languages. If you are refering to type inference as the alternative, you may be right -- people who prefer dynamically typed languages may tend to be unaware of it (like everyone else), but I doubt they would like it, ignoring efficiency issues and sticking strictly to expressiveness and program reliability. When worried about efficiency, everyone prefers statically typed languages, I never meant to claim otherwise. Statically typed languages without declarations are a step in the right direction, but not far enough for my taste. You are still too restricted in the sorts of expressions you are allowed to write (not to mention that for non-toy languages, compilation is slow as a beejeeber). But at least it solves the problems of errors in declarations and having to make pervasive changes to the code when you change declarations. ] tree_insert Null x = Node x Null Null ] tree_insert (Node y l r) x | x < y = Node (tree_insert l x) r ] tree_insert (Node y l r) x = Node l (tree_insert r x) ]The compiler infers the type (i.e. you don't have to write it!) ] tree_insert :: (Ord a) => Tree a -> a -> Tree a ]which means that tree_insert will work on any type of data that is a ]member of class Ord (i.e. has < defined). Can you use this function to insert different types in the same tree? If so, how is it different from dynamic typing? -- David Gudeman gudeman@cs.arizona.edu noao!arizona!gudeman
augustss@cs.chalmers.se (Lennart Augustsson) (03/15/91)
In article <628@optima.cs.arizona.edu> gudeman@cs.arizona.edu (David Gudeman) writes: >Statically typed languages without declarations are a step in the >right direction, but not far enough for my taste. You are still too >restricted in the sorts of expressions you are allowed to write (not >to mention that for non-toy languages, compilation is slow as a >beejeeber). I don't know what implementations you have tried, I don't think ML style type inference is particularely slow (except on pathological examples). >] tree_insert Null x = Node x Null Null >] tree_insert (Node y l r) x | x < y = Node (tree_insert l x) r >] tree_insert (Node y l r) x = Node l (tree_insert r x) >]The compiler infers the type (i.e. you don't have to write it!) >] tree_insert :: (Ord a) => Tree a -> a -> Tree a >]which means that tree_insert will work on any type of data that is a >]member of class Ord (i.e. has < defined). > >Can you use this function to insert different types in the same tree? >If so, how is it different from dynamic typing? No, one particular tree can only contain values of one type. I know you find this too restrictive, I was just trying to point out that compile time type checking does not imply that you have to declare the type of the functions you define. -- Lennart Augustsson [This signature is intentionally left blank.]
brnstnd@kramden.acf.nyu.edu (Dan Bernstein) (03/15/91)
In article <PCG.91Mar13185855@aberdb.test.aber.ac.uk> pcg@test.aber.ac.uk (Piercarlo Antonio Grandi) writes: > On 12 Mar 91 08:45:22 GMT, brnstnd@kramden.acf.nyu.edu (Dan Bernstein) said: > Incidentally, I hereby announce my defeat; I have realized that too many > people are using dynamic and static typing to indicate when the type of > *variables* is known. On the contrary. Typing refers to the typing of every single reference in the program, not just ``x'' and ``y''. > brnstnd> <reference> a section of program text that refers to > brnstnd> a value. > Normally this is called the scope, No. The scope of a variable is the largest section X of program text such that any part of X may contain a reference to that variable. > In all texts that I know reference > is more or less synonymous with pointer. Yes. The program text ``x + y'', for example, refers to the value computed as the sum of x and y. You can draw an arrow from ``x + y'' to that value if you want. There are languages where a variable may have a reference---a section of program text referring to a value---as a value in and of itself. Sometimes the program text can be made implicit when you have a reference, as in C++. > 2) Don't provide dynamic typing as a language primitive, and make it > possible to implement it more or less grossly on top of the provided > language primitives. So why do you consider this ``gross'' when it's implemented outside the compiler but (presumably) not gross when it's inside the compiler? As long as the language has good syntax, you can hide all the ugliness of (e.g.) polymorphism inside a header file or library. I advocate that this be done for any essentially syntactic feature. > I reckon that Gudeman thinks that Bernstein advocates 1), while > Bernstein really advocates 2); Gudeman himself advocates 3), and I > advocate 4). Actually, I think Gudeman thinks he advocates (4). > an efficient > implementation of dynamic typing as language primitive is not much worse > (thanks to caching and hinting) than one of static typing, For me compile time and typechecking are both important while I'm writing a program. I cannot afford to choose between fast compilations with no typechecking and slow compilations with full typechecking. (Similarly, many programs take a noticeable amount of time for each debugging run. I cannot afford to choose between fast compile time with slow run time and slow compile time with fast run time. My solution for those programs is to optimize by hand, once.) Do you optimize programs while you're testing? Probably not. But when each test run takes a noticeable amount of time, don't you wish that you could make them run faster without wasting so much time on optimization? Similarly, in a dynamically typed language, would you turn on strict typechecking and other optimizations while you're testing? Probably not. But when you make type errors, don't you wish that you could have found them without wasting so much time on optimization? Well, you could. All you had to do was use static typing. ---Dan
gudeman@cs.arizona.edu (David Gudeman) (03/15/91)
In article <PCG.91Mar13185855@aberdb.test.aber.ac.uk> Piercarlo Antonio Grandi writes:
]...
]1) Don't provide dynamic typing as a language primitive and make it
]prohibitive...
]
]2) Don't provide dynamic typing as a language primitive, and make it
]possible to implement it...
]
]3) Provide dynamic typing only...
]
]4) Provide *both* dynamic and static typing...
]
]I reckon that Gudeman thinks that Bernstein advocates 1), while
]Bernstein really advocates 2); Gudeman himself advocates 3), and I
]advocate 4).
No, Gudeman thinks that Bernstein advocates (2), Gudeman himself
advocates (4), and Gudeman is willing to accept Grandi's word that
Grandi advocates (4).
Didn't anyone read "Runtime Polymorphism... part 2"? OK, maybe part 1
one was so uninspiring that no one bothered with the sequal. Anyway,
in that posting I described my preference for a system with optional
static typing.
Maybe my bickering with the B&D language people over the importance of
static typing has lead people to believe that I am opposed to static
typing in all forms, but I am not. I am opposed to any language
feature that restricts my options and increases my effort in the name
of security. The language designer has no idea how much security my
program requires.
--
David Gudeman
gudeman@cs.arizona.edu
noao!arizona!gudeman
aipdc@castle.ed.ac.uk (Paul Crowley) (03/16/91)
In article <650@optima.cs.arizona.edu> gudeman@cs.arizona.edu (David Gudeman) writes: >Maybe my bickering with the B&D language people So C is a B&D language. Wow. ____ \/ o\ Paul Crowley aipdc@uk.ac.ed.castle \ / /\__/ Part straight. Part gay. All queer. \/
pcg@test.aber.ac.uk (Piercarlo Antonio Grandi) (03/16/91)
On 13 Mar 91 07:48:23 GMT, gudeman@cs.arizona.edu (David Gudeman) said:
gudeman> I'm not entirely sure I understand what you are getting at, but
gudeman> that's never stopped me from talking before...
Same feelings on my side. I actually think that I expressed myself quite
obscurely, because it seems that we talking of different things...
gudeman> In article <PCG.91Mar12135254@aberda.test.aber.ac.uk> Piercarlo
gudeman> Antonio Grandi writes:
pcg> No, no, we have *two* misunderstanding here. I have never said that
pcg> in this example one need declare *explicitly* the types of the
pcg> arguments of an operator; simply, that a type is defined by the
pcg> operators that can be meaningfully applied to the values of that
pcg> type.
gudeman> What does "meaningfully applied" mean? In a dynamically typed
gudeman> language, any function can be legally applied to any value. So
gudeman> are you suggesting that every value has the same <type>? Or do
gudeman> you want "meaningfully applied" to mean that the function
gudeman> doesn't produce an error message when called with that value?
Let's say then that a type is the intersection of the codomains of a
given group of functions , or something like that.
gudeman> Then what do you do with something like
gudeman> procedure foo(x1,x2)
gudeman> if number(x1) then return x1 + x2
gudeman> else return x2
gudeman> end
gudeman> where x2 must be a number if x1 is, but otherwise can have any
gudeman> type?
This is the problem of procedures with multiple arguments. We all know
that it can be argued that functions with multiple arguments either have
a single structured argument or are functionals...
This said, let's arbitrarily choose the single composite argument view:
the procedure defines the type of all cartesian products where as you
say either the argument are both numeric, or are of any type at all. In
other words, this procedures by itself defines the type of all pairs,
because it will never fail on any pair of values.
gudeman> I think the whole concept is on shakey ground.
Not much more than other definitions of type... And probably much less,
because it gives an operative definition. Look at the intersection of
the codomains of a given group of functions, and that's a type.
Note that this includes the encoding functions, the typeof function, if
any, and so on. I know it is terribly difficult to "see". Maybe I have
not made very clear that that values and their denotations are *very*
different things in my view, thus encoding (and representation) is
crucial to my argument.
pcg> ... is regrettable and is only really an efficiency hack, just like
pcg> the one between 'eq' and 'equal', and it generates much the same
pcg> class of problems...
gudeman> An aside, 'eq' and 'equal' are semantically distinct. I would
gudeman> consider the language to be lacking an important operation if
gudeman> it were missing either one. The same applies to 'eql'. They
gudeman> all serve different purposes.
No, all, except 'equal', are redundant. We have already been thru this
discussion, and I still maintain that ideally if two values are the same
number, they *must* represent the same denotation. Saying that they
actually encode different denotations simply means, IMNHO, that actually
part of the value has been encoded in its address, which is an
efficiency hack.
pcg> This is wrong, as far as I can see, because then there is no way to
pcg> know the <type> or <typing> of a value... I think you are wrongly
pcg> assuming, in other words, in your definition above, that there is
pcg> some magic way to know which set a value belongs to, without
pcg> encoding it into the value itself...
gudeman> I wasn't making any assumptions about the representation of
gudeman> values.
Neither I am, but maybe I did not make it clear that to me <value> is
the same as <number>. Period.
gudeman> Frankly, I'm surprised to see representation come up at all,
gudeman> since I don't see what it has to do with the subject at hand
Because I am interested in <type> as in a computer, where all
denotations must be represented by a number which is some string of
digits base 256 (usually).
So, what's a type? Essentially, the (finite!) set of all the the bit
strings that a given set of functions can operate upon, because that's
about as good as you can get. Again, note that typeof maybe one of these
functions, and used by these functions as a filter, and then we have
<typing>, which is a far more restrictive idea.
gudeman> (unless you are thinking of <type> as a particular way of
gudeman> interpreting a group of bytes as a value. That doesn't fit
gudeman> your definition though.)
Well, it's close to that. Maybe we are reading different thing sin my
definition, maybe you are assuming that we have the same idea of what is
a <value>.
pcg> ... On the other hand if you encode the set into the value itself,
pcg> then you are defining types as equivalence classes, and your
pcg> definition and mine are exactly equivalent.
gudeman> Not at all. Your definition has the <type> of a value changing
gudeman> when new functions are added. Mine doesn't.
No, not necessarily, depends on two things: whether you add the new
functions to the group that defines the <type>, and then on whether the
new functions are more restrictive or not.
For example, if 'unsigned' is define by + and -, adding * does not
change the relevant set of values. But adding / does, because its
second argument can only belong to a set smaller than 'unsigned'.
So we have that bitstring '1' belongs to two <types> then.
Note: in the above the curried function view of multiple
argument is implicitly used.
gudeman> Then no separate notion of <typing> is needed.
pcg> ... The end result of your proposal is to drop the notion of <type>
pcg> and keep only that of <typing>, not viceversa.
gudeman> I'm throwing away your concept of <type>, as I said.
But I am trying to persuade you that <type> as distinct from <typing> is
a useful notion, at least as an ideal. Having a concept of <type> as I
see it does easily account for example for things like coercions,
multiple inheritance, and mutable typing, which are hard to explain well
if you only have <typing>.
pcg> ... when a new release of Unix switches the implementation of
pcg> /etc/passwd from a text file to an hash file, you need a program to
pcg> convert formats, whose role is essentially that of converting one
pcg> <type> to another to maintain ther illusion that <typing> has not
pcg> changed.
gudeman> The job of the program is to convert from one representation to
gudeman> another. The set of operations on the file has not changed, so
gudeman> the the <type> hasn't changed, by your definition.
No, it has, it has, because the set of operations also includes, and
here you miss I think one of the crucial points, the set of *encoding*
functions. I tried to be very clear about this; for example I have
written that under my definition
struct complex1 { float re,im; }
struct complex2 { float im,re; }
are two different *<types>*, even if their denotation is the same. Why?
Because the following two functions are *different*:
complex1 operator + (const complex1 a, const complex1 b)
{ return complex1(a.re+b.re,a+im,b+im); }
complex2 operator + (const complex2 a, const complex2 b)
{ return complex2(a.re+b.re,a+im,b+im); }
Because the .re and .im functions in each are *different*.
I think that you are missing that in my view of the world, given the two
struct definitions given above, .re and .im are two *functions* from 64
bit values into 32 bit values, and that they are overloaded on complex1
and complex2; something like:
float operator .re(complex1 c) { ... }
float operator .re(complex2 c) { ... }
float operator .im(complex1 c) { ... }
float operator .im(complex2 c) { ... }
Just to show you how radical is my approach, to me 32 bit floats are
just four digit base 256 numbers with peculiar definitions of +,-,...
(on most byte machines, that is).
gudeman> Again though, it looks like you mean <representation> when you
gudeman> say <type>.
Not the same thing, but it *includes* the encoding. I am not discussing
category theory, I am discussing <types> in computers languages, where
for example, all types are necessarily finite sets.
Maybe you are assuming that to me <value> == denotation, which is not
what I have written (this is a difficulty I have had with somebody else
by e-mail).
pcg> ... I know no language that IMNHO does a satisfactory job of
pcg> reconciling the gross rigidities of doing <typing> with the reality
pcg> that <types> are equivalence classes.
gudeman> Are you familiar with Common Lisp? What sort of type do you
gudeman> need that you can't define in that language?
In Common Lisp (minus CLOS) I can define the <typings> I want
(defstruct), but not <types>, not at all.
gudeman> How is an equivalence class different from a set? And what
gudeman> equivalence relation are you refering to?
We are really talking past each other. Let's say that at a level in
which unfortunately <typing> is already there, CLOS comes pretty close
to implementing my idea of types as equivalence classes of all values to
which a group of functions is applicable.
gudeman> It looks at times as though you mean $equiv(x,y) iff typeof(x)
gudeman> = typeof(y)$ -- which makes <type> and <typing> identical.
No, no, that is what *you* mean. That's <typing>:
gudeman> ... It also gives you a fairly simple way to define the result
gudeman> of the 'typeof' function(s)...
pcg> This is just <typing>, according to my terminology. You have thrown
pcg> away the notion of <type> entirely.
gudeman> Yeah, that's what I said. (I get the feeling we aren't
gudeman> communicating...)
I get the distinct feeling that while I see all your points, you disagree
because you don't see some of mine. It may be that maybe I have been too
terse :-).
--
Piercarlo Grandi | ARPA: pcg%uk.ac.aber@nsfnet-relay.ac.uk
Dept of CS, UCW Aberystwyth | UUCP: ...!mcsun!ukc!aber-cs!pcg
Penglais, Aberystwyth SY23 3BZ, UK | INET: pcg@aber.ac.uk
ram+@cs.cmu.edu (Rob MacLachlan) (03/16/91)
>>From: brnstnd@kramden.acf.nyu.edu (Dan Bernstein) >Subject: Re: Dynamic typing -- To Have and Have Not (was Runti >Date: 14 Mar 91 22:18:56 GMT > >For me compile time and typechecking are both important while I'm writing a >program. [...] Do you optimize programs while you're testing? Probably not. >But when each test run takes a noticeable amount of time, don't you wish >that you could make them run faster without wasting so much time on >optimization? > >Similarly, in a dynamically typed language, would you turn on strict >typechecking and other optimizations while you're testing? Probably not. I debug and test with typechecking on in Common Lisp, and so does everyone else I know. When properly supported (as in CMU Common Lisp), a powerful dynamic type system is a fairly general assertion mechanism, addressing consistency constraints outside the scope of conventional static typing systems. For example, you can: (declare (type (integer 3 27) i)) to say that I ranges from 3 to 47. And this assertion will be checked. The Common Lisp type system is general enough to express many interesting consistency constraints, but simple enough so that compilers can use it to do quite a bit of type inference. >But when you make type errors, don't you wish that you could have found >them without wasting so much time on optimization? Compilation speed is much less of a concern in environments that support incremental compilation. Although Lisp compilers tend to be rather slow compared to conventional compilers, Lisp seems faster because changes require much less recompilation. >Well, you could. All >you had to do was use static typing. Do your programs ever dump core? That's a run-time error check, just not a very graceful one. Most run-time type errors in Lisp systems are of the "dereferenceing off into space" sort, which can't be detected at compile time. Robert A. MacLachlan (ram@cs.cmu.edu)
brnstnd@kramden.acf.nyu.edu (Dan Bernstein) (03/18/91)
In article <1991Mar16.052952.10201@cs.cmu.edu> ram+@cs.cmu.edu (Rob MacLachlan) writes: > I debug and test with typechecking on in Common Lisp, and so does everyone > else I know. I can't afford to use Lisp either: I don't find its slight advantages in expressiveness to outweigh its slowness for all but the simplest programs. Sure, these tradeoffs between compile time, run time, compile space, run space, programming time, maintenance time, etc. will vary by project and programmer---but static typing appears to greatly reduce debugging time without hurting speed or space or effort for the vast majority of projects. Why not take what you can get for free? In contrast, the supposed conciseness of dynamically typed languages costs dearly in compile time, run time, and (for projects with many debugging runs) programming time. For these disadvantages it would have to provide a huge benefit for maintenance, yet its proponents never seem to come up with examples showing such a benefit. > >Well, you could. All > >you had to do was use static typing. > Do your programs ever dump core? That's a run-time error check, just not a > very graceful one. Yes, and one which could quite often have been prevented by stronger compile-time checks. ---Dan
ram+@cs.cmu.edu (Rob MacLachlan) (03/19/91)
>From: brnstnd@kramden.acf.nyu.edu (Dan Bernstein) >Subject: Re: Dynamic typing -- To Have and Have Not (was Runti >Date: 18 Mar 91 03:21:05 GMT > >I can't afford to use Lisp either: I don't find its slight advantages in >expressiveness to outweigh its slowness for all but the simplest >programs. > If you are writing programs that can't afford a to run 50% to 100% longer than a tense C implementation, then don't use Lisp. The advantage of Lisp (and of object-oriented programming) is not in efficiency, but in ease of evolving solutions to poorly understood problems. I agree that compile time type checking is a good thing -- the Python compiler that I wrote for CMU Common Lisp does compile-time type checking wherever possible. If you want to, you can write statically type-checked programs in Common Lisp. This will get a compile-time type warning in CMU CL: (defvar *var* '(a b c)) (declaim (list *var*)) (defun foo () (+ *var* 13)) As I see it, the main difference between CL and a language such as C is that CL knows that it doesn't always know the types of objects, whereas C pretends that it does. I think that dynamic typing is especially valuable in an object-oriented programming system, since OO programs intensively manipulate references to mutable objects. It is very difficult to do static type checking in such an environment. Determining the power of the type system is a language design decision: -- The more of the language semantics you bring into the type system, the more complex inferences you can do at compile time. -- It is impossible to bring all the language semantics into the type system, since the only way to really find out what a program is going to do is to run it (the halting problem, etc.) This means that: -- More powerful type systems offer more opportunities for optimization, but are also harder for compilers to understand. -- In any language, some programming errors can only be detected at run time. > [...] static typing appears to greatly reduce >debugging time without hurting speed or space or effort for the vast >majority of projects. Well, you say it's so, and I say it ain't... Static type checking detects superficial errors; errors that would be detected if you tested that branch in the code just once. Such bugs may be common, but fixing them is quite easy in any language. Here is a concrete example of a programming problem that exemplifies what programmers in any OO language spend most of their *time* debugging: (do ((current foo (foo-next current))) ((eq (foo-a current) 'yow!) current)) We search down a linked list of FOOs for a FOO whose A is YOW!. If for some reason, it isn't there, then we fly off the end of the list (and get a run-time error.) This is a nasty bug, because the problem isn't determining *that* we flew off the end of the list, the problem is determining *why* YOW! wasn't in the list. And the only relevant tool that current languages offer is run-time assertions: (assert (find-in #'foo-next 'yow! foo :key #'foo-a))) Of course, you can do run-time consistency checks in any language. The point is that for finding the hard bugs, that is what you end up doing in any language. >> Do your programs ever dump core? That's a run-time error check, just not a >> very graceful one. > >Yes, and one which could quite often have been prevented by stronger >compile-time checks. I do seem some potential in powerful type-inferencing systems such as in ML, but even in these languages, run-time assertions are important. Rob MacLachlan (ram@cs.cmu.edu)
gudeman@cs.arizona.edu (David Gudeman) (03/19/91)
In article <PCG.91Mar15201131@aberdb.test.aber.ac.uk> Piercarlo Antonio Grandi writes:
]gudeman> What does "meaningfully applied" mean? In a dynamically typed
]gudeman> language, any function can be legally applied to any value...
]
]Let's say then that a type is the intersection of the codomains of a
]given group of functions , or something like that.
OK, so what is the codomain of a function? You have only pushed off
the problem, not solved it.
]This is the problem of procedures with multiple arguments. We all know
]that it can be argued that functions with multiple arguments either have
]a single structured argument or are functionals...
Not unless the appropriate tuples or functionals are in the language.
Otherwise you have to deal with real multi-argument functions. In any
case, you still haven't said what you mean by "meaningfully applied".
I don't believe you can make it mean anything outside of a statically
typed language.
]...values and their denotations are *very*
]different things in my view, thus encoding (and representation) is
]crucial to my argument.
OK, this is what you seem to be saying
<value> -- a bitstring
<encoding> -- a rule for translating <value>s to values and vice versa.
<denotation> -- the value represented by some <value> under some
<encoding>.
<type> -- an <encoding>.
The obvious question is: why try to redefine two useful commonly
understood terms, namely <value> and <type> to mean things that
already have perfectly good terms, respectively "bitstring" and
"encoding"? I know, you claim that <encoding> is only a part of
<type>, but I don't see it. You just say that a certain set of
functions use a certain <encoding> to interpret their arguments, and
that these functions define a <type>. But that is no different than
saying that a <type> is an <encodig>.
[about 'eq, 'eql, and 'equal]
]No, all, except 'equal', are redundant. We have already been thru this
]discussion,
(not with me)
]and I still maintain that ideally if two values are the same
]number, they *must* represent the same denotation.
Why?
] Saying that they
]actually encode different denotations simply means, IMNHO, that actually
]part of the value has been encoded in its address, which is an
]efficiency hack.
First, it is unlikely that the same bitstring represents two different
values. Rather, two different bitstrings may represent the same
value. But this is low-level implementation detail, and shouldn't
enter into a discussion of semantic issues.
Semantically, Lisp objects can be mutable, and when you have mutable
objects there is a difference between identicality of objects and
equivalence of values. Furthermore the difference is important for
algorithmic reasons, not for efficiency reasons. When you have two
lists, A and B, and you want to know if a side effect to A might
change B, you can't find out with 'equal.
]gudeman> I wasn't making any assumptions about the representation of
]gudeman> values.
]
]Neither I am, but maybe I did not make it clear that to me <value> is
]the same as <number>. Period.
You are assuming that values (<denotations>) are represented as
numbers (actually bitstrings). You are also assuming that the same
bitstring must represent a different value for different types.
Neither assumption is reasonable.
]... I am interested in <type> as in a computer, where all
]denotations must be represented by a number which is some string of
]digits base 256 (usually).
I am interested in <type> as in programming languages, where you don't
have to specify the hardware representation of values. Your
definitions are no more than a formalization of the way types and
values are implemented in statically typed languages. They are not
suitable for discussion of more general types of programming languages
nor for discussion of high-level, abstract issues.
]... Having a concept of <type> as I
]see it does easily account for example for things like coercions,
]multiple inheritance, and mutable typing, which are hard to explain well
]if you only have <typing>.
These things are all quite trivial to explain using the notion that a
type is a set or a unary predicate.
--
David Gudeman
gudeman@cs.arizona.edu
noao!arizona!gudeman
kers@hplb.hpl.hp.com (Chris Dollin) (03/19/91)
Dan Bernstein writes:
In contrast, the supposed conciseness of dynamically typed languages
costs dearly in compile time, run time, and (for projects with many
debugging runs) programming time. For these disadvantages it would have
to provide a huge benefit for maintenance, yet its proponents never seem
to come up with examples showing such a benefit.
Perhaps Dan would like to explain why dynamically typed languages "cost dearly
in compile-time", since the compiler is performing fewer checks? I can think of
three possible interpretations:
(a) The compiler is slower, because it has to generate extra code for the
necessary run-time checks.
Planting a few extra procedure calls is unlikely to take as much time as (say)
doing ML-style type unification.
(b) The compiler is faster, *but* it will be called on to compile entities very
many more times as their trivial type errors are detected.
Removing type errors doesn't tka that long (by observation). Also, for entities
that are complex enough that they take several passes before their type errors
are removed dynamically, they'll probably take several passes through the
static checking compiler - trashing any presumptive speed advantage.
(c) The compiler is written in the language it compiles, hence is dynamically
typed, hence is slow.
Begs the question. Also the compiler (being a relatively fixed application) may
have had optimisations applied to it - such as optional type declarations - to
make it go faster. It may have had optimisations applied to it that are only
possible by virtue of being dynamically typed.
Dan being Dan, he probably has some alternative (i) in mind; perhaps he would
enlighten us.
--
Regards, Kers. | "You're better off not dreaming of the things to come;
Caravan: | Dreams are always ending far too soon."
oz@yunexus.yorku.ca (Ozan Yigit) (03/19/91)
In article <see ref> brnstnd@kramden.acf.nyu.edu (Dan Bernstein) writes: >I can't afford to use Lisp either: I don't find its slight advantages in >expressiveness to outweigh its slowness for all but the simplest >programs. Lisp or Scheme's advantages in expressiveness are not slight at all, but still, it is amusing to see you acknowledge the expressiveness of those languages you do not know much about _exceed_ those languages that you do know something about. Also, do you really understand what "expressiveness" mean? >In contrast, the supposed conciseness of dynamically typed languages >costs dearly in compile time, run time, and (for projects with many >debugging runs) programming time. Dan, you have no idea what you are talking about. oz
tmb@ai.mit.edu (Thomas M. Breuel) (03/20/91)
In article <22032@yunexus.YorkU.CA>, oz@yunexus.yorku.ca (Ozan Yigit) writes: |> >In contrast, the supposed conciseness of dynamically typed languages |> >costs dearly in compile time, run time, and (for projects with many |> >debugging runs) programming time. |> |> Dan, you have no idea what you are talking about. I wouldn't be quite so harsh. Static type checking is very good at eliminating a large fraction of those mistakes that people commonly make. As a side-benefit, simpler compilers are able to generate better code if type information is available at compile time. To me, polymorphic statically typed programming languages like ML are currently the best compromise between flexibility, compile-time checking, and efficiency.
brnstnd@kramden.acf.nyu.edu (Dan Bernstein) (03/20/91)
In article <22032@yunexus.YorkU.CA> oz@yunexus.yorku.ca (Ozan Yigit) writes: > Lisp or Scheme's advantages in expressiveness are not slight at all, but > still, it is amusing to see you acknowledge the expressiveness of those > languages you do not know much about _exceed_ those languages that you do > know something about. Indeed, and I'm glad to hear that you've stopped beating your children. Of the languages that I've used much, I find Forth the most expressive. Lisp and C come in way below, and several other languages (which, naturally, I've given up on) compete with Pascal and Fortran for the bottom of the bucket. I don't know enough about Scheme to judge it, but by all accounts it is more expressive than Fortran or C. Yet I continue to use C (and, when necessary, Fortran) much more than any other. Why? Because Forth is not portable, Lisp on anything but a Symbolics is so slow that my test runs often take ten times as long, and Ada compilers are snails. Expressiveness is nice. Anything syntactic is nice. But I don't need niceties. I need portability. I need fast compile times and run times so that the machine's turnaround time during testing and debugging doesn't become a significant part of my turnaround time. I need language power: full access to what the machine can do. Lisp doesn't have any of this. > Also, do you really understand what "expressiveness" mean? Yes, I think so. Do you? > >In contrast, the supposed conciseness of dynamically typed languages > >costs dearly in compile time, run time, and (for projects with many > >debugging runs) programming time. > Dan, you have no idea what you are talking about. Actually, I have a reasonably good idea of what I'm talking about. My comments on dynamically typed languages are based not only on my experience but also on many objective and subjective articles by both detractors from and proponents of such languages. As a matter of fact, if you want to buck the establishment, it's your problem to prove that dynamically typed languages aren't as inefficient as most experiments have found them to be. Would you write a compressor in a dynamically typed language? ---Dan
ram+@cs.cmu.edu (Rob MacLachlan) (03/20/91)
Subject: Re: blip [Re: Dynamic typing -- To Have and Have Not ...] Date: 19 Mar 91 23:59:35 GMT >Of the languages that I've used much, I find Forth the most expressive. Lisp >and C come in way below [...] Yet I continue to use C (and, when necessary, >Fortran) much more than any other. Why? Because Forth is not portable, Lisp >on anything but a Symbolics is so slow that my test runs often take ten times >as long, and Ada compilers are snails. Are you doing number crunching? If so, non-Symbolics Lisp products perform badly. But for other problems, Lispms have been surpassed in speed by Lisps running on conventional workstations (MIPS, SPARC, etc.) And CMU Common Lisp provides better-than-Lispm safety and debuggability with better-than-Lispm speed (not to mention cost-effectiveness.) CMU CL also offers good number crunching performance in real programs (5x Allegro.) >I need portability. I need fast compile times and run times so >that the machine's turnaround time during testing and debugging doesn't >become a significant part of my turnaround time. I need language power: >full access to what the machine can do. Lisp doesn't have any of this. I think that you overstate the case. Common Lisp is at least as portable as C, and Lisp systems offer unsurpassed compile-debug turnaround times (through incremental compilation.) "Full access to what the machine can do" is somewhat more nebulous. If you mean writing device drivers, then Lisp is not for you. But if you just mean that you want to use all the machine's primitive datatypes with reasonable efficiency, then Lisp *can* do this (although not all implementations provide as consistent support as CMU CL.) Common Lisp certainly wins big compared to, say Pascal, in that it has about all the operations that hardware implements (boolean arithmetic, bit-vectors, decode-float, all the flavors of division, etc.) >[...] if you want to buck the establishment, it's your problem to prove that >dynamically typed languages aren't as inefficient as most experiments have >found them to be. Some statements I am willing to defend: -- For some uses, efficiency isn't everything (research, prototyping, development.) -- For the things that C, Pascal, Modula, C++, etc. are used for, it is almost always possible to get a Lisp program to come withing a factor of two of the performance of these more conventional languages. (I am confining myself to general or "systems" programming, as opposed to data processing and scientific computing.) -- It can be very, very hard to get this good performance, but many of the performance tar-pits can be reliably negotiated when there is good compiler feedback. >Would you write a compressor in a dynamically typed language? If you mean something like the Unix system program "compress", then I wouldn't. But if your goal was to compress a complex input (like natural language) down to the smallest amount of space, with run-time relatively unimportant, then Lisp would be a good candidate. Rob MacLachlan (ram@cs.cmu.edu)
cs450a03@uc780.umd.edu (03/20/91)
Chris Dollin writes: >Perhaps Dan would like to explain why dynamically typed languages >"cost dearly in compile-time", since the compiler is performing fewer >checks? I can think of three possible interpretations: ... You missed one: That the compiler was thrown together in a "short" period of time, etc. And another: Because the compiler is intended to optimize the hell out of critical sections of code, it spends quite a bit of CPU on optimizing. Another one: That there is a poor match between language operations and machine architecture. Part of the fix for this is coding style, but it still feeds the other problems (makes them nice, healthy, well-fed problems). C'Est la vie. Raul Rockwell
brnstnd@kramden.acf.nyu.edu (Dan Bernstein) (03/20/91)
In article <KERS.91Mar19082354@cdollin.hpl.hp.com> kers@hplb.hpl.hp.com (Chris Dollin) writes: > Perhaps Dan would like to explain why dynamically typed languages "cost dearly > in compile-time", since the compiler is performing fewer checks? You're right, I should have said ``costs dearly in either compile time or run time, and in either case (for projects with many debugging runs) programming time.'' My point is that I can't afford to make that choice except for projects where the compile time or run time is going to be very small no matter what language I use. That's rarely the case. ---Dan
brnstnd@kramden.acf.nyu.edu (Dan Bernstein) (03/20/91)
In article <1991Mar20.041716.4486@cs.cmu.edu> ram+@cs.cmu.edu (Rob MacLachlan) writes: > I think that you overstate the case. Common Lisp is at least as portable as > C, What world do you live in? About two-thirds of the UNIX systems I use can't even compile any of the available Lisps. In any case, portability is defined by what's out there, not what could be out there; and about 95% of the machines I use do *not* support Lisp even if they *could*. > and Lisp systems offer unsurpassed compile-debug turnaround times (through > incremental compilation.) ``Unsurpassed'' is exaggeration, and even if compile times were instant I'd spend forever just waiting for most programs to run. > But if you just mean that you want to use all the machine's > primitive datatypes with reasonable efficiency, No. A machine is much more than its ``primitive datatypes.'' But Lisp doesn't even provide full access to pointers. > >[...] if you want to buck the establishment, it's your problem to prove that > >dynamically typed languages aren't as inefficient as most experiments have > >found them to be. > -- For some uses, efficiency isn't everything (research, prototyping, > development.) In fact, I've been focusing on the prototyping and development stage of a program, because that's when it's most important to get good compile times *and* run times. ---Dan
brnstnd@kramden.acf.nyu.edu (Dan Bernstein) (03/20/91)
In article <896@optima.cs.arizona.edu> gudeman@cs.arizona.edu (David Gudeman) writes: > In article <11820:Mar1923:59:3591@kramden.acf.nyu.edu> Dan Bernstein writes: > ]Of the languages that I've used much, I find Forth the most expressive. > Urp. OK, now I know you have a completely different concept of > expressiveness than I do. As they say, de syntactic gustibus non disputandum. Or something like that. It's natural that different people should find expressions more natural in different languages. What language do you find most expressive? Let me guess: BCPL? :-) ---Dan
quale@picard.cs.wisc.edu (Douglas E. Quale) (03/20/91)
In article <11820:Mar1923:59:3591@kramden.acf.nyu.edu> brnstnd@kramden.acf.nyu.edu (Dan Bernstein) writes: > >Actually, I have a reasonably good idea of what I'm talking about. My >comments on dynamically typed languages are based not only on my >experience but also on many objective and subjective articles by both >detractors from and proponents of such languages. As a matter of fact, And Dan also claims that debugging and program development is faster in a statically typed language than in a dynamically typed language. Since you claim your beliefs are based on your vast knowledge of the applicable literature, please give us a reference supporting this dubious claim. In another article Dan claims that Gnu Emacs is mostly written in C, "with a small amount of helper code." This is absolutely false. -- Doug Quale quale@khan.cs.wisc.edu
oz@yunexus.yorku.ca (Ozan Yigit) (03/21/91)
In article <see ref> brnstnd@kramden.acf.nyu.edu (Dan Bernstein) writes: >Indeed, and I'm glad to hear that you've stopped beating your children. I have no children, and likewise the suggested fallacy does not exist. My observation is based on your past and present statements indicating significant lack of insight into other programming languages you like to _talk_ about, e.g.: | No. A machine is much more than its ``primitive datatypes.'' But Lisp | doesn't even provide full access to pointers. Imagine that! ;-) >Of the languages that I've used much, I find Forth the most expressive. Good for you. It must be all the nice syntax. > I need fast compile times and run times so >that the machine's turnaround time during testing and debugging doesn't >become a significant part of my turnaround time. Use the right compiler [and the right machine] for fast compile times, right language for prototyping, and right environment for testing and debugging. [yawn] > I need language power: full access to what the machine can do. You seem to be confusing language "power" with language "level". > Lisp doesn't have any of this. Is this "have" the same as your previously re-defined "have" to mean something in relation to general computability, or is this something more meaningful and useful? >Actually, I have a reasonably good idea of what I'm talking about. My >comments on dynamically typed languages are based not only on my >experience but also on many objective and subjective articles by both >detractors from and proponents of such languages. Of course. Just as interesting, the neighbourhood cabbie was telling me about how he gave a ride to Elvis, and he had witnesses to prove it. So? >Would you write a compressor in a dynamically typed language? Silly question, despite its answer. 1988, compress.lisp [UN*X compress in Common Lisp] by Paul Fuqua, done. oz --- In seeking the unattainable, simplicity | Internet: oz@nexus.yorku.ca only gets in the way. -- Alan J. Perlis | Uucp: utai/utzoo!yunexus!oz
brnstnd@kramden.acf.nyu.edu (Dan Bernstein) (03/21/91)
In article <22075@yunexus.YorkU.CA> oz@yunexus.yorku.ca (Ozan Yigit) writes: > In article <see ref> brnstnd@kramden.acf.nyu.edu (Dan Bernstein) writes: > | No. A machine is much more than its ``primitive datatypes.'' But Lisp > | doesn't even provide full access to pointers. > Imagine that! ;-) It doesn't. I can't even find a Lisp for the Convex that takes advantage of array indexing: in C, p[i] runs as fast as *p on that machine, but since Lisp doesn't truly grok pointers it falls flat. Or is it heresy in your faith to even conceive of the idea that Lisp doesn't understand pointers as well as C? > > I need fast compile times and run times so > >that the machine's turnaround time during testing and debugging doesn't > >become a significant part of my turnaround time. > Use the right compiler [and the right machine] for fast compile times, Oh, what a *useful* suggestion. ``Your programs don't run fast enough? Buy a Cray.'' Believe it or not, even on the Cray, the compiler takes a noticeable amount of time, and there do exist programs that run for longer than a second. > > I need language power: full access to what the machine can do. > You seem to be confusing language "power" with language "level". No. Ada is in some ways more powerful than C: among its primitives, for example, are tasks. This doesn't make it higher-level or lower-level than C; it just adds some power, by providing more access to what the machine (in this case, OS) can do. > > Lisp doesn't have any of this. > Is this "have" the same as your previously re-defined "have" to mean > something in relation to general computability, or is this something > more meaningful and useful? Does ``any of this'' refer to some semantic feature of a programming language? No. Therefore my previously defined ``have'' does not apply here. You understand overloaded operations; why don't you understand overloaded words? > Of course. Just as interesting, the neighbourhood cabbie was telling me > about how he gave a ride to Elvis, and he had witnesses to prove it. So? Hey, bud, you started. > >Would you write a compressor in a dynamically typed language? > Silly question, despite its answer. 1988, compress.lisp [UN*X compress in > Common Lisp] by Paul Fuqua, done. You didn't answer the question. Would you write a compressor in a dynamically typed language? I wouldn't, because each compile run takes a noticeable amount of time, and each test run takes a noticeable amount of time. If I used a dynamically typed language, I'd lose big on either compile time, run time, or both. That can mean the difference between a week and a month in project turnaround time, not to mention a slower final program. The dynamic-typing people claim that I'll get it all back in maintenance, because my program will be shorter. I don't believe them: dynamic typing wouldn't simplify my huptrie.h, for example. They claim that I won't have as many testing and debugging runs. Sorry, but it just isn't true: practically every bug I found and behavior I changed would have been found and changed the same way in most languages, and I have the project logs to prove it. Maybe it's a silly question, but for me it exemplifies what's wrong with dynamically typed languages. ---Dan
kers@hplb.hpl.hp.com (Chris Dollin) (03/21/91)
Dan tails with: Would you write a compressor in a dynamically typed language? Odd you should ask that, but when I was trying to understand LZ compression, I did just that. It was fast enough. For a production version, I would have tightened up the code, using my knowledge of the properties of the program - including types, of course. [Actually I rewrote it in C because Pop11 wasn't - and alas, still isn't - available on my home machine, but C is. Over the next year or so my Pop clone should gradually start to generate native code; it will be an interesting experiment to compare speeds then.] -- Regards, Kers. | "You're better off not dreaming of the things to come; Caravan: | Dreams are always ending far too soon."
kers@hplb.hpl.hp.com (Chris Dollin) (03/21/91)
Dan replies: You're right, I should have said ``costs dearly in either compile time or run time, and in either case (for projects with many debugging runs) programming time.'' My point is that I can't afford to make that choice except for projects where the compile time or run time is going to be very small no matter what language I use. That's rarely the case. OK, but I'm still puzzled as to why you included the option of dearer compile-time costs at all. Can you illustrate a case where a compiler for a dynamically-typed language is *slower* than a compiler for a statically typed language - when the compiler is written in the same language for both (ie, we're comparing apples and apples)? As it happens, I have been writing a program in a dynamically-checked language recently - a typechecker [ironic, eh?] for a specification language. I am happy to report that the number of *type* errors that occurred was trivially small; perhaps once there was a type error that took me longer then fiven minutes to track dowm. [Usually it's a case of "bugger, forgot to change the call of rename_sigentry in copy_module"; static typechecking would have caught it about thirty seconds earlier.] I am also happy to report that the genuine logical errors could not have been caught by anything short of a full theorem prover operating from a fully correct abstract specification. [I just timed how long it took to load the complete typechecker into the system, starting from Unix command-line prompt and including the time it took to type "load loader.p" to the editor; 1min 6sec for compiling 8000 lines of source to MC68K machine code, on an HP9000/350. This time includes the program setup-time (it constructs various tables of built-in identifiers on the way, and reads a file describing the parse-tree magic numbers). Compiling the database component from the toolset - written in C with a small Yacc grammar - took 8 minutes for about 12000 lines of code, not including the time it would take to build the standard library components; same machine, of course. The figures are open to interpretation, but they're datapoints.] [By the way, Dan, if you find Forth "expressive", you might like Pop; Forth's open stack, sort-of Lispy datatypes, conventional syntax.] -- Regards, Kers. | "You're better off not dreaming of the things to come; Caravan: | Dreams are always ending far too soon."
lavinus@csgrad.cs.vt.edu (03/22/91)
Hello, Type warriors... I was meaning to stay out of this, but alas... Do people out there really think that any one language is good for all applications? Obviously, if you want to write a UN*X device driver, you do it in C, and if you want to do heavy number crunching, you do it in Fortran (for speed), or better yet, in Occam or something on a multiprocessor. There are some applications for which dynamic typing makes programming infinitely easier, and there are some for which dynamic typing gains you little, and thus is not worth the efficiency hit (which is often minor - compare programs written in C and Yale's T dialect of Lisp/Scheme sometime). Aside from all that, expressiveness is more a matter of taste than anything else - some people just naturally think in one paradigm or another. The arguments are thus rendered rather pointless - it's not as though when this argument is won, we're going to remove from existence all languages which belong to the losing side (that's assuming the argument *can* be won). It all comes down to something like, "Oh yeah, well my Scheme compiler can beat up your C compiler..." :-) Ah well, on with the flames... Joe -- _______________________________________________________________ _ _ __ Joseph W. Lavinus (lavinus@csgrad.cs.vt.edu) | / \ |_ Virginia Tech, Blacksburg, Virginia __| \_/ |_
amanda@visix.com (Amanda Walker) (03/22/91)
brnstnd@kramden.acf.nyu.edu (Dan Bernstein) writes: > | Lisp doesn't even provide full access to pointers. > Imagine that! ;-) It doesn't. That depends on the Lisp. T, for example, not only has a very good compiler, but provides a data type called a "locative" that acts like a pointer, but doesn't even get confused when GCs happen. Most modern Lisps provide just as much machine-level access as, say, C does. Or is it heresy in your faith to even conceive of the idea that Lisp doesn't understand pointers as well as C? In my case, it's more experience than faith. Have you actually looked at what's been going on in the Lisp world over the past 5-7 years? You might be very surprised. Would you write a compressor in a dynamically typed language? Yes. In fact, I'd *rather* write a compressor in a dynamically typed language. I wouldn't, because each compile run takes a noticeable amount of time, and each test run takes a noticeable amount of time. That has mostly to do with the particular compiler you are using, and not much to do with any inherent properties of the language itself. The fact that C may be easier to compile (for some value of "easier") than Lisp is a separate claim, and one I would be happy to concede. However, ease of compilation is not the constraining factor in software development. If it were, we'd all be using assembler. Not that C is very much of an improvement, mind you... Maybe it's a silly question, but for me it exemplifies what's wrong with dynamically typed languages. From what you've said so far, it sounds like descriptions of what's wrong with the state of much the commercial dynamically-type language market. On that I have no argument, but I think that blaming this on the fact that a language is dynamically typed is bordering on religious belief. -- Amanda Walker amanda@visix.com Visix Software Inc. ...!uunet!visix!amanda -- "Many of the truths we cling to depend greatly on our point of view." --Obi-Wan Kenobi in "The Empire Strikes Back"
kend@data.UUCP (Ken Dickey) (03/23/91)
{Warning: I have not been following this thread} brnstnd@kramden.acf.nyu.edu (Dan Bernstein) writes: >... Would you write a compressor in a >dynamically typed language? I wouldn't, because each compile run takes a >noticeable amount of time, and each test run takes a noticeable amount >of time. If I used a dynamically typed language, I'd lose big on either >compile time, run time, or both. That can mean the difference between a >week and a month in project turnaround time, not to mention a slower >final program. Interesting. I don't know what languages you typically use [Pascal, Eiffel, C?], but find many more good environments for fast program development in dynamically typed languages. If you want fast code, there are some good Scheme compilers around. Of course, I write software systems, not just programs. If you are doing device drivers, and really require speed, make use of your local assembler and the hardware caches. -Ken Dickey kend@data.uucp
brnstnd@kramden.acf.nyu.edu (Dan Bernstein) (03/26/91)
In article <KERS.91Mar21121931@cdollin.hpl.hp.com> kers@hplb.hpl.hp.com (Chris Dollin) writes: > OK, but I'm still puzzled as to why you included the option of dearer > compile-time costs at all. Can you illustrate a case where a compiler for a > dynamically-typed language is *slower* than a compiler for a statically typed > language - when the compiler is written in the same language for both (ie, > we're comparing apples and apples)? Many people have spent many years trying to optimize dynamically typed languages (i.e., to get rid of the dynamic typing in the object code), with some success. When a compiler *doesn't* optimize a dynamically typed language, the final code is hellishly slow (as in most Lisps). ---Dan
rick@cua.cary.ibm.com (Rick DeNatale) (03/26/91)
In article <14160@life.ai.mit.edu> tmb@ai.mit.edu writes: >I wouldn't be quite so harsh. Static type checking is very good >at eliminating a large fraction of those mistakes that people >commonly make. There seems to be a discrepancy between reporters on this issue that is language dependent. Type errors seem to be a big source of errors in C but seem to appear rarely in Smalltalk programs. At least this is true in my own experiences and others that I have talked to agree. In Smalltalk the most common errors seem to be object state problems. I think that there is a reason for this, and it has to do with the purity of object orientation extending down to the implementation/realization level. In most 'normal' programming languages, a type error occurs when you present a string of bits to a piece of code that expects to interpret that string of bits in a particular way (short integer, float, char *, class foo), and the bits aren't actually the right type of bits. In a pure object oriented implementation, you just can't give the wrong string of bits to a piece of code, the method dispatching sees to that. I'm actually a little bit surprised that people put so much faith in compile time type checking systems that they are willing to accept an implementation that allows type errors that escape the compiler to cause wild branches and other unsavory acts, and then demand that such stuff is required for "industrial strength". I haven't seen a strong typing system yet that doesn't require you to (hopefully carefully) circumvent it at times, or that is absolutely bullet proof even without circumvention (array bounds checking, overflows etc.). It takes real confidence to work in dangerous environments without safety equipment. Or maybe it's a less desirable quality than confidence! Rick DeNatale
kers@hplb.hpl.hp.com (Chris Dollin) (03/26/91)
Dan responds: In article [one of mine] Chris Dollin writes: > OK, but I'm still puzzled as to why you included the option of dearer > compile-time costs at all. Can you illustrate a case where a compiler for a > dynamically-typed language is *slower* than a compiler for a statically typed > language - when the compiler is written in the same language for both (ie, > we're comparing apples and apples)? Many people have spent many years trying to optimize dynamically typed languages (i.e., to get rid of the dynamic typing in the object code), with some success. When a compiler *doesn't* optimize a dynamically typed language, the final code is hellishly slow (as in most Lisps). Sorry, Dan, but this doesn't answer my question - can you tell us about a case where a compiler in language L for a dynamically typed language D was slower than a compiler in L for a statically typed language S, because of the absence of compile-time type-testing? As for ``the final code is hellishly slow'' when the compiler does not attempt to optimise out dynamic typing, what sort of factor are you talking about? 2? 10? 100? [I'm prepared to pay a factor of 2, I'd be reluctant about a factor of 10, and would regard a factor of 100 as dreadful.] As one of my earlier postings remarked, the Pop compiler (written in Pop11, a DTL) compiles Pop code about 5 times faster than an HP-UX ANSI C compiler (written in C, an STL). All other things are grossly unequal, of course, because the C compiler is repeatedly re-reading header files because of separate compilation, and because we are liberal with our use of repeated include files; if all that accounts for a factor of 5 I'd be surprised. I presume you mean that ``optimising out dynamic typing can be slow''. Sure. Optimising compilers can be slow - gcc takes what seems an outlandishly long time compiling one of my modules, because it's a single 800 line procedure. (The Acorn C compiler is even slower on this example.) More will follow when I've time to gather my thoughts ... -- Regards, Kers. | "You're better off not dreaming of the things to come; Caravan: | Dreams are always ending far too soon."
brnstnd@kramden.acf.nyu.edu (Dan Bernstein) (03/27/91)
In article <KERS.91Mar26111126@cdollin.hpl.hp.com> kers@hplb.hpl.hp.com (Chris Dollin) writes: > Sorry, Dan, but this doesn't answer my question Look, static typing gives me both fast compile times and fast run times. Dynamic typing used to lose big on run times; all the interesting recent work on dynamically typed languages has been in optimization, but for those reasonably competitive run times they lose big on compile times. Why should I pay that price, especially during development when I need both fast compiles and fast runs for good turnaround? > As for ``the final code is hellishly slow'' when the compiler does not attempt > to optimise out dynamic typing, what sort of factor are you talking about? 2? > 10? 100? Between 2 and 10. It usually depends on how many of the library routines have been written in a faster language. ---Dan
jls@rutabaga.Rational.COM (Jim Showalter) (03/27/91)
>I'm actually a little bit surprised that people put so much faith in compile >time type checking systems that they are willing to accept an implementation >that allows type errors that escape the compiler to cause wild branches and >other unsavory acts, and then demand that such stuff is required for >"industrial strength". Who are these people? I'D certainly never accept such an implementation. I demand strong compile time checking AND a validated compiler. But then. that's why I prefer to work in Ada. >I haven't seen a strong typing system yet that doesn't require you to >(hopefully carefully) circumvent it at times, Agreed. But the point is that in a properly designed language you have to go out of your way (by design) to effect such circumvention. >or that is absolutely bullet >proof even without circumvention (array bounds checking, overflows etc.). Ada may not be completely bulletproof, but the examples you cite it certainly detects and traps. -- ***** DISCLAIMER: The opinions expressed herein are my own. Duh. Like you'd ever be able to find a company (or, for that matter, very many people) with opinions like mine. -- "When I want your opinion, I'll read it in your entrails."
rick@cua.cary.ibm.com (Rick DeNatale) (03/28/91)
In article <jls.670045356@rutabaga> jls@rutabaga.Rational.COM (Jim Showalter) writes: >>I'm actually a little bit surprised that people put so much faith in compile >>time type checking systems that they are willing to accept an implementation >>that allows type errors that escape the compiler to cause wild branches and >>other unsavory acts, and then demand that such stuff is required for >>"industrial strength". > >Who are these people? I'D certainly never accept such an implementation. >I demand strong compile time checking AND a validated compiler. But then. >that's why I prefer to work in Ada. > >>I haven't seen a strong typing system yet that doesn't require you to >>(hopefully carefully) circumvent it at times, > >Agreed. But the point is that in a properly designed language you have >to go out of your way (by design) to effect such circumvention. > So even with a validated compiler, you still need validated programmers, or you have to limit what you can do. Rick Denatale ** Of course my opinion is my own, who else would have it!
px@fct.unl.pt (Joaquim Baptista [pxQuim]) (04/02/91)
In article <879@puck.mrcu> paj@mrcu (Paul Johnson) writes:
On the other hand I am interested in the assertion that type errors
are rare in Smalltalk development. Does anyone have any statistics to
back this up? I think this discussion could probably do with an
injection of fact, lest it degenerate into a language flame war.
I do not have any hard data, but I believe that I can give you a give
argument for it.
In a strongly typed language such as Eiffel, the programmer must
declare the type of all its variables, arguments, and such. If the
programmer later changes its mind, these type declarations must be
updated everywhere, which is a tedious and error-prone process.
Having no types just means that this sort of error does not happen,
while the other kinds of error probably remain at the same level.
--
Joaquim Manuel Soares Baptista, aka px@fct.unl.pt, px@unl.uucp
Snail: CRIA, UNINOVA, FCT/UNL, 2825 Mt Caparica, Portugal
So long, and thanks for all the fish.
pallas@eng.sun.com (Joseph Pallas) (04/03/91)
In <PX.91Apr1225918@hal.fct.unl.pt> px@fct.unl.pt (Joaquim Baptista [pxQuim]) writes: >Having no types ... Nope, try again. How about, "Having no declared types ..." >just means that this sort of error does not happen, Nope, try again. How about, "just means that this sort of error is not detected at compile time, but at run time," >while the other kinds of error probably remain at the same level. People who think that dynamically typed languages result in typeless programs need to reflect on what they are doing when they write their programs. If your program operates on some object, you can be certain that there is a type implied by that operation. joe
new@ee.udel.edu (Darren New) (04/03/91)
In article <879@puck.mrcu> paj@uk.co.gec-mrc (Paul Johnson) writes: >On the other hand I am interested in the assertion that type errors >are rare in Smalltalk development. Does anyone have any statistics to >back this up? Well, I have one statis. :-) My dissertation project is implemented in Smalltalk. I've been working on it for four years or so, and I've gotten three types of type-mismatch errors (i.e., "message not understood"): 1) Looked up a name in the wrong dictionary, didn't check the result because I "knew" it would always be there, sent the result (nil, because it wasn't found) a message, and "nil" didn't understand it. Not a type error because the problem was the wrong dictionary, not the wrong type. Some other runtime error (subscript out of bounds, say) would have happened in another language. 2) Sent a message to a string instead of to the result of looking up that string in a dictionary. Always caught on the first test run. 3) Sent a message to an object which should have implemented that message but didn't because I didn't think it would be called on this particular test run. Always caught the first time. In summary, "wrong type" errors just never really came up because they were the wrong type except number 2. In that case, it was always invariably caught on the first run of that method. -- Darren -- --- Darren New --- Grad Student --- CIS --- Univ. of Delaware --- ----- Network Protocols, Graphics, Programming Languages, FDTs ----- +=+=+ My time is very valuable, but unfortunately only to me +=+=+ + When you drive screws with a hammer, screwdrivers are unrecognisable +
px@fct.unl.pt (Joaquim Baptista [pxQuim]) (04/03/91)
REPOST WITH CORRECTION: In a strongly typed language such as Eiffel, the programmer must declare the type of all its variables, arguments, and such. If the programmer later changes its mind, these type declarations must be updated everywhere, which is a tedious and error-prone process. ---vv--- Having no declared types just means that this sort of error does not happen, while the other kinds of error probably remain at the same level. REPLY TO JOSEPH PALLAS: In article <pallas.670613621@red-dwarf> pallas@eng.sun.com (Joseph Pallas) writes: In <PX.91Apr1225918@hal.fct.unl.pt> I wrote: >Having no types ... Nope, try again. How about, "Having no declared types ..." Thank you. That was what I wanted to say. >just means that this sort of error does not happen, Nope, try again. How about, "just means that this sort of error is not detected at compile time, but at run time," >while the other kinds of error probably remain at the same level. People who think that dynamically typed languages result in typeless programs need to reflect on what they are doing when they write their programs. If your program operates on some object, you can be certain that there is a type implied by that operation. My previous posting obviously was ill written if people can interpret it to mean something that I do not think at all. Programmers ALWAYS think in types, even when the language is typeless (in the sense of having a single universal "type", as Lisp or Prolog). An entirely different matter is wether the programmer should be required to declare the types of everything. I state that doing this is error-prone, specially when the programmer changes some type. Type declarations detect type errors, but these errors tend to be "book keeping errors", instead of errors in the logic of the algorithm or some other "higher" errors. About compile time versus run tim