Re: macros
- From: Seamus MacRae <smacrae319@xxxxxxxxxxxxxxx>
- Date: Sun, 12 Jul 2009 03:13:17 -0400
Thomas A. Russ wrote:
Seamus MacRae <smacrae319@xxxxxxxxxxxxxx> writes:Thomas A. Russ wrote:Seamus MacRae <smacrae319@xxxxxxxxxxxxxx> writes:Except the hypothetical situation at issue was when you wanted to add aanonymous.c.lisper@xxxxxxxxx wrote:Precisely. It doesn't enter into this at all, in most cases.(defgeneric frobnobdicate (a b)Eh, I don't see any namespace related anything in that. I do see
(:method ((foo foo) (bar bar)) ;;arguments
.. body)
(:method ((foo foo) (baz baz))
..body))
(defclass foo () ;; no inheritance
(AttributesHere))
(defclass bar ()
(AttributesHere))
(defclass baz ()
(AttributesHere))
(defclass bat (baz) ;inherits from baz
())
Then I can do:
(frobnobdicate InstanceOfFoo InstanceOfBar)
(frobnobdicate InstanceOfFoo InstanceOfBaz)
(frobnobdicate InstanceOfFoo InstanceOfBat)
manually managed dispatch tables and plenty of other migraine-inducers
though.
It is nicely in the background for when you need it, but it doesn't
intrude into your day-to-day programming most of the time.
new dispatch for a method and the "defgeneric" dispatch-table thingie
was not in the same namespace you want your own code to be in.
That isn't a problem.
The recurrent mantra of a Lisp acolyte.
In the real world, of course, repeating such mantras will not magically make real, actual problems go away.
But you seem to want to argue without actually trying to understand
the technology you are arguing about.
Not true. Furthermore, a tired old debating trick, changing the subject from whatever it originally was to one's own opponent and making things personal.
Nobody with an IQ over 80 falls for that sort of lameness. Shame on you for insulting everyone's intelligence by even trying!
But perhaps I'm incorrect in this assumption.
No "perhaps" about it.
Um, yes.Suppose you created all of that stuff and then someone else wanted toUm, no.
frobnobdicate their quux objects. Now they have to mess with the
defgeneric ... etc. in your namespace, in your source code...
(defgeneric frobnobdicate (a b)
(:method ((foo foo) (bar bar)) ;;arguments
.. body)
(:method ((foo foo) (baz baz))
..body))
has to become something else, I'm guessing
(defgeneric frobnobdicate (a b)
(:method ((foo foo) (bar bar)) ;;arguments
.. body)
(:method ((foo foo) (baz baz))
.. body)
(:method ((foo foo) (quux quux))
..body))
or something close to that, to add quux to the set of classes it
dispatches on.
[calls me a liar]
No, you're the liar. I made a straightforward extrapolation from the originally-posted code, which cannot possibly be wrong unless there's some horrible special-casing for when there are more than two, or exactly three, or some such, dispatches.
Yet if there IS such horrible special-casing, then I STILL win, on my larger point that this whole thing is a god-awful mess.
Checkmate.
They just add their own method by just doingThat is in contradiction with how Anonymous C Lisper said it was
(defmethod frobnicate ((foo quux) (baz quux))
..body)
and this method gets added. They do this in their own source code.
They don't need to do anything to your source code.
done.
[calls me a liar]
No, no, no! I've told you repeatedly that I am not. Don't you listen?
Furthermore, the generic function frobnicate exists in *some*
namespace. Whose?
[calls me a liar]
This is getting tiresome.
Perhaps you were confused because ...I will not entertain public speculation regarding my mental
functioning. Please refrain from such. I will not respond to that kind
of nonsense except to trim most of it when replying and make a note
similar to this one that it is not polite behavior.
Well noting that your are confused [rest deleted]
I will not entertain public speculation regarding my mental
functioning. Please refrain from such. I will not respond to that kind
of nonsense except to trim most of it when replying and make a note
similar to this one that it is not polite behavior.
What I've seen so far of Lisp in your posts looks like an excellent
recipe for creating tightly-coupled code that will end up being a
nightmare to debug and maintain in any seriously large real-world
project. Localizing generic functions in one spot in the code causes
code-ownership-respecting change-request bottlenecks or else people
stepping on one another's toes. Disseminating them throughout the code
base, on the other hand, causes the code that's affecting *your* code to
be widely dispersed and hard to locate.
This is not any more of a problem than subclassing someone else's
class.
I had previously explained why it was one. It's kind of like that, but in a hypothetical OO system where instead of
class X { void method ... }
class Y extends X { void method ... }
you instead had
class X extended-by Y { void method ... }
class Y { void method ... }
and to add your own class Z, you had to edit the declaration of class X. Someone else's class X.
That would have all of the same localization and coupling
issues.
But not the changing-someone-else's-code issues.
Haven't you guys ever heard of encapsulation?
Sure.
Then why don't you *use* it?!
Just because the solution is different from Java's solution doesn'tNo, of course not. It's the solution not scaling to large devteams that
make it ineffective.
makes it ineffective.
[calls me a liar]
Oh, how tiresome.
See again my hammer and picnic blanket analogy for why you might want toI think that analogy was already torpedoed in another post.
be able to do new things with existing objects.
Hmmm. Missed it.
Incoherent.
Suppose you wanted to graph a tree of objects. java.lang.ClassFortunately for those of your coworkers who rely on its behavior.
doesn't have a graph() method, and you can't add one.
Um, why should coworkers care if you add a method that they don't use?
How does that affect their code.
Suppose your change to Class violated invariants that used to hold, and that their code relied upon?
Suppose it was an interface, adding to which broke their code that implemented it? (Bad enough that you can break code by editing your own interfaces -- Sun recently did this, and there's a recent thread in cljp about it; they added a method to a database interface that broke someone's code, who complained there and of course it spawned a huge debate.
A Java programmer create an external iterator that traverses classes
somehow.
And does its own type dispatch, because you can't add methods that use
the built-in type dispatch. Talk about nightmares.
The alternative was apparently worse; indeed, too horrible to contemplate.
In CLOS you just define a graph method specializing as necessarySo a Lisp programmer has the option: create an external iterator that
traverses classes somehow, or muck with a system class and thereby break
everyone else's code in the project, thus getting the entire rest of the
team mad at them and probably winding up fired.
First of all, you don't muck with anyone's code.
See above.
Second of all, you can't break anyone else's code by defining a new
function on a class that the other's don't use.
And on a class that they do use?
Just like adding a new subclass to a Java class can't break someone
else's code that uses the original class.
Not if that class was designed right, with the protected and private keywords used appropriately and the subclass therefore unable to muck with the superclass's invariants.
Oops. Common Lisp has no concept of protected. Or private. Game over.
So, it's clear you don't understand ...
I will not entertain public speculation regarding my mental
functioning. Please refrain from such. I will not respond to that kind
of nonsense except to trim most of it when replying and make a note
similar to this one that it is not polite behavior.
It still doesn't follow that one would choose a methodology that makesBut you can't have such a methodology. There are only three possibilities:
one or the other of these approaches MORE difficult, when one can
instead have a methodology where adding a noun or adding a verb are
equally easy.
[calls me a liar]
Tiresome, tiresome, tiresome.
One does package.Noun.verb;
One does package.verb(Noun);
One does package1.verb(package2.Noun) or package1.Noun.package2.verb.
The third is clearly cumbersome and difficult. Eliminate it. The first
makes it easy to add new nouns, and new verbs to new nouns, only. The
second makes it easy to add new verbs, and new nouns to existing verbs,
only. So the first makes it hard to add new verbs to existing nouns and
the second makes it hard to add new nouns to existing verbs, while the
third makes it easy to do both but hard to USE the darn things after.
Um, how about this other option:
One does package:name
and then everything you want to name: classes, functions, structures,
tags, etc. becomes equally easy to add.
That's just sneaking number three in through the back door. You mention classes, structures, atc. AND functions. So it's package1.verb(package2.Noun) then, modulo differences in syntax and capitalization conventions.
No. I advocate choosing to make 16% harder (package.Noun.verb) over
making 84% harder (package.verb(Noun)) or 100% harder
(package1.verb(package2.Noun) or package1.Noun.package2.verb).
As opposed to making all 100% easy.
Which is not possible. (No namespacing at all creates its own new nasty class of pain. Try developing in Squeak some time. The lack of namespaces for classes is *painful*, leading to collisions AND very long class names that attempt to avoid them, usually by adding poor-man's-namespaces in the form of long common prefixes in related classes' names, which in turn would play hell with name completion if the in-built editor HAD name completion ...
No, I am not, and you will kindly refrain from using accusatoryThe case at issue is when there's a pre-existing generic function withUm. You are making unwarranted assumptions again.
the desired name, in library code or otherwise where I couldn't fiddle
with it (except as a lone-wolf hacker), and in a namespace I shouldn't
put my own stuff in (except as a lone-wolf hacker).
expressions ("you are <something that's bad>") directed at me in your
news posts ever again. It's extremely rude.
Well, if you will stop making unwarranted assumptions
Wrong answer.
All you need to know is what the API for the generic function is.That lets you call it but not add to it. To add to the generic function
in Anonymous C Lisper's post (the thing with the "(foo foo) (bar bar)"
stuff in it) obviously requires its source code.
Only because you assume [rest deleted]
See above re: making things personal.
That is all you need know is the signature and the semantics (theWorks for generic functions. Doesn't work for macros, since little
latter so you know that this is actually the generic function you want).
implementation details like the names of internally used variables in
the macros and how many times it references one of its parameters will
change its behavior. Without the source code, trying to debug problems
caused by extra side effects or by collisions between your variables and
variables inside the macro will be a nightmare.
Not if the macro is designed competently so that it only uses fresh
symbols that can't clash with anything.
Series Expansion did an excellent job of explaining how that's mathematically impossible for any conceivable lexical macro system, short of one whose expansion-generation functions can employ a halting oracle. (That Lisp macros operate upon trees rather than linear text does not make a material difference to his arguments.)
Of these, the former is a pretty big deal, and the latter two mean
subtle bugs could arise where the macro got used that would be hard to
figure out without access to its source code.
Well, this has already been dealt with.
By calling people names and then sticking your head in the sand. That doesn't do ostriches much good, from what I've heard, and I doubt it will avail you of much, either.
I'll take your words as conceding that macros can be defined without
any of those problems.
How dishonest of you. I concede no such thing, of course.
(BTW: Namespaces in Common Lisp are exactlyJava packages don't, either, save that they get their own documentation
that. They are NAMEspaces and only concern themselves with the
resolution of names. They don't get overloaded with notions of
functions, classes, or anything extraneous like that)
blurb and there's a "within this namespace only" level of access control
between protected ("same-namespace and subclasses") and private ("this
class and nested classes only", roughly).
Of course, the latter probably mystifies you, like everything else to do
with encapsulation.
Hmmm. Was there some comment about being rude further up?
A comment about turnabout being fair play seems appropriate here.
4) Assuming that you are extending the generic function in a reasonableSubclassing a library class doesn't change the base class's
manner, for example by adding methods that dispatch on types that
you have defined, then there is no "lone-wolf hacker" part to this
at all. It is a controlled extension of the library. It is no more
"lone-wolf hacker" than subclassing a library class in Java would be.
behavior. Whereas letting anyone edit
Adding a new method doesn't change the base functions behavior on any
existing arguments.
Already addressed; see above.
(foo foo) (bar bar)
(foo foo) (baz baz)
to
(foo foo) (bar bar)
(foo foo) (baz baz)
(foo foo) (quux quux)
necessarily also allows editing it to
(foo foo) (mumble mumble)
(foo foo) (baz baz)
and people frobnobdicating bars elsewhere in the project getting a nasty
surprise next time integration tests are run.
Well, sure.
Well, there's the inevitable victory for my side, then.
Common Lisp assumes that the programmers are reasonable and that in a
large project they won't engage in malicious behavior.
Accidents are more my concern.
Suppose whoever went to turn
(foo foo) (bar bar)
(foo foo) (baz baz)
into
(foo foo) (bar bar)
(foo foo) (quux quux)
(foo foo) (baz baz)
fat-fingered the ) key, which is next to ', and got:
(foo foo) (bar bar)
(foo foo) (quux quux)'
(foo foo) (baz baz)
instead? Likely this will quote the (foo foo) form on the next line, with who knows what consequences. Given the lack of typing in CL, the result will probably be run-time problems with code that previously worked flawlessly and used the (foo foo) (baz baz) dispatch, rather than a build failure with an error message pointing very close to the site of the typo.
Of course, when just about everyone might have to add to that dispatch table, a typo like that becomes increasingly likely in the long run. Whereas if the table is edited only by one coder that "owns" it that coder becomes, as was previously discussed, a bottleneck in the project, somewhat like having a too-coarse-grained lock in concurrent code losing you most of the performance benefits of parallelism.
Any large project will have a set of procedures
that govern who does what and how they should do it.
Bottleneck alert. (Did you know that coarse grained locks may also be more prone to deadlock? They're held longer and more likely to overlap in time.)
There is a fundamental philosophical difference here.
That much is clear, and it seems your philosophy has no place in large professional software development projects.
This does not make sense.The hypothesis at issue is that this metaclass, or whatever, alreadySo far so code.
exists and I'm not free to modify that code.
Typo.
Excuses, excuses.
That doesn't stop you from either creating a subclass of that libraryExcept that the latter is done by modifying the piece of code with the
class or extending the generic function by adding methods.
defgeneric thingy, at least for a nonempty subset of generic functions
if not for all of them.
No.
Yes, unless you're calling both me and Anonymous C. Lisper liars.
Perhaps that is the analogy that you need to understand this. In CommonIt's not the concept that concerns me. It's the actual
Lisp adding a method to a generic function is the same as creating a new
subclass of an existing class. It extends the generic function by
allowing it to apply to a new combination of method argument types.
mechanics. Altering the dispatch table to add something means having
write access, which means having the ability to also alter or remove
something, perhaps unintentionally while trying to add something. It's a
fruitful source of breakage, it encourages tight coupling, and in some
projects, it might even be considered a security risk.
Well, you can always redefine things is Lisp.
Kaboom!
Tastes vary.
Yes, and when one of your coworkers has a taste for haggis or balut, the whole work environment suffers.
It is, in essence, adding a dynamically dispatched "subclass" to theAssuming no slips of the finger while editing the defgeneric thing, in
generic function.
cases where that's how the particular generic function is defined
instead of that other way.
Well, the same caveat applies to creating a subclass of a class.
Not quite the same. See above, once again.
This contradicts what you guys said earlier. Earlier you intimated thatMethods are not in packages.Packages and methods are very different things.Of course they are, but methods are in packages, and packages create
namespaces, and not everyone on a large project (or a typical project
even) will be able to make changes to things in every namespace.
verbs (and maybe nouns AS WELL) were in namespaces.
[calls me a liar]
How tiresome.
If methods really are not in packages, there goes the neighborhood. Name
collisions are going to be causing an unbelievable amount of headaches
in any sufficiently large project.
[calls me a liar]
*yawn*
Only symbols are in packages.I don't see that this is a useful distinction, between "the method is in
the package" and "the method's name is in the package". It amounts to
the same thing. Using Java-like syntax, as I'm more familiar with it:
Um, not really.
No, really, I used a Java-like syntax because I'm more familiar with it. Truly I did!
Just as you can subclass any class you like (except Java finalI'm sorry, but it does. Contrast:
classes), you can extend any generic function in Common Lisp.
That doesn't change the namespace.
package foo;
public class FooException extends Exception { }
which extends Exception (in java.lang) with FooException (in foo) and a
hypothetical addition of a Lisp class quux to a foo package with a
dispatch added for quuxes to a generic function in a bar package. You
aren't just subclassing something in the bar package, you're actually
changing the behavior of something in the bar package.
[calls me a liar]
ZZZzzzzzz ...
In the Java version you don't have to touch anything in java.lang. The
JVM figures out on the fly during class-loading what dispatch tables to
use. Maybe if the generic function in the bar package was defined the
"defmethod" way the same holds for the Lisp compiler or VM.
Same with Common Lisp and generic functions.
No, not really, because of the explicit dispatch tables.
However, if
the generic function was defined the "defgeneric" way, the way Anonymous
C Lisper did his, then there's a problem, because there's an explicit
dispatch table in the code instead of the compiler making one. To
frobnobdicate a quux, code belonging to the bar package would have to be
edited, and not just code belonging to the foo package (where quux
itself is).
[calls me and Anonymous C. Lisper liars]
I don't know about him, but I know I'm not one.
It merely extends the generic function, much as sublasses extendIn the above example, the addition of FooException does not prevent
their parents.
people using the original Exception.
Nor does adding a new method stop people from using the existing methods.
I'm not analogizing with the method but with the whole generic function. Editing it replaces it with a different one, rather than creating an extension that has a different name and extended abilities and *coexists* with the previous version, unlike extending a Java class.
Extending a generic function doesn't seem to me to create two versions, keeping the original version under the original name and adding a new version under a different name (ala Exception and FooException). Instead, it *changes* the generic function (frobnobdicate and frobnobdicate). This means nobody can use the original frobnobdicate -- and every bit of code referring to it refers to the new frobnobdicate now.
Obviously, havoc would ensue if subclassing Exception caused every bit
of code using Exception to suddenly be using FooException instead.
The generic function doesn't change.
Sure it does. Methods are added to it. Adding methods to Java classes and (especially) interfaces can cause havoc. The same will be true of generic functions.
In fact, though you repeatedly analogize extending a generic function to
subclassing, it seems to be more like editing Exception itself and
adding a new method to it. Maybe it doesn't disturb the existing code
that uses Exception (except for breaking subclasses that added a
same-named method that couldn't be either an overload or an override of
the new base-class method), but it does change something in java.lang,
and it also breaks encapsulation, big-time.
No
You've gotten into a very bad habit of calling me a liar every paragraph or two. Please cease and desist. Thank you.
The closer analogy in Java would be
class A {
f ();
}
class B extends A {
}
We've *been* through this already, and I've already explained why that's not quite true.
Sheesh!
Your generic functions sound to me somewhat like data-less classes, more
like a Java interface with one method specified than like a
function. Only where Java interfaces are implemented by adding
"implements Foo" to other classes, only some Lisp generic functions are
analogously "implemented"; others, the ones defined using "defgeneric"
as in Anonymous C Lisper's post, are more like a hypothetical
alternative kind of interface that lists a method name and a list of the
implementing classes and is implemented by adding an entry to that
list. It therefore cannot be implemented without changing code used by
other parts of the project, potentially breaking things.
[calls me a liar]
Figures.
Signal terminated.
.
- Prev by Date: kh-pcgames
- Next by Date: Re: macros
- Previous by thread: Re: macros
- Next by thread: Re: macros
- Index(es):
Relevant Pages
|
Loading