Marco Antoniotti <marc...@cs.nyu.edu> wrote: > Dirk Thierbach wrote: > I never said that what we have is the RIGHT THING. I said that > CMUCL/SBCL (which *DOES* quite a bit of type inference) gets "close" to it.
And I disagree. *I* say that there are several ways to do it: One is CL with as much static type inference as you can get, and one is another language with polymorphic static typing built in right from the beginning. Both work, both have advantages and disadvantages (depending on your taste), and choosing between them is a trade-off.
> I have never stopped anybody from using O'Caml or Haskell or > whatever.
But you keep calling names.
> I am just stating my preference: Common Lisp + Type Inference. Now > it is up to you to convince me and others that dropping the Common > Lisp part is worth while.
But I don't want to convince you or others to drop CL. Why did you assume I wanted to do that? Why can there only be "one" best way?
> It is up to me to convice you that picking it up is worth while.
You don't have to convince me, I have programmed in CL, and I know that picking it up is worthwhile. Still, for most applications I prefer statically typed languages, because they fit my taste and my style of programming better. All I want you to do is to accept that.
> So, when is the last time you did some serious Common Lisp programming?
> And unlike static typing, the typical mode in which these > techniques are deployed is as an adjunct to a programming > language, not as built-in parts of programming languages > which refuse to run your code until you pass all their tests.
So your language of choice requires nothing of what you type in? What if your cat runs across the keyboard? Will that necessarily produce runnable code?
Of course you don't mean that. You have a firmly held opinion, and you're frustrated that others don't share it. I'm in great danger of making an analogy with certain politicians.
The only solid argument I've seen that static typing is significantly in the way came from one of the static typers (Was it Joachim?), who pointed out that it can sometimes take an annoyingly long time to finish propagating a change. The rest seem to be claims that a message queue could have lots of types of message, which is conveniently done as a list of heterogeneous types, and can as easily be done as a list of a sum type, or heterogeneous lists used where a tuple is really the right data structure.
Personally, I can see the value in both approaches. Which means that I agree with your calm, deliberative opinion that static typing is not the ultimate good, while I disagree with your hysterical opinion that it is necessarily bad.
Dirk Thierbach wrote: > Good. As has already been mentioned, Haskell is certainly not perfect, > but having a look at it doesn't hurt. The most difficult part to get > used to is its lazyness, and as consequence the restriction of side > effects into these things with the ugly and frightening name Monads.
I always thought the word "monad" was quite pretty in itself. Probably depends what syllable you put the stress on when speaking. I probably say it wrong...
Also, remember that Lisp is fundamentally a language for defining languages (usually with sexp-syntax, of course). AFAIK, there IS a completely statically-typed language built with Lisp, though the name escapes me (had Q in it maybe?).
The best lisp approach to haskell might be to make a haskell-like sublanguage IN lisp, similarly to the lisp approach to prolog, or the lisp approach to SQL, or the lisp approach to OO.
> As a Smalltalker I used to think like that as-well, then it started to > seem more like a glass-half-full thing. Cardelli's term 'untyped' > clearly expresses one of the things that Smalltalkers like - no > restrictions on the values that a variable or parameter can hold. > Cardelli's term 'safe' clearly expresses one of the other things that > Smalltalkers like.
But that's the thing. It's not very clear, if you think types are something objects rather than variables have (except in the variable-metaobject manner I detailed in another post).
> Would you really want Lisp to be 'typed' in Cardelli's meaning?
No, probably not. But I'm just not about to concede the unqualified word "type", seeing as it's the backbone of the lisp language specification, which expresses everything in terms of the types objects belong to. I'm perfectly happy to say "cardelli's term untyped". But temporarily ceding the word "type" when others might have an agenda to permanently co-opt it is dangerous...
> This notion of subtyping *can* be supported within a type-safe static > type system as C++'s templates and SML's functors prove. However, it > does require type annotations, and neither C++ nor SML were originally > designed with direct support for this notion of subtyping in mind.
Mh, I'm not sure in what sense templates support subtyping (or typing at all, for that matter). ;-)
But note that subtyping does not strictly require type annotations. Type inference with subtyping *is* possible, but it is too expensive to be considered worthwhile.
A slighlty different approach to achieve something "subtyping-like" which is more appropriate for type inference, is row polymorphism, as employed in O'Caml. Actually, I'm pretty certain that the combination of universal and existential row polymorphism would be much more powerful than subtyping anyway.
"Computer games don't affect kids; I mean if Pac Man affected us as kids, we would all be running around in darkened rooms, munching magic pills, and listening to repetitive electronic music." - Kristian Wilson, Nintendo Inc.
In article <nXp*z-...@news.chiark.greenend.org.uk>, Mark Carroll <ma...@chiark.greenend.org.uk> wrote:
> Of course it will:
> > let x = 4 :: Int > > let y = 5 :: Double > > x + y
> <interactive>:1: > Couldn't match `Int' against `Double' > Expected type: Int > Inferred type: Double > In the second argument of `(+)', namely `y' > In the definition of `it': it = x + y
This is yet another example of an overly restrictive compiler by default.
> Your mistake is in thinking that 5 can only be parsed as an integer.
No, I'm fully aware that the literal 5 is of type number, the specific subtype of which the compiler is free to use according to its type inferencing rules. But that doesn't change the fact that the literal 5 *is* an integer, just as the literal 5.0 *is* a floating point number, and the literal 25/5 *is* a ratio. Just because a compiler, be it Haskell or lisp or whatever, can read these as it sees fit, doesn't change what these things *are*. So Haskell allows a literal integer to be added to a literal float. That it does so by reading the literal integer as a float is useful and correct behviour in my opinion. The fact that it will not let you do:
Prelude> let x = 5 :: Int Prelude> let y = 9 :: Double Prelude> x + y
by inferring that I wish to now read the 5 :: Int as a Double so that it can be added to a Double, is an inconsistency, and yet another example of how the default is to strictness, with flexibility optional. I prefer a flexible default, to which I add strictness later on.
In article <bpfri3$a2...@news.oberberg.net>, Joachim Durchholz
<joachim.durchh...@web.de> wrote: > Erann Gat wrote: > > I've been singing the praises of Spin ever since it was used to catch race > > condition bugs in code that I had written. That really knocked my socks > > off.
"Formal Analysis of the Remote Agent Before and After Flight"
Klaus Havelund, Mike Lowry, SeungJoon Park, Charles Pecheur, John Penix , Willem Visser, Jon L. White
Abstract: This paper describes two separate efforts that used the SPIN model checker to verify deep space autonomy flight software. The first effort occurred at the beginning of a spiral development process and found five concurrency errors early in the design cycle that the developers acknowledge would not have been found through testing. This effort required a substantial manual modeling effort involving both abstraction and translation from the prototype LISP code to the PROMELA language used by SPIN.
Interestingly, no one brought up the well known (or at least it ought to be well known) story of the race condition in the Remote Agent as a counterexample to my claim that an undue emphasis on static typing led to neglecting what ultimately turned out to be the real problem in the billing system, so I'll do it myself.
The Remote Agent was written in Lisp, and it also suffered a potentially catastrophic race-induced problem (a deadlock this time) in flight. However, there was one significant difference: the race condition existed not because we didn't try to find them. We did, and in fact the methods that we used to find them were successful. However, we only applied them to part of the codebase, and the bug turned up in a part of the code that we hadn't analyzed.
If we had analyzed the entire code base it would have saved us a lot of grief. The reason we didn't is because 1) using SPIN is very labor intensive and we had a lot of code, so we didn't have time to do it all. To use SPIN you have to essentially re-code your algorithm into SPIN's own language (PREOMLA). And 2) We thought that if we built a small core that was analyzed by SPIN, then we could get away with not analyzing the rest of the code as long as it was built on top of this proven-safe core. The RA bug showed that this doesn't work in general. (BTW, this is also a counterexample to the theory that you can't learn anything from a single data point.)
The pernicious nature of multithreading bugs is well enough documented that IMO no mission-criticial multithreaded code should ever be used without running it through SPIN or something equivalent. Of course, it would be nice if there were a programmign language that had SPIN's analysis built-in to it so that it weren't so expensive to do. There's no reason why this can't be done, it just hasn't been. And yes, I think this can be attributed at least in part to the undue emphasis placed on static typing.
> traditional OO languages work well. I also heard it makes type inference > more difficult, but I don't know what's the problem here (could anybody > clarify?).
Well, consider the value restriction in Standard ML. If I write:
val r = ref []
Then r would have the type 'a list ref. So, I should be able to do something like:
r := 2 :: !r
and also something like:
r := "three" :: !r
But this is obviously unsound. You can think of the ref cell as acting as a channel of communication between different parts of a program that aren't type checked against each other.
The solution in SML'97 is that when you write
val v = some expression
the free type variables in can only be generalized (that is, bound by forall) if "some expression" is guaranteed syntactically not to cause a problem. Specifically, you can have constants, variables, lambda abstraction, and constructors (other than ref) applied to the those same things. Values can be polymorphic, but expressions that might do some computation can't, so you can't have function application there.
SML'90 had a different restriction, and I don't know what Ocaml does. I'm sure Andreas or Matthias could explain this better.
Andreas Rossberg wrote: > Mh, I'm not sure in what sense templates support subtyping (or typing at > all, for that matter). ;-)
They do in the sense I just described, only the type is inferred from the template code (which is itself annotated). Admittedly this is not the way C++ or SML programmers think about it, but I've become convinced this is a potentially fruitful point-of-view.
> But note that subtyping does not strictly require type annotations. [...]
I've also become convinced that subtyping in the sense I describe does, although I'm not yet prepared to defend this statement. Let me finish Pierce's book first...
In article <4bpr81-vm....@ID-7776.user.dfncis.de>, Dirk Thierbach <dthierb...@gmx.de> wrote:
> don't > expect everybody else to agree with you that this silly, mostly > useless and dangerous feature must be present by default.
Again, you see it as useless, I, and everyone who uses lisp, where this behaviour is the default, see it as useful and reasonable.
You see it as dangerous, but people have been using this feature of lisp for years and years, and yet we don't hear complaints about the "subtle conversion bugs" that we've laid ourselves open to.
Maybe the reality is that type errors are actually much easier to spot than advocates of static typing like to admit. So detecting them at runtime isn't a disaster, especially as there are certain kinds of type errors, which happen to be particularly prevalent in interactive programs, that can *only* be detected dynamically anyway.
In article <gNOSPAMat-1911030935140...@k-137-79-50-101.jpl.nasa.gov>,
Erann Gat <gNOSPA...@jpl.nasa.gov> wrote: >In article <bpfpqg$7l...@news.oberberg.net>, Joachim Durchholz ><joachim.durchh...@web.de> wrote:
>> Well, I didn't read a single post that shared that manager's opinion. >> Which says a lot about the assumptions of managers on the benefits of >> static typing, and very little about the actual benefits of static typing.
>I think it says more about the mindset of people who hang out on >comp.lang.lisp and comp.lang.functional. But this particular manager was >not your typical PHB either. He came from academia, where he was a well >known and respected researcher. He was, in fact, a very smart person.
Even Very Smart People can have mental blocks, get stuck in a paradigm, etc. It took a long time for Einstein to be convinced of the validity of Quantum Mechanics, and if String Theory turns out to be correct many modern, well-respected physicists will have to recant.
There's also an issue that a mind-set that works in academia may not translate well to industry, because the constraints are different. The need to get product out the door often requires cutting corners, so you can't apply all the cool ideas you developed in the research lab.
-- Barry Margolin, barry.margo...@level3.com Level(3), Woburn, MA *** DON'T SEND TECHNICAL QUESTIONS DIRECTLY TO ME, post them to newsgroups. Please DON'T copy followups to me -- I'll assume it wasn't posted to the group.
> >>That's the point, actually. Early on in the process you really don't > >>know what your design is going to end up like. You'd *like* to know > >>what you're dealing with, but you're not sure if you have all the > >>bases covered, or if maybe you need to segregate things or coerce > >>them, etc. At this point, writing it down is premature. Later on, it > >>may be that you do want a uniform list.
> >>Then again, maybe not.
And so we write the code to deal with what we know to be required now (or assume to be required in the immediate future). And when things change, we change the code. So the interesting question is what are the relative costs of changing the code. In a modern statically typed language, how do you reduce the ripple from changing data-type definitions?
In my Smalltalk code that would include going through and changing the method parameter names from anInteger to anIntegerOrString ;-)
> > I'd really like to see an example of a design process where you cannot > > decide if your data is used uniformly or not. I already asked Raffael, > > but didn't get one.
> > If these things happen so often, it surely must be possible to find > > an example.
> XP?
> (This might not be the answer you expected, but extreme programming is > built around the notion that you can change your mind about the design > of a program very late in the game.)
No! No! No! Not that! I've more or less given up on comp.object since they became comp.xp ;-)
> But that's the thing. It's not very clear, if you think types are something > objects rather than variables have (except in the variable-metaobject > manner I detailed in another post).
In some typed languages (like Haskell), values and expression have types. There are no variables. When I write
let x = big_hairy_expression in (f x, g x)
x is not a variable with some type, nor is it a variable-that-holds-a-value-of-some-type. It's just a name that I use in place of big_hairy_expression so that I don't have to type it twice. B_h_e has a type, so x has the same type because it's just a stand-in for b_h_e.
Joachim Durchholz <joachim.durchh...@web.de> wrote in message <news:bpfum4$dq0$2@news.oberberg.net>... > Oleg Trott wrote: > > Speaking of C, in his "Type Systems" paper, L. Cardelli mentions that even > > C, with its weak typing, is more maintainable than Lisp (first paragraph of > > page 6).
> I'd be rather surprised to hear something like that. > What definition of "maintainability" does he use?
He doesn't provide a definition.
> Oleg Trott wrote: > I wouldn't be surprised if he was correct though. > Is there objective empirical evidence of this?
Cardelli doesn't provide support for that blanket assertion in the paper - so maybe, for him, it was self evident ;-)
Empirical evidence that Ada is more maintainable that C++ does seem to exist.
In article <bpfrgc$a2...@news.oberberg.net>, Joachim Durchholz <joachim.durchh...@web.de> wrote:
> Raffael Cavallaro wrote: > > I do it all the time. Fortunately, what you and the B&D fetish > > designers of Haskell think "one *does not* want" are not binding on > > me (pun intended), as I can use lisp.
this bit here ^^^^^^^ signals that I was writing a joke. Sorry if that wan't clear.
In article <bpfs9r$b6...@news.oberberg.net>, Joachim Durchholz
<joachim.durchh...@web.de> wrote: > Erann Gat wrote: > > Right, and as soon as the first one was caught the system should have > > stopped and waited for human intervention instead of proceeding.
> > This would not have eliminated the problem, of course, but it would > > have mitigated the damage and made recovery easier. IMO that is in > > general the best you can hope for.
> You simply can't stop a payment system unless you wish to go out of > business.
Do you bother to think at all before you post, or do you just blindly contradict everything I say? Of course you can, and in fact we did, and the company is still in business now many years later (and doing quite well actually). Figuring out how we managed that is left as an excercise.
Joachim Durchholz <joachim.durchh...@web.de> wrote in message <news:bpg0bd$h1a$1@news.oberberg.net>... > Isaac Gouy wrote: > > Why do you say Strongtalk failed? The technology was working nicely > > when the company was acquired by Sun and the technology adapted to > > Java (HotSpot VM).
> It worked, but it wasn't accepted by the Smalltalk community. > Actually, mention of Strongtalk in a Smalltalk group is more likely to > start a flame war than anything else.
On the contrary!
The flame war would be about the extent to which Strongtalks technology (polymorphic inline caches) have already been incorporated into current Smalltalk implementations.
You may also find this seminar interesting: "Bytecode-to-bytecode adaptive optimization for Smalltalk Compilation and execution architecture for late-bound object-oriented programming languages"
Isaac Gouy wrote: > Joachim Durchholz <joachim.durchh...@web.de>:
>>Isaac Gouy wrote: >>>Why do you say Strongtalk failed? The technology was working nicely >>>when the company was acquired by Sun and the technology adapted to >>>Java (HotSpot VM).
>>It worked, but it wasn't accepted by the Smalltalk community. >>Actually, mention of Strongtalk in a Smalltalk group is more likely to >>start a flame war than anything else.
> On the contrary!
> The flame war would be about the extent to which Strongtalks > technology (polymorphic inline caches) have already been incorporated > into current Smalltalk implementations.
OK. You need to mention "static typing" as well to get the flame war. (Which was what Strongtalk was about IIRC.)
Erann Gat wrote: > Joachim Durchholz <joachim.durchh...@web.de> wrote:
>> Conflation [of object destruction and resource deallocation] is a >> good idea as long as you can guarantee that the destructor has no >> side effects /on the program/.
> That's true, but this can never be the case in general because the > destructor *always* has, by definition, the side-effect of destroying > the object. This is safe only if the object is not shared among > threads, which is a very severe (and generally unenforcable) > constraint.
Threading doesn't make much of a difference, destroying an object and calling the destructor is dangerous whether the aliases live within the same thread or elsewhere. (Note that aliasing happens if there is sharing and vice versa. The two are describing the same thing.)
BTW Erlang philosophy states that objects should /never/ be shared across threads, and Erlang enforces this simply by offering no way to do that, so it's easy enough to enfoce. From what I hear from the Erlang camp, the constraint isn't severe as well; in fact, they consider this as a key property of systems that remain stable in the presence of software errors.
Jesse Tov wrote: > Joachim Durchholz <joachim.durchh...@web.de>:
>>traditional OO languages work well. I also heard it makes type inference >>more difficult, but I don't know what's the problem here (could anybody >>clarify?).
> Well, consider the value restriction in Standard ML. If I write:
> val r = ref []
> Then r would have the type 'a list ref. So, I should be able to do > something like:
> r := 2 :: !r
> and also something like:
> r := "three" :: !r
> But this is obviously unsound.
Ah, I see. Essentially it's the same as the process that destroys subtyping relationships: Reading from a name restricts the allowable types for that name to the type that the reader expects, plus all types that are subtypes. Writing to a name has the reverse effect, allowable types are restricted upwards towards supertypes. I'm not sure whether HM typing can handle this kind of constraint at all.
Reading and writing the same name usually results in having both an upwards /and/ a downwards restriction, making the name monomorphic. In an OO context, this means that the name cannot be redefined at all in a subclass without making the subclass a non-subtype. In an FPL context, I suspect that HM typing will either be forced into making the name monomorphic (not desirable since that directly inhibits many forms of reuse), or fail because there is no valid types-to-names association. (Hope I understood this correctly. Corrections welcome.)
Joachim Durchholz <joachim.durchh...@web.de> writes: > You are also unsafe if the destructor has side effects such as > modifying global data, since it's an effect that isn't represented > syntactically (which means that the effect will most likely be > overlooked during code review - a nasty source of potential errors).
Sometimes using a destructor in the RAII idiom is the only way to achieve safety without code duplication, whether or not it has effect on "global" data or any data with wider scope. Consider that destruction may be triggered by exception-induced stack unwinding. RAII makes "do/undo" actions both safe and concise.
Consider this simple example, from production code:
An "overlay" is temporary, scoped assignment to some other mutable variable that will be undone upon the overlay's destruction, similar to Perl's "local" declaration or layered assignment to special variables in CL.
Assuming that the value_type's assignment operator can't throw an exception, using an overlay is safe and is safer than writing four separate, explicit assignment statements (one to store the original value, one to change the value, another to restore the original value under normal exit, and yet another to restore the value in case of a caught exception, presumably before rethrowing).
The cleanup is not represented syntactically, but the symmetric promise is: I may change this variable here, and I will change it back before going away.
{ int i = 0;
try { const overlay<int> o( i, 1 ); // We're not leaving until i is 0 again.
In article <bpfthh$29...@la.iki.fi>, Lauri Alanko <l...@iki.fi> wrote: > g...@jpl.nasa.gov (Erann Gat) virkkoi: > > It was also an experiment of sorts: if static typing does indeed have the > > value that people claim I would have expected the advocates of static > > typing to have countered with at least one anecdote where a catastrophic > > failure caused by a run-time type error.
> The Mars Climate Orbiter. Mixing between units is just the kind of silly > error that a good type system can eliminate reliably: just write a > simple module:
> module Force(Force, newton, lbf, ....) where > newtype Force = MkForce { forceNewtons :: Double } > newton n = MkForce { forceNewtons = n } > lbf n = MkForce { forceNewtons = n * 4.4482216 } > ....
> After this you cannot create force values without explicitly > mentioning the unit. Attempt to use a plain Double, and you'll get a > type error. And for good reason.
A good example, but not really on point IMO. The problem there was that units weren't modelled in the code at all. That was a (common) design decision. Static typing won't help if you don't tell the system that this floating point number is in newtons and that one is in pounds.
In fact, I often cite MCO as an example for my basic claim, which is that languages mold people's thinking in often undesirable ways. People write "1.2" and "3.4" when what they really mean is "1.2 newtons" and "3.4 pounds" because the languages that they use understand "1.2" and "3.4" and they don't understand "1.2 newtons" and "3.4 pounds". If MCO code had been written in a language that directly supported physical quantities (as opposed to simply floating point numbers) or one where such a facility could have been added things might have gone differently.
There are many, many examples of this sort of thing. People think that "ints" are integers, "floats" and "doubles" are reals, or that char*'s are strings when none of these things are really true. They do this because the languages understand "int" and "float" and "double" and "char*" but don't know about integers and reals and strings. I suspect most of the people reading this know this, but a lot of programmers don't, or at least don't think about it when they code.