Newsgroups: comp.lang.misc Path: utzoo!utgpu!news-server.csri.toronto.edu!rpi!zaphod.mps.ohio-state.edu!wuarchive!decwrl!stanford.edu!neon.Stanford.EDU!brm From: brm@neon.Stanford.EDU (Brian R. Murphy) Subject: Re: Dynamic typing (part 3) Message-ID: <1991Apr22.234707.7023@neon.Stanford.EDU> Keywords: type inference static dynamic lisp ml Sender: brm@cs.stanford.edu Organization: Computer Science Department, Stanford University, Ca , USA References: <1991Apr9.021700.2688@neon.Stanford.EDU> <3092@opal.cs.tu-berlin.de> Date: Mon, 22 Apr 1991 23:47:07 GMT Lines: 169 In article <3092@opal.cs.tu-berlin.de> wg@opal.cs.tu-berlin.de writes: >[LANGUAGE WARNING: You might going to see a lot of spelling errors, > notion errors, style errors, etc.] > >brm@neon.Stanford.EDU (Brian R. Murphy) writes: > > >My complaint about statically typed languages is that I _can't_ do > >some things in them that I _do_ in dynamically typed languages (such > >as Lisp). For example, I can't I write a function which returns > >either a boolean or an integer in a complex way. I can't write my own > >Y combinator. I can't write a function which allows either a sequence > >of functions which range over numbers or a sequence of numbers as an > >argument. > >Your examples are quite abstract. Maybe its missing phantasy >-- but i cannot imagine the use of a function which returns >either a boolean or an integer unless there is some relationship >between this values, e.g. the boolean is an error value and >normally an integer is expected, in which case either subsorting >(multiple supersorts, of course) or parameterization is sufficient. I fail to see how parameterization is relevant. But yes, the case of of a possible error value _is_ a more common case of my example. For example, a Lisp expression such as (if (= y 0) (error) (/ x y)) would have type [something like] (error + int). So either we must automatically include errors in every base type, introduce an explicit union declaration in just about every function, or complicate things a bit by going to a continuation-passing semantics. Other examples besides errors _do_ come up, though, so why not just adopt a more general solution? >And i see no need to write your own Y combinator, unless you use a >language for a ground course in lambda calculus. (BTW, you can >model it in strongly typed functional languages using recursive >data types; of course a bit cumbersome). Almost any sort of `meta-interpreter/compiler' (a la Abelson & Sussman) which constructs functions in a generalized way contains expressions with recursive function types. The Y combinator was just a particularly simple example. Such translators are very convenient to be able to write. >I do not see the difference between primitive functions, in which >case you regard type safety as essential, and user defined onces. Well, to facilitate debugging, the primitive functions have to behave in some defined way on _every_ input, preferably some sort of error signal/report to the user. No static type system I know of can eliminate all run-time errors (cf divide by 0, array index out of bounds, disk full, out of memory, etc), so there must be a run-time mechanism. Similarly, there must be some mechanism for a user-defined partial function to signal an error when given bad arguments. The language designer cannot possibly anticipate which domains a user-defined function should be defined on, so the user must be given a way to _explicitly_ signal an error (for example, if that sorted input array turns out not to be sorted). Providing strong static typing is merely a partial solution; no type system/language can describe all function domains (sorted arrays, for example). An error-signalling mechanism must be there anyway. In a dynamically-typed language, the user has no static typing `crutch' to lean on: if he wants to signal errors on bad arguments, he can code his own test to discover these arguments; if not (perhaps on quick-and-dirty throwaway code), he can omit them. Presumably user-defined functions intended for reuse and use by others would signal an error on bad arguments. Type declarations cover certain cases, making it easy for programmers in statically-typed languages to believe more precise argument validity checks (is that array sorted?) aren't needed. [To be honest, I wouldn't really check an array for sortedness in most cases, but just specify what happens if it's not, but hopefully you get the idea.] > > [Thus] I want a type system which > > (a) allows me to omit many type declarations (where unnecessary) > > (b) allows me to be very specific in constraining some arguments > >Yes, i'am too. > > >With dynamic typing, I can simply write predicates to constrain > >arguments/variables (Common Lisp, FL, some implementations of Scheme > >do this). > >... but i regard it as an abuse of notion to call such dynamic >calculated predicates types. You can model them just using conditionals >and an error halt function. What Common Lisp gives you here is simply >syntactic sugar in an essential untyped environment, and some >(rather restricted) kind of partial evaluation of this >predicates. Actually, what Common Lisp gives you isn't adequate at all. It allows primitives to behave in an undefined way on arguments out of their specified domain. Perhaps I'm biased, but having been exposed to CLU and FL, which both have completely specified primitive functions (with specific errors signalled on bad arguments or other error conditions), I really don't like a language with incompletely determined behavior. Your program may work one minute, but not the next, or on a different machine, and there will be no way to tell why... > >You static typing advocates claim that a static type system can do > >this for me, but I claim that it can't. A type language powerful > >enough to constrain arguments anywhere near as precisely as I need > >won't allow me to omit many types. > >My experience is the other way around. It would allow me to omit >many types, and in some rare cases, if have to add declarations. Perhaps you don't demand as powerful a type language as I do... (arbitrary type union, intersection, dependent types, higher-order functions) >However, unless I'am going to hack a one-night program, i would >always declare signatures at least for the exported functions >of a module even if they could be inferred. This seems to me >a reasonable kind of documentation. So would I. But, as I keep saying, any type language which allows internal declarations to be inferred won't allow me to specify the interfaces with the precision I need... > >Type inference is only possible > >for a limited class of type languages, and they tend to be fairly > >weak. In addition, certain programs are forbidden simply because they > >utilize types which can't be described by the type language used. > >This is theoretically right. But the question is, how often >instances outside this limited, weak class are required in >practice? For example, tell me an instance of practical evidence >for the need of self application! I can't come up with a quick example of self-application. However, I have written Lisp programs for which no type inference system could ever figure out what arguments certain functions would be applied to---for example, a lookup table of functions, each constrained in a complex way by program control to only be applied to correct arguments. Sure, I could build a big union type describing what sorts of functions could be in the table, and explicitly extract one of the appropriate type, but that just increases the number of things I have to keep up-to-date when I modify the program and add a new function to the table. I'd rather not have to cope with that when first trying something out. >Whats irritating me more and more in this discussion (as long as >I'am following it; the last week) is that the advocates of dynamic >typing claim for more expressivness, which *looks* like >the arguments of practical men, but in fact seems to be >more of philosophical nature. (This would be quite alright, but say >it like it is.) Actually, I think the practical argument is this: In certain situations (such as in protyping), I want to program in a completely unconstrained way. If one way of expressing things makes things easier, I should be able do it. ANYTHING which forces me to think about my program in an unnatural way will distract me from the problem at hand. A static type system forces upon me someone else's idea of how things should be done. A dynamic type system leaves me more free to do what ever moves me. Later I can worry about going back and making things safe for other people to use, or more efficient, or whatever. >Wolfgang Grieskamp >wg@opal.cs.tu-berlin.de tub!tubopal!wg wg%opal@DB0TUI11.BITNET -Brian Murphy brm@cs.stanford.edu