![](/i/fill.gif) |
![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
Darren New <dne### [at] san rr com> wrote:
> > That's the whole idea in modular thinking: You don't know what's inside
> > the module, and you *don't care*. You don't need to care.
> It also prevents code from *inside* the module from doing certain things in
> an easy way.
Everything in the class is public to the class itself. I don't understand
your point.
> >> If it's half-enforced, as in the compiler complains and won't compile the
> >> code, but there's ways to get around it anyway (on purpose or by mistake),
> >> then it's IMO the worst of all possible worlds. You'll spend hours or days
> >> trying to debug code that's already right because the client is convinced
> >> the other code they've written is bugfree and it's easier to blame you than
> >> to find the wild pointer in their own code. The whole idea of class
> >> invariants goes out the window.
> >
> > No you'll explain to me how a naming convention of public variables helps
> > this problem.
> The naming convention doesn't help that. The safety of the language helps
> that. In other words, what helps it is not the naming convention, but the
> fact that code that accesses private variables *must* use the naming
> convention to do so.
So your solution to the problem of being able to modify private members
by accident because of the language being unsafe is to make the language
safe... and to make everything public (with a naming convention of
variables).
I don't see how the latter follows.
> I'm just less worried about intentionally violating modularity than
> accidentally doing so.
Problems with programming errors in unsafe languages has nothing to
do with the subject of modularity.
Accessing something out of boundaries by accident is always a bug
regardless of what effects that has. This has nothing to do with whether
modularity is a good thing or not.
> (I've had server code written in unsafe languages where it would dump core
> on a segfault, and the providers wanted to blame it on the fact that the Xen
> kernel was on the machine. We weren't booting Xen, but they wouldn't even
> look at their code until we uninstalled that kernel. Just as an example of
> how people can avoid admitting their own bugs might be part of the problem.
And this has what to do with the original subject?
You always succeed in returning to your favorite subject: Bashing
"unsafe" languages. And in your world there's only one such language: C++.
--
- Warp
Post a reply to this message
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
Warp wrote:
> Darren New <dne### [at] san rr com> wrote:
>> 1) They're enforced 100%. Nobody outside the class is capable of writing to
>> or reading from a private variable. This provides maximum isolation at the
>> expense of not having available at runtime lots of stuff the compiler knows
>> at compile time that you might want to know (e.g., reflection type stuff).
>
> If you need to know the private members of the class from the outside,
> then that class has been badly designed.
>
> With a well-designed class you don't *need* to know, nor care.
Not all uses of reflection come from outside the class, and not all uses of
reflection violate modularity. For example, the ability to instantiate an
instance of a class whose name you have in a string is technically
"reflection". That doesn't break modularity.
>> 2) They're enforced 100%, but people outside the class can read but not
>> write them. This is a little more fragile to change
>
> It breaks modularity badly. The whole idea of data hiding is that the
> private part should be completely opaque to the outside. The second it
> isn't, outside code will start making assumptions, and you can't change
> the implementation of the class anymore.
Right. That's what I meant by "it's more fragile to change."
> Some people argue that accessors to all private members, especially if
> they are automatically generated, is almost as bad as having all the
> members public.
It seems semantically identical to me. :-) Of course, if you can override
the private accessors, you might be able to keep some sorts of compatibility.
> It exposes the internal implementation, and thus outside
> code will start making assumptions about it, making it harder to change
> the implementation later.
>
> Some accessors may be justifiable, but you should not go overboard.
Agreed. I'm not advocating that people access private members without
knowing what they're doing and why. I'm not advocating they access private
members when they can change the module to reveal what they need revealed.
>> 3) They're enforced, but there are explicit mechanisms to bypass them, such
>> as reflection. This is my personal preference, because it's easy to find the
>> places in the code that might be breaking your invariants or might rely on
>> your internal implementation, while providing the power of metadata. This is
>> the C# and Java model.
>
> If you don't have access to the code which is making assumptions about
> your class' internal structure, you can't change your class without breaking
> that existing code.
It depends what assumptions are being made by the code you don't have. It's
also the case that it's not uncommon for things like reflection to be used
internally to the class or the module to replace what would otherwise be
fragile boilerplate code.
If the code that violates modularity adjusts automatically when you change
the class being violated, it's in practice capable of handling far more
kinds of changes than one might expect.
> Sure, this situation might not be extremely common, but it can happen.
> And it's something which might have been avoided with a good interface
> design and a completely opaque private implementation.
Agreed. The problem comes when you're stuck with a bad interface design and
an opaque implementation. :-) Surely, you don't imagine that reading the
source code of a class implementation and then using internal functions to
get the values of private variables is a normal way of programming LISP objects?
>> 4) They're enforced by the compiler but not by the runtime. This removes
>> *both* the ability to debug your module in isolation *and* the ability to do
>> metaprogramming that eliminates boilerplate by using the information the
>> compiler has already calculated.
>
> Why would you want runtime checks if access rights could be fully checked
> at compile time?
I wouldn't. I don't know any languages that fully check access rights at
compile time that don't provide well-defined standard mechanisms for
bypassing them at need.
>> 5) They're not enforced by the compiler or the runtime, but there are
>> conventions and/or standards that make it obvious to everyone when you're
>> breaking the data encapsulation, and the runtime ensures that you can only
>> do this "on purpose". That is, the unenforced conventions (or enforced but
>> bypassable mechanisms) ensure that the only way of breaking encapsulation is
>> on purpose. This is almost as good as #3, except it may be harder to track
>> down who is violating your invariants.
>
> Again, if you must keep "reverse compatibility", for a lack of a better
> term (in other words, you as a library developer must maintain compatibility
> with existing code which uses your library, and you don't have access to
> this existing code nor can change it), it can be a problem if the existing
> code can make assumptions about your library and does so.
Agreed. But I don't know of any language that doesn't let you get into that
situation. It's just a question of how easy or hard it is, and how obvious
it is that it's happening.
>>> I think that your problem is that you have some kind of holy war against
>>> C++, and every single discussion about programming is always somehow turned
>>> to bashing C++.
>
>> Nope. Someone brought up C++ by talking about the compiler enforcing
>> "private:". I was just pointing out that the compiler only enforces it in
>> some ways, and not in the ways that I thought was also important for modularity.
>
> You immediately assumed that it must be talking about C++, of course,
> and immediately jumped at the opportunity of bashing it.
Well, the first thing I objected to is the idea that a language is kludgey
if you can access private variables whose names only appear in the source
code of the class implementation.
My first mention of C++ (in the same paragraph where I meantioned another
half-dozen OO languages) was that it's trivial to bypass the modularity of
it, meaning that you can read and write private members of a class by using
well-defined if non-portable operations. I then asked you if you didn't
think that meant modularity was lacking in C++.
Actually, you mentioned "private:" first, and I admit I assumed you were
talking about C++, since the only other languages I know that use that
syntax are ones you'd already said are kludgey.
I think we're just off on the wrong foot here, perhaps. You said "data
hiding is an integral part of OO" and criticized CLOS for not having it. Yet
the only way to unhide the private data in CLOS is to read the source code
for the implementation of the class. Hence, it seems you were saying that
any mechanism that allows the access of private members, even with complete
access to the source, is a bad thing (assuming you think kludges are bad).
Yet I can get access to the private members of *every* language that has the
"private:" syntax. C++ is the only language I know that use that syntax that
doesn't have a standard way of accessing private variables. All C++ has is
non-portable (but standard) ways of accessing private variables. So I talked
about C++.
> In a discussion about the modular programming paradigm and data hiding you
> succeeded in creating an lengthy thread about how you can trash memory in C++.
> It's not exactly like you avoided the subject.
No, I didn't avoid it. There was an implication that C++ had better
modularity and data hiding than languages with reflection implemented
standardly. I'm pointing out that it doesn't. You can dump an instance into
an array of characters and rebuild whatever you need, if you accept you'll
have a non-portable knowledge of how the class is laid out.
>> OK. I'm just trying to communicate here. That's why I'm asking the question.
>> It seemed to me that you preferred a language with unsafe behavior and
>> compiler-enforced checks to one with safe behavior and no compiler-enforced
>> checks.
>
> That "you preferred a language with unsafe behavior" is 100% your own
> invention. I have nowhere said that.
Are you missing the "it seemed to me" bit there? Do you not understand what
that phrase means? It means "Hey, we seem to have a miscommunication going
on. This is what I received. Is it what you meant?" That doesn't mean I'm
"twisting" your words. It means I'm asking you to clarify.
> What I have said is that I prefer enforced modularity over non-enforced
> one (which IMO is not modularity at all). That "unsafe" bit is all your
> own twisting.
I prefer enforced modularity over non-enforced modularity myself. I know of
virtually no language that supports enforced modularity. They all have
specific trap doors just to get around the modularity restrictions. (Except
perhaps Ada and Eiffel, neither of which is a really a language I use enough
to be sure of.)
>> I've never heard you call C++ a kludge OO language. I assumed you were
>> excluding C++ from that criticism when you said a language that allows
>> access to private members is a kludge OO.
>
> At least you admit you are making assumptions.
Yeah. Sure. That's a basic part of communication. That's why my comments are
full of things like "it seems to me..." and "are you saying that..."
They're indicators that I'm making assumptions that need to be validated. It
doesn't mean I'm saying you said that. It means it sounds to me like you're
implying that, and I'm asking if I'm interpreting you correctly.
--
Darren New, San Diego CA, USA (PST)
My fortune cookie said, "You will soon be
unable to read this, even at arm's length."
Post a reply to this message
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
Warp wrote:
> Darren New <dne### [at] san rr com> wrote:
>> Actually, let me ask this. Since you seem to be saying that reflection means
>> that an OO system is a "kludge" because it breaks modularity
>
> In fact I said that trying to emulate OOP in a language which has no
> direct support for OOP is a kludge. The only thing I said about reflection
> is that IMO it breaks modularity and thus you lose many of the benefits of
> modular design.
Fair enough. I misremembered precisely what you said, and went with the
logical implication that seemed to follow.
I'll still disagree that emulating OO with closures in LISP is a kludge,
because other features of LISP provide direct support for changing the
language to support other features directly. Once you hit that meta-level of
support, it's a whole new ball of wax.
--
Darren New, San Diego CA, USA (PST)
My fortune cookie said, "You will soon be
unable to read this, even at arm's length."
Post a reply to this message
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
Warp wrote:
> Darren New <dne### [at] san rr com> wrote:
>> Actually, I thought it ended in C++ because that's the only unsafe OOP
>> language I know of.
>
> I can't understand if that means "I haven't studied unsafe OOP languages
> other than C++" or "to my knowledge there are no other unsafe OOP languages".
There are few other unsafe OOP languages I know enough about to discuss. Ada
and C++ are the only unsafe OOP languages I know, and I don't know Ada well
enough to know what bad code is erroneous and what bad code is simply UB in
Ada.
>> It's the only language I know where the compiler
>> attempts to enforce modularity but in practice fails to do so, by declaring
>> violation of modularity "undefined" or "compiler-specific" rather than
>> catching all cases of broken modularity at compile time or runtime.
>
> Then you don't know much, do you.
Possibly. What OO languages do you know of where the compiler attempts catch
all violations of modularity at compile time but in practice fails to do so?
>> I was trying to figure out how well-documented modularity breaking is a
>> "kludge" but poorly documented unsupported modularity breaking isn't.
>
> The connection between my usage of the word "kludge" and your usage
> of the words "unsafe" and "reflection" is purely your invention.
OK. I was confused by the fact that your post had
1) A paragraph about how CLOS allows access to the private variables,
2) a comment about languages lacking data hiding aren't really OO,
3) a lack of specific support for hiding means the OO is kludgey.
I leapt to the conclusion that in 2 and 3 you were actually talking about
the same thing as you were talking about in 1. That was a bad assumption,
and started me arguing that CLOS has a reasonable degree of modularity built
in.
If you weren't talking about CLOS in 2 and 3, I'm not really sure why you
quoted (1) there.
If you *were* talking about CLOS, I'm arguing that there isn't to my
knowledge any OO language that does a better job. And none that I know of
that use "private:".
--
Darren New, San Diego CA, USA (PST)
My fortune cookie said, "You will soon be
unable to read this, even at arm's length."
Post a reply to this message
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
Warp wrote:
> Darren New <dne### [at] san rr com> wrote:
>>>> I'm saying that C++ does not enforce that you don't change private instance
>>>> variables from outside the class.
>>> And this has exactly what to do with whether (compiler-enforced) data
>>> hiding is a good thing or not?
>
>> Because there's two reasons for data hiding: (1) allow more flexibility in
>> implementation, (2) allow easier debugging.
>
>> If you can violate my encapsulation, I don't see the data as especially
>> encapsulated, is all.
>
> You didn't answer my question.
You said the designs of C# and Java are badly broken because they let you
get to the private variables of an instance using reflection. You then said
C++ doesn't let you get to a private variable of an instance. I was
disputing that claim. That's what compiler-enforced data hiding has to do
with C++.
> "Is enforced data hiding a good thing or a bad thing?"
> "In C++ you can bypass the encapsulation, so it's a bad thing."
You skipped a bunch of context in between that I thought still applied.
Maybe you're reading messages in a different order than I am. That's one of
the problems when a conversation really gets going online. :-)
If you're not going to apply that context, see my other messages where I
explain the types of access and seem to be mostly in agreement with you.
>>> Every single discussion about programming with you turns into C++ bashing,
>>> no matter what the subject.
>> I'm happy to bash any other unsafe OO language you might want to name. :-)
> It's getting really tiresome and old.
I didn't start out bashing C++ this time. We got pretty far until you
*seemed to* claim C++ was better at modularity than C# or Java or CLOS. I
merely listed C++ amongst a half-dozen languages I was criticizing for
different individual design choices.
--
Darren New, San Diego CA, USA (PST)
My fortune cookie said, "You will soon be
unable to read this, even at arm's length."
Post a reply to this message
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
Warp wrote:
> Darren New <dne### [at] san rr com> wrote:
>>> That's the whole idea in modular thinking: You don't know what's inside
>>> the module, and you *don't care*. You don't need to care.
>
>> It also prevents code from *inside* the module from doing certain things in
>> an easy way.
>
> Everything in the class is public to the class itself. I don't understand
> your point.
I can use reflection to iterate over my own member variables without
breaking modularity. I can use reflection to look at the documentation of
other classes without breaking modularity. I can use reflection to
instantiate an instance of a class whose name is in a string without
breaking modularity.
My point is that not all uses of reflection are kludgey, and including it in
a language doesn't mean the design of the language is badly broken.
For further examples, peek at the Google API for database interaction on
Google App Engine. For example, looking at
http://code.google.com/appengine/docs/python/gettingstarted/usingdatastore.html
you can see how a "Greeting" class is declared, a "greetings"
list-of-greeting is created by db.GqlQuery(), and later the elements of that
list are accessed via greeting.author syntax. How does db.GqlQuery() create
a greeting instance and fill in the author based on the names of the rows of
the database? Reflection.
> So your solution to the problem of being able to modify private members
> by accident because of the language being unsafe is to make the language
> safe... and to make everything public (with a naming convention of
> variables).
>
> I don't see how the latter follows.
No. Making it safe and not making everything public and not needing naming
conventions is best. That's what CLOS does, modulo reading the source and
invoking interpreter-internal routines with the names of private variables.
That's what C# does, modulo the reflection libraries. That's what C++ does,
modulo using pointers and UB.
It would be better, of course, if languages like Python didn't expose
private variables unless you were intentionally doing meta-programming. But
Python has all kinds of hacks to let you control access to variables. For
example, there's even a case where if you type
x = y.z
and y.z's *value* has a method called __getvalue__ (or some such name) then
the return value of invoking that method is what actually goes into x. So,
yeah, there's a lot of non-OO stuff going on there.
>> I'm just less worried about intentionally violating modularity than
>> accidentally doing so.
>
> Problems with programming errors in unsafe languages has nothing to
> do with the subject of modularity.
I disagree, as I've explained. There's two aspects to modularity, only one
of which is addressed by unsafe languages.
> Accessing something out of boundaries by accident is always a bug
> regardless of what effects that has. This has nothing to do with whether
> modularity is a good thing or not.
Modularity is a good thing. The inability to violate modularity by mistake
is a good thing, don't you think?
>> (I've had server code written in unsafe languages where it would dump core
>> on a segfault, and the providers wanted to blame it on the fact that the Xen
>> kernel was on the machine. We weren't booting Xen, but they wouldn't even
>> look at their code until we uninstalled that kernel. Just as an example of
>> how people can avoid admitting their own bugs might be part of the problem.
>
> And this has what to do with the original subject?
That when modularity can be accidentally violated, it can be difficult to
debug, in part because those with the accidental bug can be reluctant to
admit they have a bug when it's actually the violated module that's crashing.
Again, the point is there's two parts of modularity: future upgrade
proofing, and class invariant maintenance. I think they're both important.
You seem to be completely ignoring any discussion of the second part.
> You always succeed in returning to your favorite subject: Bashing
> "unsafe" languages. And in your world there's only one such language: C++.
Not at all. Lots of unsafe languages are out there - C and assembler both
spring to mind. I don't know of many unsafe OO languages, is all. C++ is the
only OO language I know where the default operation of the language is to be
unsafe, rather than it being used as a well-labeled escape hatch where
necessary for performance. Doing unsafe things when you declare them after a
#pragma UNSAFE
for example doesn't seem to be a "kludge" in the language design to me.
--
Darren New, San Diego CA, USA (PST)
My fortune cookie said, "You will soon be
unable to read this, even at arm's length."
Post a reply to this message
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
Darren New wrote:
> My point is that not all uses of reflection are kludgey, and including
> it in a language doesn't mean the design of the language is badly broken.
Oh, and a lot of these language use reflection to implement debuggers,
performance monitors, and unit test frameworks. :-) None of which I'd call
kludges or violations of modularity.
Funny enough, every Python class and method has documentation that's
included in the class (in a member called "__doc__"). There are python test
frameworks that go thru all your classes and members, finding the document
strings, looking for something that looks like the result of running tests
interactively in the doc string, then runs the tests and compares the results.
http://docs.python.org/library/doctest.html
The stuff in the blue strings, with >>> marking the calls (which is the
default python interactive interpreter prompt) gets run by this library,
which then tells you what failed after comparing the results to the
documentation.
In this case, the documentation for the class includes its own simple tests,
which I thought was pretty cool.
Sure, you can do all this sort of stuff with preprocessors generating code,
but that's just because compilers have all the information that reflection
is supposed to make available.
As an aside, there's actually a lack in the terminology. I use the term
"reflection" to mean looking at the source code while it runs, and
"introspection" to mean looking at the process while it runs. So the
question of "what member variables does class XYZ have" is reflection, and
"what's the name of the method two stack frames up from here" is
introspection. Unfortunately, I don't think I've convinced anyone else to
make that distinction. :-)
--
Darren New, San Diego CA, USA (PST)
My fortune cookie said, "You will soon be
unable to read this, even at arm's length."
Post a reply to this message
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
Darren New <dne### [at] san rr com> wrote:
> OK. I was confused by the fact that your post had
> 1) A paragraph about how CLOS allows access to the private variables,
> 2) a comment about languages lacking data hiding aren't really OO,
> 3) a lack of specific support for hiding means the OO is kludgey.
Me too.
Post a reply to this message
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
From: Nicolas Alvarez
Subject: Re: This is the sort of brokenness...
Date: 19 Mar 2009 22:12:25
Message: <49c2fb87@news.povray.org>
|
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
Warp wrote:
> Nicolas Alvarez <nic### [at] gmail com> wrote:
>> Instead of using private:, you could just add an underscore to internal
>> variables.
>
> But why would I want to do that? There's no benefit. There are only
> potential disadvantages.
Because Python has no real "private", and in practice the underscore seems
to work well enough.
Post a reply to this message
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
From: Nicolas Alvarez
Subject: Re: This is the sort of brokenness...
Date: 19 Mar 2009 22:15:20
Message: <49c2fc38@news.povray.org>
|
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
Warp wrote:
> I still can't comprehend what's so bad in the compiler doing the check
> that you don't access the private members.
>
It's not bad to have the compiler do that, it's good.
But some languages don't have that feature, and that doesn't make them bad
either.
Post a reply to this message
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |