From: Norbert_Paul on
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
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
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
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
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/
First  |  Prev  |  Next  |  Last
Pages: 3 4 5 6 7 8 9 10 11 12 13 14
Prev: scheme problem
Next: lisp student job offer