Prev: scheme problem
Next: lisp student job offer
From: Norbert_Paul on 16 May 2010 08:37 Tamas K Papp wrote: > On Sat, 15 May 2010 23:22:07 +0200, Norbert_Paul wrote: > >> Well I aint living on the street yet, and my turn from Java to Lisp >> looks quite dangerous when watching mainstream. So Lisp might equally >> drag me on the street instead of protecting from living there. > > You mean that learning something new somehow jeopardizes your job? > You must be working for the government :-) I am working at a university and, hence, in fact, employed by the government. But so far learning something new was appreciated there. Lisp is not that new for for me. I am only out of practice: It was Java which was new for me (and was fun learning, too). But only recently I could free myself from the obligation to use Java.
From: Scott L. Burson on 23 May 2010 14:41 As I am working in Java now for my day job, I am working on code that has been written using the Java collections. And I noticed the other day a place where an object A, which has a collection in one of its slots, was returning that collection through a getter, and a second object B was calling that getter and then modifying the returned collection. E.g.: class A { List<Something> children; List<Something> getChildren() { return children; } } class B { void mumble(A a) { aKids = a.getChildren(); // ... aKids.set(someIdx, someNewKid); // ... } } What's wrong with this, of course, is that it breaks class A's abstraction boundary. Such behavior is a likely source of bugs. Now, in the actual code I encountered, `mumble' did eventually do `a.setChildren(aKids);', giving A a chance to establish any needed invariants -- and since A did indeed have such an invariant (the children needed a back-pointer to their parent) this was, in fact, critical to the correct operation of the code. But one can easily imagine omitting the `setChildren' call, particularly if, at one point in the development of the code, it wasn't critical because it did not actually have any invariants to establish. Then later, one can also easily imagine, one discovers that such an invariant is needed, but it's hard to track down all the places in the code that need to be changed, because one has gotten in the habit of passing these collections around and modifying them outside of the objects that own them. This, I think, is one of the strongest arguments for functional collections: they prevent an entire class of bugs, by making it impossible for a client of an interface to directly modify a collection owned by the implementation of that interface. The client always has to go through the interface to modify the collection, and so the abstraction boundary is not broken. Java does provide `Collections.unmodifiableList' etc. for the same purpose, but no one seems to be in the habit of using these calls. (There is a problem with them: the enforcement is dynamic, i.e. at run time, rather than static; they would be more useful if they returned an instance of an immutable interface, so the compiler could tell you . But the JCF has no immutable interfaces.) Like most things in programming, it depends on what you're doing. If your primary focus is on algorithms, functional collections may not seem to have any clear advantages. But really, relatively few of us spend most of our time on the relatively small but CPU-intensive pieces of code that go by the name of algorithm. Most of us, I think, spend most of our time on much larger pieces of code, one of our primary tasks being the management of complexity. And when it comes to managing complexity, I argue that functional collections have clear advantages that more than offset their minor performance cost. -- Scott
From: Norbert_Paul on 24 May 2010 06:12 Scott L. Burson wrote: > As I am working in Java now for my day job, I am working on code that > has been written using the Java collections. And I noticed the other > day a place where an object A, which has a collection in one of its > slots, was returning that collection through a getter, and a second > object B was calling that getter and then modifying the returned > collection. E.g.: > > class A { > List<Something> children; > List<Something> getChildren() { return children; } > } > > class B { > void mumble(A a) { > aKids = a.getChildren(); > // ... > aKids.set(someIdx, someNewKid); > // ... > } > } > > What's wrong with this, of course, is that it breaks class A's > abstraction boundary. Such behavior is a likely source of bugs. Why does it so? The intented abstraction should be documented, and I can imagine very well, that exposing the List-object could be intented. children may very well be children = new List<Something>(){ // override List-modifications here thus ensuring invariants. }; > Now, in the actual code I encountered, `mumble' did eventually do > `a.setChildren(aKids);', giving A a chance to establish any needed > invariants -- and since A did indeed have such an invariant (the > children needed a back-pointer to their parent) this was, in fact, > critical to the correct operation of the code. > > But one can easily imagine omitting the `setChildren' call, > particularly if, at one point in the development of the code, it > wasn't critical because it did not actually have any invariants to > establish. Then later, one can also easily imagine, one discovers > that such an invariant is needed, but it's hard to track down all the > places in the code that need to be changed, because one has gotten in > the habit of passing these collections around and modifying them > outside of the objects that own them. children = new List<Something>(){ // override List-modifications here thus ensuring invariants. }; > This, I think, is one of the strongest arguments for functional > collections: they prevent an entire class of bugs, by making it > impossible for a client of an interface to directly modify ... How do you prevent these bugs in Lisp? How would you then write get-children? The Lispy way is, as far as I know, maintaining a list of children and simply returning it. But then you have elt to introduce bugs. AFAIK in MOP you have access to the class precedence list and could even destructively modify it, which, of course, is forbidden by the spec but need not be checked, surely due to effiecency reasons. Should a language prevent bugs by removing features? > Java does provide `Collections.unmodifiableList' etc. for the same > purpose, but no one seems to be in the habit of using these calls. Bad programmer's habits are not an issue of a language. > (There is a problem with them: the enforcement is dynamic, i.e. at run > time, rather than static; they would be more useful if they returned > an instance of an immutable interface, so the compiler could tell > you . But the JCF has no immutable interfaces.) This is true, but dynamic enforcement is OK and better than nothing. Maye you could @deprecate the modifiers (in List<ST>{ /*... here */}). I'm not sure if this is allowed but then you would at least get compiler warnings. > Like most things in programming, it depends on what you're doing. If > your primary focus is on algorithms, functional collections may not > seem to have any clear advantages. But really, relatively few of us > spend most of our time on the relatively small but CPU-intensive > pieces of code that go by the name of algorithm. Most of us, I think, > spend most of our time on much larger pieces of code, one of our > primary tasks being the management of complexity. And when it comes > to managing complexity, I argue that functional collections have clear > advantages that more than offset their minor performance cost. Yes, but my particular interest with which I started this thread was what goes by the name of algorithm. > -- Scott -- Norbert
From: Norbert_Paul on 24 May 2010 06:28 Pascal Costanza wrote: > But not so in Java, because the full-blown for statement only allows for > introducing local variables of exactly one type. If you want to add > additional local variables of some other type, you have to do this > outside of the for statement: Just two hints: { // make count local. > int count = 0; > for (Iterator it1 = l1.iterator(), it2 = l2.iterator(); > it1.hasNext() && it2.hasNext(); > count++) { // always but bodies in braces > System.out.println(count + " " + it1.next() + " " + it2.next()); }} > This is a pretty crappy design for a language... It only is a minor issue for me even though I often use the above achema. The biggest problem I have with Java is that you alwas have to deal with types and type declarations at a premature point where you still want to be free to experiment. It is not flexible enough for me. Generics make it even worse. Have an idea and sketch it down in Java. Then your code sketch gets polluted by all these verbose type declarations tainting your initial concept. If you have to change something you must refactor all according declarations. Then, additionally, you have that ugly mix of primitive types, array types and classes and these funny wrappers from primitive to class. At least you don't have to always (explicitly) cast between primitive and wrapper any more. > Pascal Norbert
From: Pascal Costanza on 24 May 2010 07:48
On 24/05/2010 12:28, Norbert_Paul wrote: > Pascal Costanza wrote: >> But not so in Java, because the full-blown for statement only allows for >> introducing local variables of exactly one type. If you want to add >> additional local variables of some other type, you have to do this >> outside of the for statement: > Just two hints: > > { // make count local. That doesn't buy us a lot. >> int count = 0; >> for (Iterator it1 = l1.iterator(), it2 = l2.iterator(); >> it1.hasNext() && it2.hasNext(); >> count++) > { // always but bodies in braces No, that's silly. >> System.out.println(count + " " + it1.next() + " " + it2.next()); > }} > >> This is a pretty crappy design for a language... > It only is a minor issue for me even though I often use the above > achema. The biggest problem I have with Java is that you alwas have > to deal with types and type declarations at a premature point where > you still want to be free to experiment. > It is not flexible enough for me. Generics make it even worse. Have > an idea and sketch it down in Java. Then your code sketch gets > polluted by all these verbose type declarations tainting your > initial concept. If you have to change something you must refactor > all according declarations. Then, additionally, you have that ugly > mix of primitive types, array types and classes and these > funny wrappers from primitive to class. At least you don't have to > always (explicitly) cast between primitive and wrapper any more. I agree to all of this. Pascal -- My website: http://p-cos.net Common Lisp Document Repository: http://cdr.eurolisp.org Closer to MOP & ContextL: http://common-lisp.net/project/closer/ |