Re: Seeking computer-programming job (Sunnyvale, CA)

S

Series Expansion

There is no "commonplace lisp problem of short names". If you want
to write long descriptive names in Lisp you can do it just fine.

That doesn't help you much when you have to use pre-existing code, or
implementation-standard names. ("car", anyone?)
 
A

Alessio Stalla

eric-and-jane-smith said:
Macros in other programming languages often seem to obfuscate the code as
much as they improve it, such that there may be little or no net gain.  
But CL macros can make the code orders of magnitude clearer, easier to
read, easier to work with, and less error prone.  There is a powerful
synergy between the different advantages of CL macros, such that knowing
them as a list of advantages is not enough to understand their real
power.

Yes, a good point.

I've lately decided that I really like the example of WITH-OPEN-FILE as
an example of a nice Lisp macro.  OK, it's already in the language, but
the important point is that it would be something easy to add if it
WASN'T in the language.  This exposition can proceed by looking at the
problem it was put in to solve, and comparing it to two Java solutions.

Problem: When you open a file, you have to remember to close it, even if
         the code using the file happens to throw an exception.

OK.  The standard Java solution would be to use "try ... finally":

    OutputStream out = null;  // Needs definition outside try block.
    try {
       out = new FileOutputStream(filename);
       // do stuff
    } finally {
       if (out != null) {
         try {
            out.close();
         } catch (IOException ioe) {
            // Do something like log this.
         }
      }
    }

So, this is just a bit cumbersome, because you have to remember to
declare the variables in the correct place, remember to put in the
finally block, with appropriate safety checks.  Note that because the
close() method can also throw an exception, you have to wrap it in its
own exception handling block.  That's a lot of infrastructure that you
need to write around the file handling code that you really care about,
namely the "// do stuff" part.

Note in particular, that STATIC TYPING DOES NOTHING to enforce this type
of good programming practice.  That's because it isn't a type error, but
an oversight in program strucure.  Macros, on the other hand, allow you
to easily capture such good practice.

[Aside: There has of late been a lot of "excitement" about pattern-based
design.  This is all well and good, but it is something that,
essentially, Common Lisp has had all along through the judicious use of
macros.  In fact, lisp has gone beyond the notion of mere patterns and
instead calls this aspect the process of building a Domain Specific
Language (DSL)]

Now, you can, in fact, write the same thing in Common Lisp, yielding a
program that looks kind of like this:

   (let ((out nil))
     (unwind-protect
       (progn
          (setq out (open filename :direction :eek:utput))
           ;; Do stuff
          )
       (unless (null out)
         (close out))))

where we set up the same basic structure.  Now, since we nearly always
want to do something like this when we open the file, we would like to
have a safe way to do this that is convenient and gets the details
right.  So we take the basic outline of what we want and write a macro
that embodies this particular idiom:

 (defmacro with-open-file ((var filename &rest open-options) &body body)
   `(let ((,var nil))
      (unwind-protect
        (progn
          (setq ,var (open ,filename ,@open-options))
          ,@body)
        (unless (null ,var)
           (close ,var)))))

So now we have a macro that automatically handles the task of opening a
file and remembering to close it.  That simplifies our code to the more
perspicacious and correct, because the macro has been carefully
constructed:

  (with-open-file (out filename :direction :eek:utput)
     ;; do-stuff
    )

This also allows us to concentrate our attention on the actual DO-STUFF
part, which is the variable part of this endeavor and the place where
logic bugs can occur.  We don't have to bother ourselves looking at all
of the boilerplate and making sure we got it right, since we've already
gotten the boilerplate parts correct in our macro.

So, how would one go about doing something similar in Java.  The real
crux of the matter is that there isn't any facility in the language that
allows you to introduce a new control structure that allows you to
insert your actual program into the middle of some combination of
existing constructs.

As near as I can tell, the method one would need to use involves
encapsulating the program you want to execute inside a (possibly
anonymous) class with a defined "run" interface so that it can be
executed.  That would involve something along the lines of

public interface FileProcessor {
   public void processFile (OutputStream out);

}

public class ProcessFile {
  public static void processOpenFile (String filename, FileProcessor worker) {
    OutputStream out = null;
    try {
       out = new FileOutputStream(filename);
       worker.processFile(out);
    } finally {
       if (out != null) {
         try {
            out.close();
         } catch (IOException ioe) {
            ioe.printStackTrace();
         }
      }
    }
  }

}

OK.  With this infrastructure defined, we can now write code that looks
sort  of like this:

ProcessFile.processOpenFile(filename,
                            new FileProcessor () {
                              public void processFile(OutputStream out) {
                                // do stuff
                              }
                             });

where we create a new anonymous class instance to encapsulate the body
of the code that we want to execute.

So, it is possible to build up such abstractions in Java, but to my eyes
that looks a lot clunkier and less integrated than the Lisp macro
solution.

The "Spring" Java framework uses this pattern alot in its so-called
"template" classes, which help removing part of the boilerplate from
using some of the most common Java libraries. For example, when using
Hibernate (most used Java ORM) you have to write something like this
all the time:

//sessionFactory is initialized once and then reused everywhere
Session session = sessionFactory.createSession();
Transaction t = session.beginTransaction();
try {
//do stuff
t.commit();
} catch(HibernateException e) {
t.rollback();
} finally {
session.close();
}

While Spring gives you something like

HibernateTemplate tmpl = new HibernateTemplate(sessionFactory);
tmpl.doInHibernate(new HibernateCallback() {
public void dontRememberItsName(Session s) throws HibernateException
{
//do stuff
}
});

This is IMHO not only much uglier than a Lisp macro; it's really
conceptually different. It's more like

(do-in-hibernate (lambda (session) ...do stuff...))

where do-in-hibernate is a higher-order function, not a macro. This
might not look like a big difference to someone that is not accustomed
to macros, but can quickly become a pain in the @$$ if you start
nesting many pseudo-macro calls like that. Plus, the code loses some
of its "declarative" look, and loses big functionality too - no more
tampering with the lexical environment is allowed, including voluntary
variable capture, local declarations, etc. and also no more code
walking to analyze and transform user-provided code, since it's hidden
in a lambda. Of course, these are advanced concepts - I myself really
used them just a few times, mostly for experimentation - but knowing
they're possible gives you another weapon against the hardest
problems.
You see, there isn't any way of introducing new forms into
Java that look like the language itself.  So there isn't any way to add
your own control structure construct that looks like

 with_open_file (out, filename) {
    // do stuff
  }

which would be the real equivalent of the Common Lisp macro solution.

In fact, with-open-file style macros are so useful that IIRC the
designers of C# built them into the language, with the using(resource)
{ ...code... } construct. Of course, language designers can only
foresee a limited set of patterns that can have the dignity of a
dedicated language construct. So, you're out of luck as soon as you
encounter a pattern the language designers haven't included, unless
you have macros.
So this is a very simple example of  what is meant when lisp programmers
talk about using macros to write code that is correct, and a very
simplistic example of how we go about writing DSLs to solve problems, by
removing the superstructure required by the programming language from
sight so we can concentrate on getting the actual business logic right.

Really good points. I'll save your post and show it to my fellow
coworkers - all Java developers ;)

(To be fair, this is not much a Java problem but a general non-Lisp
problem ;-) In fact, Java is pretty advanced for being a "mainstream"
language - compare for example with C++ and its total lack of dynamic
typing, no reflection, no garbage collection, no anonymous classes
that can at least simulate closures, etc. etc.).

Alessio
 
G

gugamilare

Cool. But why store a function that's there in your code anyway?

Also, the commonplace lisp problem of short names. The name "st" in
particular isn't particularly clear in meaning, and moreover, you'll
quickly run out of short names to use in a big enough project anyway.
st has its own package - namespace. If you want to use it, you need to
import it into your package. So someone who doesn't want to use st can
use it for something else, even if this someone uses a library that in
turn uses my library. This is how CL package system works. But,
anyway, that is not the point here.
 
S

Series Expansion

It does not work like that at all, it is very different from that
actually. Macros don't CHANGE your code, they GENERATE code.

Code with bugs, potentially, if the macro OR its inputs has bugs.
It would be comparable to some function in Java that generates text
that is going to be evaluated by the compiler as the real code instead
of the macro call. Someone said that Seed7 templates are just a
function that is run during compile time. Well, that is the very
definition of Lisp macros.

Templates are a notorious source of headaches in languages that
support them.

They are a source of power too; I don't claim otherwise. But with that
power tends to come a greater complexity to bug-hunting. The more
tightly coupled some pieces of code are (say, a macro and its inputs)
the harder it is to narrow a bug down to exactly one of those pieces.
There are systems written in CL that survive (or survived) for years
running WHILE being developed / debugged / used, so you CANNOT say
that CL have this huge security hole, or that it is that simple to
blow everything up.

Who said anything about security holes? And there's another issue --
modifying a running system.

1. If you goof, the whole thing might come down like a house of cards.
You can actually do that in Java now with the debugging interface
-- change something to null that shouldn't be, while the program
is running, and watch the dominos go over one by one.
So, if you have a mission-critical system (say, your web site)
and change something and goof, you're off the air and one of
your customers is inexplicably billed for $1,048,576.00 instead
of $19.99 for their order of the it-slices-it-dices-of-the-week.
Versus editing source files, then compiling them to a binary
(NOT overwriting the running, production one), then running the
new binary on a test machine and debugging it until it works,
then quietly replacing the old binary with the new-but-tested one.
Now your customers won't get subjected to your betas, alphas, and
pre-alphas without notice or consent. No JEE programmer worth his
salt would be caught dead changing their production system while
it was running for obvious reasons.
2. If you change the code on the fly, where do the changes go? Will
they survive a restart of the program, a reboot of the box, a
power failre? How does any of this integrate with revision
control? Oh, wait, see point 1: there probably cannot even be
any revision control, and Lord knows where the source files go,
if there even are any.
If you don't want to learn Lisp to really know what macros are, fine,
but don't start flaming about things that you don't understand. I did
not say that Java is a bad language.

Flaming? Flaming?? Flaming is when a bunch of invaders from
comp.lang.lisp pop up in comp.lang.java.programmer and call some
nicely formatted Java code someone posted a "pile of vomit", which is
something I distinctly recall seeing upthread just a little ways. It
wasn't my code, so I let it pass, but I did notice it.
 
T

Thomas A. Russ

Series Expansion said:
Not yet. There are plans afoot to address this, in Java 7 or (more
likely) Java 8, by adding some sort of syntactic sugar here, perhaps
full-blown closures.

Then it might be C++, Java > Lisp.

Hmmm. A curious algebra, this one.

Each time a language adopts a featuer from Lisp, it becomes better than
Lisp. As Alice would say, "curiouser and curiouser"
 
G

gugamilare

Another potential source of headaches too, I'll warrant. Imagine the
chaos that would ensue if Java did allow this. Someone might add a
method to the String class called "foo" to do X. Someone else adds a
method to the String class called "foo" to do Y. Then someone tries to
use both pieces of code together in the same project and kaboom!

The only way out looks like adding a whole 'nother system of package
namespaces *inside* *each and every class* for its methods.

Even then you get dependency headaches. Code calling someString.foo()
won't compile if you don't have the right non-standard String
extension in your classpath somewhere. It's one thing if you have
instanceOfSomeUnfamiliarClass.foo() and obviously need a library
installed, but when code calls non-existent methods on standard
library classes like String, who you gonna call? A google search for
"SomeUnfamiliarClass" might turn up the needed library; a google
search for "String" isn't likely to help much.

You are once again flaming what you do not understand. The CL package
system will solve the name collision with no harm done. Any method you
write, you will write it in your own library's namespace. Two
different libraries can implement many and many functions with the
same name. They will only make external the functions / methods that
provide the functionality of the lib. If anyone want to use the two of
them together, and it has some symbol clashes (probably just a few of
them), this person can explicitly tell how the clashes should be
handled.
 
T

Tamas K Papp

Not yet. There are plans afoot to address this, in Java 7 or (more
likely) Java 8, by adding some sort of syntactic sugar here, perhaps
full-blown closures.

Then it might be C++, Java > Lisp.

You are just not getting it, are you? Those languages will be
superior to Lisp when _you_ can add constructs like that.

Tamas
 
S

Series Expansion

Strangely enough, this doesn't occurs in Lisp.  
Are lisp programmers super geniuses?
Or are they just not imaginative enough to shoot themselves in the foot?
By the way, why would any body want to shoot himself in the foot?
No, that's silly, we just don't do that.

Okay. So your thesis is that lisp programmers are perfect. Maybe not
super geniuses, but perfect. They do not have accidents. They do not
make mistakes. They never shoot themselves in the foot.

OK, got that.

I think we're done here. :)
consequently, you can still run programs that are 50 years old on
modern lisp system.

OK, not quite done. This gem absolutely has to be polished and brought
into the light for all to see.

Yes, folks, Pascal is seriously suggesting that it might be a PLUS
that we can run fifty-year-old code. Code with Y2K bugs, no GUI,
cryptic diagnostic messages, and rudimentary teletype output. Code
that will probably make the operating system complain that "Windows
cannot find a compatible punched-card reader. Plug one into a USB port
and click OK to continue." Good luck with that.

In practice, code tends to be obsolete even quicker than hardware
does. Library code not so much, but application code and business
logic, definitely. Fifty-year-old code won't know about your mouse or
graphics hardware. It won't know about curses. It probably won't even
know about line-oriented interaction from the dark ages. It's probably
got a batch-processing interface. Say, anyone got an emulator for a
DEC reel-to-reel tape drive from the sixties?

Business logic suffers worse than the user interface, if anything.
Fifty-year-old tax software will get your taxes wrong. Fifty-year-old
accounting and legal software won't know about Sarbanes-Oxley. Fifty-
year-old patent-filing software thinks universities still can't patent
shit. Fifty-year-old software certainly can't provide cloud-based
fulfillment services to rival Amazon's S3 offerings. Try finding a
fifty-year-old media player that can parse your mp3s, or a fifty-year-
old compiler that groks Java for that matter. I'm not sure fifty-year-
old code can even grok TCP/IP; certainly not NNTP, HTTP, or HTML. So
much for modern network integration.

OK, so it can run fifty-year-old code. Now tell me why you'd want to.
That's strange.  Are you sure you are looking at lisp?  When I do, I
see solutions, not problems.

Solutions, perhaps, but not apparently to typical desktop users'
problems. Or else where are the desktop applications that are coded in
Lisp?

(As an added zinger: Amazon's server-side stuff did once include quite
a lot of Lisp, though obviously not fifty-year-old Lisp. Then they
migrated to Java. I wonder why?)
 
A

Alessio Stalla

Cool. But why store a function that's there in your code anyway?

Also, the commonplace lisp problem of short names. The name "st" in
particular isn't particularly clear in meaning, and moreover, you'll
quickly run out of short names to use in a big enough project anyway.




Java is Turing-complete, so it can probably be done.

Ahh, the good old Turing argument! By the same line of reasoning,
since Common Lisp is Turing-complete too, you can do in it everything
you do in a statically typed language, and the two are just
equivalent :)

The point here is not what is theoretically possible, but what is
doable reasonably with the language by a human being.
You can certainly implement an interpreter in Java that can save and
load its code. This also lets the function be created at run-time from
other data or user-input.

You can also monkey about with ClassLoader and byte arrays and
bytecode, or (since Java 6) with ClassLoader, Compiler, and Strings
full of Java source code, and make classes on the fly, save them to
disk (e.g. as .class files), load them, instantiate them (with
reflection), and invoke them (with reflection, or polymorphically
through a compile-time-known base class or interface).

So yeah, Java can do it, though it's clunky. And Java can do it with
"functions" created on the fly, not just some existing one like
Math.sin(double). In the case of using ClassLoader and eventually
calling the object via a base class or interface rather than
reflection, and using a JIT-enabled JVM, it can even run the new
function with performance rivaling compiled and optimized C code.

Look, I have a concrete proof Java can do what you say and more: it's
called ABCL. It's a CL implementation written in Java. :)

Peace,
Ale
 
S

Series Expansion

Oh, yes, of course, because there is even a CL compiler written in
Java :p

That is not exactly what I meant. You can do anything, but how non-
extensible, non-compatible with existing code and complicated this
would be? The library that I made is pretty small, straightforward and
readable, and it fits inside CL perfectly, with small changes when you
want to use it.

And if code using your library and code using a different library to
do the same job get used together, it's collision time.

Heck, just two hunks of code separately using your library, but with
different "small changes". Then there's GUARANTEED to be name clashes
and incompatibility.

Meanwhile, your objection to Java's capabilities in this area (which
are used very sparingly anyway, thus apparently needed very sparingly)
is that they will cause ... incompatibility.
An interpreter would be slow.

Hence the alternative, further down in my post.
Why don't you write your own entire compiler instead with the new
feature added?

Why bother with the time and (probably) bugs when you can use the
existing compiler?
If anyone needed to do this in Java, he
or she would not do it, and change the approach he or she is using to
not need it. That is what I meant by "it can't be done".

In other words, when you said "it can't be done" you didn't actually
mean it can't be done. Closer to "nobody wants to do it". Only if
you'd stated it that way to begin with, it wouldn't have seemed to
support your argument. "Lisp lets you more easily do something that
hardly anybody wants to do." Wow! What an endorsement! :)
 
G

gugamilare

Code with bugs, potentially, if the macro OR its inputs has bugs.

Just like a function can have bugs. Or do you write bugless functions?
Tell me the secret then.

By using macros the bug will be in one single place, so you can easily
change it. If you write the same code a hundred times and realize that
it is wrong, you will have to change the one hundred places where you
wrote that same thing. This is clearly not the case if you use a macro
- there is only one place that you need to change. Of course you can't
make things perfectly, but they do most of the times get a lot better
if you use macros.
Templates are a notorious source of headaches in languages that
support them.

Macros are not a source of headaches in CL because they are most times
as simple as a normal function. And you can always expand the code
directly from the source (by hitting a small sequence of keystrokes)
if you thing it has a bug somewhere.
They are a source of power too; I don't claim otherwise. But with that
power tends to come a greater complexity to bug-hunting. The more
tightly coupled some pieces of code are (say, a macro and its inputs)
the harder it is to narrow a bug down to exactly one of those pieces.

I really don't think this is the case with CL macros.
Who said anything about security holes? And there's another issue --
modifying a running system.

1. If you goof, the whole thing might come down like a house of cards.
   You can actually do that in Java now with the debugging interface
   -- change something to null that shouldn't be, while the program
   is running, and watch the dominos go over one by one.
   So, if you have a mission-critical system (say, your web site)
   and change something and goof, you're off the air and one of
   your customers is inexplicably billed for $1,048,576.00 instead
   of $19.99 for their order of the it-slices-it-dices-of-the-week.
   Versus editing source files, then compiling them to a binary
   (NOT overwriting the running, production one), then running the
   new binary on a test machine and debugging it until it works,
   then quietly replacing the old binary with the new-but-tested one.
   Now your customers won't get subjected to your betas, alphas, and
   pre-alphas without notice or consent. No JEE programmer worth his
   salt would be caught dead changing their production system while
   it was running for obvious reasons.

Listen, I didn't design the system, so I don't know about how things
worked. This is just an example of how stable and modifiable a CL
system can be.
2. If you change the code on the fly, where do the changes go? Will
   they survive a restart of the program, a reboot of the box, a
   power failre? How does any of this integrate with revision
   control? Oh, wait, see point 1: there probably cannot even be
   any revision control, and Lord knows where the source files go,
   if there even are any.

How about saving your changes in a file before you load them??? And
using a persistent database as well. This is the programmer's problem.
Flaming? Flaming?? Flaming is when a bunch of invaders from
comp.lang.lisp pop up in comp.lang.java.programmer and call some
nicely formatted Java code someone posted a "pile of vomit", which is
something I distinctly recall seeing upthread just a little ways. It
wasn't my code, so I let it pass, but I did notice it.

It surely wasn't me who did that, so don't throw it on my face.
 
S

Series Expansion

st has its own package - namespace. If you want to use it, you need to
import it into your package. So someone who doesn't want to use st can
use it for something else, even if this someone uses a library that in
turn uses my library. This is how CL package system works. But,
anyway, that is not the point here.

Oh, Lisp has namespaces already? That's not too bad. Though Java has
had this from the outset:

package foo;
import bar.St;

public class SomethingThatUsesBarsSt {
// ...
}



package quux;
import foo.SomethingThatUsesBarsSt;

public class St {
// My St!
// ... do something with a SomethingThatUsesBarsSt
// ...
}
 
S

Stefan Ram

gugamilare said:
Oh, I forgot this question. Because in Lisp you can create functions
like objects. The canonical example:
(defun make-adder (n)
(lambda (x) (+ x n)))

class Adder
{ final int n;
public Adder( final int n ){ this.n = n; }
public int add( final int x ){ return x + this.n; }}

public class Main
{ public static void main( final java.lang.String[] args )
{ final Adder add0 = new Adder( 0 );
final Adder add1 = new Adder( 1 );
java.lang.System.out.println( add0.add( 0 ));
java.lang.System.out.println( add1.add( 0 )); }}

0
1
 
S

Series Expansion

And before the universe comes to an end. But *just* before.

What are you using, Java 1.1? We're at Java 6 now and have had JIT and
near-C-speed performance for quite some time now.
 
S

Spiros Bousbouras

That doesn't help you much when you have to use pre-existing code,

If someone decided to use short names in their code that may be
considered a problem with that particular piece of code but it's
certainly not a problem with Lisp. Lisp doesn't force anyone to
use short names.
or implementation-standard names. ("car", anyone?)

If a name is part of the core language then every Lisp
programmer will know what it means , there's no need for long
descriptive names. Do you think it would be an improvement for
Lisp if car had a longer name?
 
K

Kaz Kylheku

["Followup-To:" header set to comp.lang.lisp.]
On 13 Mai, 10:52, Nicolas Neuss <[email protected]>
wrote: [...]
Hmm, I consider myself a Lisp fan and would not want to choose between
these two statements.  Maybe you should refine your simplistic world
view a little qbit?
As I already said: This is not my simplisic world view.
Proof: Just tell me about great language features that are
missing in Lisp.

This is a really strange request, because language features aren't
arbitrarily composable. In a lot of circumstances adding one feature
means you've got to get rid of, or compromise, another feature. C-
style pointers strike me as the best example of this kind of feature;
they're a great feature for C to have, but you don't want them in a
lot of other languages because the make it so easy for you to light
your own hair on fire[1]. Common Lisp relegates them to the realm of
non-standard extensions, which is (IMO) as it should be.

Actually, you can make C-like pointers in Lisp if you want.

http://paste.lisp.org/display/71952

You can use (ref <place>) to take the ``address'' of some expression
that refers to a place, and (deref <r>) to map such a reference
to an an expression that designates that place. The reference
is a first class value, and its existence prevents the
referenced object from disappearing; i.e. the ``pointer''
can't suddenly go bad.

I didn't implement pointer arithmetic here, because it didn't consider that a
requirement. That would be doable over vectors and arrays, and a special-case
representation of references for this situation.

The C feature of using offsetof to displace pointers within a struct would, of
course, not map nicely to Lisp objects or structs.
 
S

Series Expansion

You are once again flaming what you do not understand.

I'm not the one doing the flaming here at all.
The CL package system will solve the name collision with no harm done.

I just explained why the package system won't. Adding new classes in
their own packages? No problem, the package system can distinguish
foo.String from bar.String. Adding new methods to an existing class,
say foo.String? Uh-oh. Is that baz's foo.String.doIt() or quux's
foo.String.doIt()? They have the same method name, are methods of
classes with the same name, and the classes are in the same package.
As far as the compiler is concerned, they are the same. The package
system cannot distinguish them unless we add another layer of
namespacing, this time INSIDE classes, and have foo.String.baz.doIt()
and foo.String.quux.doIt() instead of just foo.String.doIt() as the
fully-qualified names. And now we're deep in migraine-headache
territory, without oar or compass...
They will only make external the functions / methods that
provide the functionality of the lib.

Java can do this too.

So, basically, you're saying that the way you avoid the name clash is
to make baz's String.doIt() and quux's String.doIt() private. Of
course, this now means that anyone who wants their own code to call a
String.doIt() has to roll their own and can't get a canned one from a
library. Because if they do, and there's multiple incompatible
libraries that provide this, then we're back to square one and name
clashes.

Of course, the best way to avoid multiple incompatible libraries is to
have commonly desired functionality built into the standard library.
And then letting third parties create (non-private, at least) methods
in your classes is unnecessary.

Of course letting them create private ones is also tricky. They need
to be a new kind of private: private to the module that defines them,
NOT to the class they're defined in (now that those are different).
Now the private/public/local/global scope system gets more
complicated.

Everywhere I look I see tradeoffs here.
If anyone want to use the two of
them together, and it has some symbol clashes (probably just a few of
them), this person can explicitly tell how the clashes should be
handled.

Ah, I love the smell of boilerplate code in the morning!
 
T

Thomas A. Russ

Series Expansion said:
Another potential source of headaches too, I'll warrant. Imagine the
chaos that would ensue if Java did allow this. Someone might add a
method to the String class called "foo" to do X. Someone else adds a
method to the String class called "foo" to do Y. Then someone tries to
use both pieces of code together in the same project and kaboom!

The only way out looks like adding a whole 'nother system of package
namespaces *inside* *each and every class* for its methods.

Well, it seems that Common Lisp has already solved that problem.

They have namespaces (called "packages" in Lisp terminology) that take
care of this problem for you.

So this is absolutely not a headache.
Even then you get dependency headaches. Code calling someString.foo()
won't compile if you don't have the right non-standard String
extension in your classpath somewhere.

Hmmm. Classpath. Lisp doesn't have classpaths. Of course, you do have
the headache of making sure that even code containing such a call, even
if it never executes, won't compile. So you can't do early testing of
partial implementations.

And if you want to defend Java, I wouldn't get started on library
dependencies. I've experienced software combinations where one had to
have multiple versions of a particular library available, and in exactly
the right order in the classpath so that the code would run.

But again, this isn't really a problem in Lisp. I don't know why you
think it would be. The language just works differently.
It's one thing if you have
instanceOfSomeUnfamiliarClass.foo() and obviously need a library
installed, but when code calls non-existent methods on standard
library classes like String, who you gonna call? A google search for
"SomeUnfamiliarClass" might turn up the needed library; a google
search for "String" isn't likely to help much.

Um, you are making the mistake of commenting on an object system that
you don't understand.

Strange at it may seem, there actually is more than one possible model
of object-oriented programming. In fact, Lisp (not Common Lisp) at one
time had a message-passing object system called Flavors, that was a
single-dispatch system a lot closer to the Java/C++ model. But
experience with that and the development of Common Loops (an object
system, it has nothing to do with the LOOP macro) lead to the
development of the Common Lisp Object System.

In CLOS, objects just encapsulate data. Period.

Methods DO NOT BELONG TO CLASSES. Instead they are organized base on
the function they perform. Each such "generic function" is a particular
operation. Methods are added to the generic function to handle
particular argument types (via dynamic multiple dispatch). So the
methods really belong to the generic function.

And if no such generic function is defined, your lisp compiler will
complain about it. So what you would do is Google for the generic
function you wanted. And if that function has the behavior you want but
no method for your classes, you can just add it yourself! So your
complaint about how to search for the method just underscores your
profound lack of understanding of the technology you are trying to
criticize. Hard to take seriously.

Being able to create generic functions that work with objects created by
others (say in some library code you are using) is a nice way to add
functionality that the library authors didn't anticipate. To illustrate
this I like to use the analogy of a hammer.

Suppose one manufactured hammers in the manner of an object model where
methods belonged directly to classes. In that case, there would be a
set of operations anticipated by the library designer for these new
hammer objects. Typical operations would involve things like driving
nails and pulling them out. Very functional.

Now, suppose we are laying out a picnic blanket on a windy day. We
might decide that we would like to use a hammer to help hold it down.
But we can't do that with an ordinary hammer, because it doesn't have a
method for anchoring a blanket. We could only do that if we created our
own specialized hammer-that-can-anchor-blankets subclass, or else if we
put the hammer inside a special anchoring-objects bag first. The plain,
ordinary hammer can't fulfill this new purpose, because it wasn't
anticipated.

And without access to the source code, you can't change this.

Now the real irony in this discussion is that often the most outrageous
claims are made about Lisp, based on ignorance of the basics of the
language design and philosophy. That isn't even to say any real
practical knowledge gained by using the language. On the other hand,
almost all of the Lisp programmers are a lot more familiar and often do
program in other languages like Java.

I suppose one could adopt the view that the Common Lisp advocates have
somehow had their brains warped by the experience, and inexplicably
cling to the view that Common Lisp is nicer to use for many things. But
perhaps a more rational view is that those people most familiar with
both languages prefer Lisp. Those people who don't really know anything
about Lisp are the ones that don't like it. So what unifying theory
could explain such facts. I wonder.

In any case, there are lots of objections that just display a
fundamental ignorance of the Lisp language. It makes it hard to take
the critics seriously when they complain about perceived problems that
JUST DO NOT EXIST in Common Lisp programming practice.

And we'll ignore the delusion that strongly typed languages which
compile are therefore bug-free.
 
S

Series Expansion

You are just not getting it, are you?  Those languages will be
superior to Lisp when _you_ can add constructs like that.

If _I_ can add constructs like that, my coworker can too, probably
ones that are incompatible with mine, making our attempts to
collaborate blow up in our faces. Then our boss wonders why we're
stuck, and whether the money the company is spending on our salaries
and benefits would be better spent elsewhere...

That degree of malleability, in a team environment, is not all it's
cracked up to be. Ever heard of the Tower of Babel? After years of
work it would up vaporware. Guess why.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,432
Messages
2,571,682
Members
48,796
Latest member
Greg L.

Latest Threads

Top