Subject: Re: MD5 in LISP and abstraction inversions From: Erik Naggum <erik@naggum.net> Date: Sat, 17 Nov 2001 04:14:00 GMT Newsgroups: comp.lang.lisp Message-ID: <3214959235586413@naggum.net> * Bruce Hoult | I see that CL doesn't define < for complex numbers. Dylan does but says | the results are implementation-dependent. Why? Are there any other parts of mathematics that are implementation- dependent? One of C's really bad implementation-dependent features is the return value of some form of integer division by negative numbers. It is so obscure that most C programmers are completely unaware of it, but it bites hard when it bites. The same applies to the sign of char, which is extremely annoying when it suddenly changes on you just because you port to a new machine. I think a language specification should try to limit the freedom of implementations in these areas, not increase it just because of some rinkytink hardware that gets it wrong. | < should be a total ordering. If you can't make it a total ordering for | your type then don't define it at all. It is not "your type" I am concerned about, but the interaction of all types in the system, the complexity of which increases very quickly as you add types. If (< a b) is well-defined, and (< b c) is well-defined, is (< a b c) and (< a c) also well-defined? They may not be. The key to the genericity of < in CL is that it works on _all_ (real) numbers, mixed and matched however you want. I expect this to apply to user-defined number types, too, but that is precisely where there be dragons. | It's the programmer's responsibility to ensure that it holds. The | compiler can't check it, but it is allowed to assume that it holds. This is simply a cheap cop-out. If a language gives programmers tools that affect fundamental invariants, it is flat out irresponsible if it does not give the programmers tools to ascertain that those invariants are not violated. | There are plenty of parallel situations in any programming language. True, but that is not a good reason to create more of them. | You want to define < on lists to mean "shorter than"? Well, OK. At | least you'll agree it's a total ordering. I think I may have to find some neon-sign-like fonts and have the word "EXAMPLE" made out of it and then make it flash. Please get the point of the example, and drop the specifics of the example. They work just like analogies: At some point or another, they become irrelevant to the point being communicated. In this case, I wanted to point out that there are convenience reasons to make certain things _equally_ hard. Adding a new method on < seems to be easy, but it is in fact extremely hard, because the programmer needs to keep the fundamental invariants of all the types involved intact, and that requires a lot more computer science than you get from example code in books on OO. In my view, real programmers will not _want_ to add a method on < for a user-defined types because it causes many more problems than it solves, and is actually much harder than writing type-specialized functions. | Dylan side-steps the issue of generic function dispatch on multiple | arguments, because < takes only two arguments in the first place. If | you want the CL operation then you need to use reduce(). Huh? As far as I can see, the return value of the function will be used as one of its arguments for the next element in the sequence for reduce to work. (reduce #'< '(1 2 3)) would cause cause reduce to make the call equivalent to (< t 3). This is true for both both Common Lisp and Dylan, so I wonder what you had in mind. | Efficiently checking that a set of lists are ordering from shortest to | longest strikes me as such a specialized thing that you really can't | avoid a specially written function to do that. You make assumptions at break-neck speed and crash into things. Please be more careful. The intention of bringing up this *EXAMPLE* was to show that multiple types of arguments in the same function call would pose problems. Remember that Common Lisp does not use a binary <, only. With just a little effort, you should have seen (length< 2 foo 4) as an application of this function, where foo is some sequence and 2 and 4 are the regular integers. And it is obviously not just < that goes into this set of length-comparing functions. (length= foo bar zot) is a lot more useful than you might think if you stare yourself blind at length<. | You appear to be thinking that the best way to do it is to iterate | through all the lists in parallel, and return false if the first list to | be exhausted is not the first one (and then drop the first list and | repeat...). That would be true if you're expecting a list somewhere in | the middle or towards the end to be dramatically shorter than it "should" | be, but it's not the best if it turns out that there's a problem with an | early list. This is completely beside the point, but a "problem"? What "problem"? > I think the notion of adding methods to a generic function for a purely > mathematical function like < stems from a lack of reasonable builtin > support for the necessary numeric types. I mean, how often _should_ you > need to add a new kind of number class to a system? | That depends on what type of work you're doing. No, that is backwards. Since we are talking about language design, it depends on what the language specification should require of the implementation, hence the "should". The language should not impose on the _application_ to make these kinds of type additions _unnecessarily_. It should also not impose on every application that does not need this infrastructure the cost of having it available. In a dynamically typed language, there are significant costs associated with the mere existence of user-defined methods on primitive mathematical function. Moreover, the language does in fact provide you with the means to get a more elaborate dynamism, but it would be _much_ harder to get the efficient implementation of a non- generic < operator, especially if the problem is that all the callers of this function would call the generic version. > Rather than optimize for this extremely unlikely need, optmizing for the > most likely need, i.e., using the existing number classes, seems like > good engineering. | There is no incompatability between the two. So generic dispatch is cost-free? Sorry, this flies in the face of facts. /// -- Norway is now run by a priest from the fundamentalist Christian People's Party, the fifth largest party representing one eighth of the electorate. -- Carrying a Swiss Army pocket knife in Oslo, Norway, is a criminal offense.