> Jon Harrop wrote: > > The fact is, pattern matching is more common that mapcar. It is ubiquitous > > in languages that support it. I think that warrants putting a standard > > pattern matcher in the next version of Lisp.
> A pattern matcher is essentially a glorified if statement. The > disadvantage with if statements is that they are not extensible - and > the same applies to pattern matchers.
> So if you want to add new subtypes / subclasses to your existing > abstract data types / classes, you have to manually go through all the > if statements / pattern matchers and add new branches to handle the new > types.
> That's what dynamic dispatch / OOP is there for to solve this particular > problem. With OOP, whenever you add a subclass, you can also add the > respective new methods that are necessary to glue the subclass with the > rest of the program.
> However in most OOP approaches, now the problem is turned to the side: > It is now hard to add new functionality, because for each new function > you have to manually go trough all the classes and add the necessary > methods to define the specific behavior for those functions.
> This situation, that you can either extend the data types or the > functionality conveniently is also called the expression problem. There > is no general solution for it that applies without exceptions because > for each function or for each datatype, you have exceptions wrt what > respective specific behaviors should be.
> However, in CLOS the situation is not that pressing: Since in CLOS you > define methods outside of classes, you can actually easily do both, add > new classes and group the necessary methods with them, or add new > generic functions and again group the necessary methods with them. All > you need is some way of determining which methods are necessary - but > this is also solved well due to the introspective interface to the > object system which allows you to query the system for existing classes > and their subclasses and existing generic functions and their methods.
> As soon as you have dynamic dispatch and dynamic types, the need for > pattern matchers is greatly reduced. Pattern matchers are indeed more > useful for languages with static type systems where at runtime you > cannot refer to the type of a value anymore.
Yes. It is also interesting of your data can be described by patterns and your operations by applying patterns. Computer Algebra is such a domain. There are others. You find lots of them in applications which are using deduction or rule-based systems. Many uses have been very dynamic in nature. Users can add, edit or remove rules at runtime. This has been always very important for knowledge-based systems.
As a tool for programming in certain domains I find it useful. But then the power of the tool is determined by the domain.
> What is missing in CLOS is a generalization of method dispatch.
Is it theoretically missing or practically missing? Is there any developer of a software system who says, he I miss this feature?
> Currently, CLOS provides two kinds of specializers: class specializers > and eql specializers which dispatch on the class or on the object > identity of a parameter respectively. It would be nice if we could also > dispatch on more complex predicates, like the contents of a parameter > (for example, if it is a collection), or some other properties (like in > predicate dispatch). This is not a brand new idea, but there are some > difficult problems involved especially if you want to integrate such > generalized dispatch with a dynamic language. Several people are > currently working on ideas how to integrate this with CLOS, and there > are some good chances that this can be done with only minor > modifications to the overall design and implementation of CLOS.
This already has been explored in the past and I doubt that newer attempts will produce something useful. I see there a trend to explore mechanisms without having a problem. Later you have a solution but not problem. I see lots of work on theoretical improvements, but little work based on practical needs. There just seems to be little software that is significant, where architectural alternatives are explored and the usefulness of those then can be evaluated.
CLOS breaks done the generic functions into methods. With specific method combinations you can get also new ways to specify the purpose of a particular method in a generic function.
I'm not sure that exposing too much specifics of the object into the parameter list is a good idea. An attempt can be seen in presentations and presentation methods in CLIM. There is lots of complicated machinery there.
> I find this a much more interesting development than the integration of > pattern matcher, which is a non-extensible old hat. You would basically > get pattern matchers for free, but in a much nicer dynamically > extensible setting.
> Of course, if you don't care about dynamic extensibility, you can as > well google for "predicate dispatch" to find out about some existing > approaches which already provide such features (including at least one > for CLOS).
Jon Harrop <j...@ffconsultancy.com> writes: > DanM wrote: >> On May 7, 9:12 pm, Jon Harrop <j...@ffconsultancy.com> wrote: >>> Greenspun. The pattern matchers in Haskell, OCaml, F# and Mathematica >>> compilers are thousands of lines of code each. Pattern matching >>> algorithms remain an active area of research.
>> Of course, that's an argument *against* standardizing it just yet.
> You could say the same of garbage collection so I'm not sure that is > a very good reason to provide no pattern matching whatsoever.
Right, but it's a very good reason not to /standardize/ such a facility, but to leave implementors and users room to experiment. This is exactly the situation with garbage collection in Common Lisp: memory management is not addressed by the Common Lisp standard, and so implementors are not bound to any particular memory management approach.
On May 7, 6:12 pm, Jon Harrop <j...@ffconsultancy.com> wrote:
> Andy Freeman wrote: > > On May 5, 11:16 am, Jon Harrop <j...@ffconsultancy.com> wrote: > >> And all benefit from a useful core pattern matcher.
> > What happens if that core is more powerful (read expensive) than what I > > need?
> You don't use all of its features.
Wrong answer - I don't use it because it's more expensive.
> > If it is less powerful, then what?
> You must combine pattern matching with other approaches
In other words, I end up writing a pattern matcher. And, outside of the tokenizer, very little of the less powerful, albeit standard, "pattern matcher" is likely to be useful.
In practice, languages with standard pattern matchers "encourage" people to bend their problem to the pattern matcher. In other words, it becomes a constraint.
> > What if its token syntax doesn't match my needs?
> Change the syntax to whatever you want.
Which is almost always painful.
> > Lisp actually has lots of pattern matchers.
> None are as common as any of the other FPLs with pattern matching built in.
The relevant question is whether they are common enough for their usage.
> > And, unlike other languages, rolling your own is painless enough that not > > using one isn't a huge problem.
> Greenspun. The pattern matchers in Haskell, OCaml, F# and Mathematica > compilers are thousands of lines of code each.
Large implementations are actually a negative.
> Pattern matching algorithms remain an active area of research.
Which means that standardization is premature.
I note that python seems to do a lot of pattern matching without any language support. (Yes, there are libraries.)
If Harrop is to be believed, F# hasn't (can't?) managed that simple trick, but Lisp could.
> > However, the basic fact remains - Harrop doesn't like Lisp.
Note that Harrop doesn't tell us why he keeps flogging F# and OCaml in cll.
Maybe he'll answer a different question - why should anyone care what he thinks about what should be in lisp, a language that he doesn't like, won't use, etc?
jos...@corporate-world.lisp.de wrote: > On May 8, 11:30 am, Pascal Costanza <p...@p-cos.net> wrote: >> What is missing in CLOS is a generalization of method dispatch.
> Is it theoretically missing or practically missing?
It's a practical inconvenience that I have encountered myself, and I am aware of other examples.
Of course, "missing" is a big word. In a Turing-complete language, nothing is really "missing." ;)
> Is there any developer of a software system who says, he I miss this feature?
Yes. There is a good chance that there will be some interesting report in this regard at this year's European Lisp Workshop.
Pascal Costanza wrote: > Jon Harrop wrote: >> The fact is, pattern matching is more common that mapcar. It is >> ubiquitous in languages that support it. I think that warrants putting a >> standard pattern matcher in the next version of Lisp.
> A pattern matcher is essentially a glorified if statement.
No, because "if" statements do not deconstruct. Pattern matching replaces if/cond/car/cdr/destructuring-bind with a single unified construct that encourages high-level programming, making for simpler and faster code.
> The > disadvantage with if statements is that they are not extensible - and > the same applies to pattern matchers.
For SML/Haskell pattern matching, yes. In OCaml, you can extend the pattern matcher using macros. In F#, you can extend the pattern matcher using active patterns (views).
> So if you want to add new subtypes / subclasses to your existing > abstract data types / classes, you have to manually go through all the > if statements / pattern matchers and add new branches to handle the new > types.
Sometimes, yes.
> That's what dynamic dispatch / OOP is there for to solve this particular > problem.
Except they don't really solve it, they just alter it to make it worse as you explain here:
> However in most OOP approaches, now the problem is turned to the side: > It is now hard to add new functionality, because for each new function > you have to manually go trough all the classes and add the necessary > methods to define the specific behavior for those functions.
Exactly.
> As soon as you have dynamic dispatch and dynamic types, the need for > pattern matchers is greatly reduced.
Lisp has those yet it cannot express the symbolic simplifier as elegantly or efficiently as SML, Haskell, OCaml, F# and many other modern FPLs.
> Pattern matchers are indeed more > useful for languages with static type systems where at runtime you > cannot refer to the type of a value anymore.
Scheme and F# are obvious counter examples. F# provides run-time type information yet pattern matching remains ubiquituous, precisely because it is so useful and cannot be represented using dynamic dispatch and types.
> What is missing in CLOS is a generalization of method dispatch. > Currently, CLOS provides two kinds of specializers: class specializers > and eql specializers which dispatch on the class or on the object > identity of a parameter respectively. It would be nice if we could also > dispatch on more complex predicates, like the contents of a parameter > (for example, if it is a collection), or some other properties (like in > predicate dispatch).
That is probably a definition of pattern matching. The main difference is that it is dynamic whereas the pattern matchers that I am used to statically reflect the structure of the data.
I have no experience from CLOS but, from what I have heard, it is extremely slow. Given that one advantage of pattern matching is performance, perhaps a CLOS-based implementation is not the way to go?
Andy Freeman wrote: > On May 7, 6:12 pm, Jon Harrop <j...@ffconsultancy.com> wrote: >> Andy Freeman wrote: >> > On May 5, 11:16 am, Jon Harrop <j...@ffconsultancy.com> wrote: >> >> And all benefit from a useful core pattern matcher.
>> > What happens if that core is more powerful (read expensive) than what I >> > need?
>> You don't use all of its features.
> Wrong answer - I don't use it because it's more expensive.
For what definition of "expensive"?
>> > If it is less powerful, then what?
>> You must combine pattern matching with other approaches
> In other words, I end up writing a pattern matcher.
Which is much easier in the presence of a simpler pattern matcher.
> In practice, languages with standard pattern matchers "encourage" > people to bend their problem to the pattern matcher. In other words, > it becomes a constraint.
Not in the presence of macros or active patterns.
>> > What if its token syntax doesn't match my needs?
>> Change the syntax to whatever you want.
> Which is almost always painful.
But it is better than what you're currently doing.
>> > And, unlike other languages, rolling your own is painless enough that >> > not using one isn't a huge problem.
>> Greenspun. The pattern matchers in Haskell, OCaml, F# and Mathematica >> compilers are thousands of lines of code each.
> Large implementations are actually a negative.
Even if they are well-defined, fast and correct?
>> Pattern matching algorithms remain an active area of research.
> Which means that standardization is premature.
I don't have a problem with using the state-of-the-art.
>> > However, the basic fact remains - Harrop doesn't like Lisp.
> Note that Harrop doesn't tell us why he keeps flogging F# and OCaml in > cll.
Because lots of people here like to see what they're missing.
> Maybe he'll answer a different question - why should anyone care what > he thinks about what should be in lisp, a language that he doesn't > like, won't use, etc?
To me, the Lisp approach to destructuring data structures is so primitive and tedious that I feel I might as well be writing assembler. Rather than reinvent the wheel, I asked for advice on libraries that supplement Lisp with a more modern approach to destructuring, only to find that most of them suck and all are inherently slow because this functionality is missing from the language.
Now, if I want to write Lisp I start by asking an OCaml compiler to output its intermediate Lisp representation.
I know I'm not a "real Lisper" and that's why I get shunned here. But Alan Crowe is and look at his Lisp implementation of a ten line OCaml function:
;; much improved (defun simplify (form) (typecase form ((tuple '* 0 *) 0) ((tuple '* * 0) 0) ((tuple '* 1 *) (simplify (third form))) ((tuple '* * 1) (simplify (second form))) ((tuple '* number number) (* (second form) (third form))) ((tuple '* number (tuple '* number *) (destructuring-bind (op1 n (op2 m x)) form (list '* (* n m) (simplify x))))) ((tuple '* (tuple '* number *) number) (destructuring-bind (op1 (op2 n x) m) form (list '* (* n m) (simplify x)))) ((tuple '* (tuple '* number *) *) ;; raise buried number (destructuring-bind (op1 (op2 n x) y) form `(* ,n (* ,(simplify x) ,(simplify y))))) ((tuple '* * *) (destructuring-bind (op left right) form (list op (simplify left) (simplify right)))) ((tuple '* etc)(error "~a not binary" form)) ((tuple '+ 0 *) (simplify (third form))) ((tuple '+ * 0) (simplify (second form))) ((tuple '+ * (tuple '* number *)) ;; (+ x (* n x)) => (* n+1 x) ;; (+ x (* n y)) => (+ x (* n y)) (destructuring-bind (*1 x (*2 n y)) form (let ((x (simplify x)) (y (simplify y))) (if (equal x y) (list '* (+ n 1) x) `(+ ,x (* ,n ,y)))))) ((tuple '+ * *) ;; (+ x x) => (* 2 x) ;; (+ x y) => (+ x y) (destructuring-bind (x y) (mapcar #'simplify (rest form)) (if (equal x y) `(* 2 ,x) (list '+ x y)))) ((tuple '+ 0 * *) (destructuring-bind (op x y z) form (list op (simplify y) (simplify z)))) ((tuple '+ * 0 *) (destructuring-bind (op x y z) form (list op (simplify x) (simplify z)))) ((tuple '+ * * 0) (destructuring-bind (op x y z) form (list op (simplify x) (simplify y)))) ((tuple '+ number number *) (destructuring-bind (op x y z) form (list op (+ x y) (simplify z)))) ((tuple '+ * * number number) (destructuring-bind (op x y n m) form (list op (simplify x) (simplify y)(+ n m)))) ((tuple '+ etc) (cons '+ (mapcar #'simplify (rest form)))) (t form)))
and before you say "oh, but he's an especially stupid Lisper". Look at Andre Thieme's Lisp:
(defun simplify (xexpr) (declare (optimize (speed 3))) (if (atom xexpr) xexpr (labels ((make-expr (op f g) (list op f g)) (optimize-plus (f g) (cond ((eql f 0) g) ((eql g 0) f) ((and (numberp f) (numberp g)) (+ f g)) (t (optimize-associatively '+ #'optimize-plus f g)))) (optimize-multiply (f g) (cond ((or (eql f 0) (eql g 0)) 0) ((eql f 1) g) ((eql g 1) f) ((and (numberp f) (numberp g)) (* f g)) (t (optimize-associatively '* #'optimize-multiply f g)))) (optimize-associatively (op opt-func f g) (cond ((and (listp g) (eq op (first g))) (let ((a (funcall opt-func f (second g)))) (funcall opt-func a (third g)))) (t (make-expr op f g))))) (declare (inline make-expr optimize-associatively)) (let* ((op (first xexpr)) (f (simplify (second xexpr))) (g (simplify (third xexpr)))) (cond ((eq op '+) (optimize-plus f g)) ((eq op '*) (optimize-multiply f g)))))))
Look at Nathan Froyd's implementation:
(defun simplify-common-funcs (xexpr) (declare (optimize (speed 3))) (if (atom xexpr) xexpr (labels ((make-expr (op f g) (list op f g)) (optimize-plus (f g) (cond ((eql f 0) g) ((eql g 0) f) ((and (numberp f) (numberp g)) (+ f g)) (t (optimize-associatively '+ #'optimize-plus f g)))) (optimize-multiply (f g) (cond ((or (eql f 0) (eql g 0)) 0) ((eql f 1) g) ((eql g 1) f) ((and (numberp f) (numberp g)) (* f g)) (t (optimize-associatively '* #'optimize-multiply f g)))) (optimize-associatively (op opt-func f g) (cond ((and (listp g) (eq op (first g))) (let ((a (funcall opt-func f (second g)))) (funcall opt-func a (third g)))) (t (make-expr op f g))))) (declare (inline make-expr optimize-associatively)) (let* ((op (first xexpr)) (f (simplify-common-funcs (second xexpr))) (g (simplify-common-funcs (third xexpr)))) (cond ((eq op '+) (optimize-plus f g)) ((eq op '*) (optimize-multiply f g)))))))
I think these solutions speak for themselves.
The Lisper's rebuttal will be "pattern matching is only good for symbolic simplification which is widely admitted to be a real weak-point of Lisp". But pattern matching is equally useful for RB trees in data structures, scene graphs in computer graphics, huffman trees in data compression, suffix encoders in bioinformatics and pretty much everything else.
I'm just joining randomly intot his conversation; I've only been spot-checking occasional posts, so forgive me if this has been covered, but...
Jon Harrop <j...@ffconsultancy.com> writes: > No, because "if" statements do not deconstruct. Pattern matching replaces > if/cond/car/cdr/destructuring-bind with a single unified construct that > encourages high-level programming, making for simpler and faster code.
Be careful here. In static languages, pattern matching encourages and exploits abstraction, while in Lisp and other dynamic languages, it can bypass intentional type and dispatch on representational type, which is not always what is wanted.
I sometimes refer to such traps as "structured data abstraction violations". They look like they're being helpful, and they trick you into using them because they seem so well-packaged. But the packaging is tricking you into bypassing abstraction instead of using it, at least in cases where the static/intentional types are not one-to-one with dynamic/representational types.
This is related in turn to the equality issue, which is less severe in static languages.
Jon Harrop wrote: > Pascal Costanza wrote: >> Jon Harrop wrote: >>> The fact is, pattern matching is more common that mapcar. It is >>> ubiquitous in languages that support it. I think that warrants putting a >>> standard pattern matcher in the next version of Lisp. >> A pattern matcher is essentially a glorified if statement.
> No, because "if" statements do not deconstruct. Pattern matching replaces > if/cond/car/cdr/destructuring-bind with a single unified construct that > encourages high-level programming, making for simpler and faster code.
What part of "glorified" is it that you didn't understand?
>> Pattern matchers are indeed more >> useful for languages with static type systems where at runtime you >> cannot refer to the type of a value anymore.
> Scheme and F# are obvious counter examples. F# provides run-time type > information yet pattern matching remains ubiquituous, precisely because it > is so useful and cannot be represented using dynamic dispatch and types.
How do you know that anything in F# is ubiquitous? It only exists for three years now...
> I have no experience from CLOS but, from what I have heard, it is extremely > slow.
Jon Harrop <j...@ffconsultancy.com> wrote: > I have no experience from CLOS but, from what I have heard, it is > extremely slow.
Gosh. Now, even Lispers themselves contribute to propagate legends about Lisp slowness :-( We're in biiiig trouble I telya.
Seriously, this kind of comment is not only annoying, but completely meaningless. What part of CLOS are you talking about ? For what kind of application ? Slow compared to what ?
For instance, I've heard people claiming that CLOS *must* be slow, given all the things it does dynamically (like computing the list of applicable methods for each generic function call). But these people forget (or just don't know) about memoization.
Then there are the clowns that pretend that dynamic dispatch is "inherently slow" because you have to go through a vtable like in C++, or a hashtable in some CLOS implementation. OK, but slow compared to what ? If your generic dispatch is for finding a short function (e.g. pixel access in an image), then performance might be an issue. But if your method is going to take 10 seconds to execute, why would you care about the dispatch time ?
So, okay, I grant you the right to say that CLOS is extremely slow compared to, say, nop ;-)
Kent M Pitman wrote: > Be careful here. In static languages, pattern matching encourages and > exploits abstraction, while in Lisp and other dynamic languages, it can > bypass intentional type and dispatch on representational type, which is > not always what is wanted.
Can you elaborate on this? Do you mean that pattern matching encourages you to be less dynamic?
Pascal Costanza wrote: > Jon Harrop wrote: >> Pascal Costanza wrote: >>> Jon Harrop wrote: >>>> The fact is, pattern matching is more common that mapcar. It is >>>> ubiquitous in languages that support it. I think that warrants putting >>>> a standard pattern matcher in the next version of Lisp. >>> A pattern matcher is essentially a glorified if statement.
>> No, because "if" statements do not deconstruct. Pattern matching replaces >> if/cond/car/cdr/destructuring-bind with a single unified construct that >> encourages high-level programming, making for simpler and faster code.
> What part of "glorified" is it that you didn't understand?
So by "glorified" you mean "nothing to do with".
>> I have no experience from CLOS but, from what I have heard, it is >> extremely slow.
Didier Verna wrote: > Jon Harrop <j...@ffconsultancy.com> wrote: >> I have no experience from CLOS but, from what I have heard, it is >> extremely slow.
> Gosh. Now, even Lispers themselves contribute to propagate > legends about Lisp slowness :-( We're in biiiig trouble I telya.
Well, if Lispers say something is slow then it must be really, really slow. ;-)
> Seriously, this kind of comment is not only annoying, but completely > meaningless. What part of CLOS are you talking about ? For what kind of > application ? Slow compared to what ?
The impression I got from Lispers is that CLOS is very slow for pretty much anything non-trivial.
> For instance, I've heard people claiming that CLOS *must* be slow, given > all the things it does dynamically (like computing the list of > applicable methods for each generic function call). But these people > forget (or just don't know) about memoization.
I've heard a lot about it being extremely "flexible", which presumably means in incurs an unusually large amount of run-time checking and dispatch.
> Then there are the clowns that pretend that dynamic dispatch is > "inherently slow" because you have to go through a vtable like in C++, > or a hashtable in some CLOS implementation. OK, but slow compared to > what ?
The most common form of dispatch in other FPLs is pattern matching. Perhaps it would be good to compare the performance of CLOS-based programs with some pattern matching equivalents?
> If your generic dispatch is for finding a short function (e.g. > pixel access in an image), then performance might be an issue. But if > your method is going to take 10 seconds to execute, why would you care > about the dispatch time?
Of course.
> So, okay, I grant you the right to say that CLOS is extremely slow > compared to, say, nop ;-)
Presumably the symbolic simplifier example could be written using CLOS. If anyone here is interested in quantitative evidence, maybe they could write a CLOS implementation and benchmark it against the others?
Didier Verna <did...@lrde.epita.fr> writes: > For instance, I've heard people claiming that CLOS *must* be slow, given > all the things it does dynamically (like computing the list of > applicable methods for each generic function call). But these people > forget (or just don't know) about memoization.
I generally agree with your statement of pain about the way people suggest slowness where it dosen't exist or exaggerate it heavily where it does. Rather than comment on those remarks, which I think stand fine as you've written them, let me point out where the personal point of pain is for me:
Dynamic inheritance is semantically different than static inheritance (and, in my opinion, more correct). It doesn't implement the same semantics. So, of course, it has different performance characteristics.
There is a philosophical issue that's overlooked when one says things like "C is faster at addition than Lisp" because C isn't doing Lisp addition quickly nor is Lisp doing C's addition slowly. The two associate different operations with the symbol +. I happen to like the definition of plus that says when you give it a number, x, and 1, you get something bigger than x. The feature of that, to me, is it's more like the math I do. C says the operation is really to do what the machine does, and that I should use some other secret operation if I want the math thing, or make sure all my numbers are small, but that the most important thing is speed.
Likewise, CLOS does inheritance correctly. It can't be done truly correctly with static analysis, because when you have an abstract class, you end up dispatching only on the statically known methods for that type. When you do oher calls to other methods, and they dispatch, and so on, it's not just fast vs slow, it's what methods are actually gonig to get called. If calling a method on a parent class means you are giving up the right to have child class methods be called, then that's not just speed but correctness... Of course, this choice point in the design space is just "defined" to be right, and program errors are tolerated as "the programmer knew this was the semantics and wrote the wrong program". But I don't think that's the only possible analysis. By that analysis, we could rename + to be - and vice versa in CL, and say "that's how we define the semantics and all program errors that result from our choice of naming are due to the programmer".
The static/dynamic thing was not done as a way of saying, as a community, "we're too lazy to do things up front where they belong". Rather, we as a community have said "we want to first choose what we want to say, and when the right time is to say it, and only after that is done will we see about making that maximally efficient". That leads to different consequences, which we have optimized well. If there are better optimizations possible for those semantics, let's see an apples-to-apples comparison. But if we're only going to talk apples-to-oranges, then the fight isn't fair from the outset.
Jon Harrop <j...@ffconsultancy.com> writes: > Kent M Pitman wrote: > > Be careful here. In static languages, pattern matching encourages and > > exploits abstraction, while in Lisp and other dynamic languages, it can > > bypass intentional type and dispatch on representational type, which is > > not always what is wanted.
> Can you elaborate on this? Do you mean that pattern matching encourages you > to be less dynamic?
No, I wouldn't say that. But you must at least understand whether you are dispatching on declared type or representational type. Lisp, which does not use so-called "strong typing", doesn't know the declared type of the data it receives; it knows the dynamically accessible representational type. That's different and sometime may have different effects.
If you're just dealing with data structures as pure shapes, it's fine. But people have been known to create overlapping shapes. And if you assume your pattern matcher will naturally sort that out, you're asking for trouble. I'm not saying you have to not be a deep thinker to want pattern matcher, but pattern matchers will attract those who are not deep thinkers because they seem to involve less direct programming.
Assume these are three independent abstractions:
(defun make-simple-frob () 'frob)
(defun make-foo (x) (list 'foo x))
(defun make-frob (x) (list 'frob (list 'foo x)))
Now assume I do:
(pattern-case v ((frob (foo $x)) ...action...))
in some language where (frob $x) is the pattern with frob being a constant term and $x being a pattern variable.
Then if I give v as the result of (list (make-simple-frob) (make-foo 3)), should it match? If I give v as (make-frob 3) should it match? Are you sure that (list (make-simple-frob) (make-foo 3)) and (make-frob 3) are intended to be semantically the same?
It's hard to make a direct comparison to strongly typed languages, since strongly typed languages often don't give you the ability to examine representational tokens, and so the extra tokens like the 'foo and 'frob in the examples above would not be there to confuse things. That makes this comparison tricky. But the feature of such languages is that many such languages can't confuse: (pattern-case v (frob($x) ...)) or (pattern-case v ([bar, foo($x)] ...)) because the function wouldn't type-reconcile without knowing this.
Note well: You might assume punning is just bad and has no place in programming, so you might say that such languages give you a leg up and are automatically superior. I don't agree. While this particular instance punning has some problems, there are cases where punning and dynamic inspect gives you very sophisticated effects that are hard to simulate in static languages.
Language choices are not about Good vs Bad, they are about design trade-offs: you trade away the things you don't plan to do in favor of the things you do plan to do. The hard part is getting that planning right so you aren't pinned into a corner down the line. Since Lisp supports changing one's mind later, I tend to like that, but it comes at some occasional costs, and I think there's a minor but manageable cost on the issue of clarity of intent on certain intentional type issues. It's not a cost that troubles me because it buys other things I care about. Static typing feels like a stranglehold to me. But it's worth acknowledging that there are people who will be troubled by these design trade-offs because they value things differently.
Jon Harrop <j...@ffconsultancy.com> writes: > Pascal Costanza wrote: >> Jon Harrop wrote: >>> I have no experience from CLOS but, from what I have heard, it is >>> extremely slow.
> states that saying CLOS is not slow because computers are now faster than > they were. Well, if only I'd known that before...
Perhaps not only your hearing, but your English reading skills as well :-)
The portion of the article in question says (in a section that is responding to common myths, speicifically: "CL/CLOS is slow"):
"Since 1985, PC have become 100 times faster. Much slower languages, such as Java and Visual Basic, have become popular. Today, CL/CLOS can run effectively on any standard PC and its implementations are faster."
1. This is not just about CLOS alone. It is true that the article is about CLOS, but CLOS did not exist in 1985, and the myth about CLOS being slow is the same as the myth about CL being slow. This is why the author consistently wrote about CL/CLOS, rather than CLOS alone.
2. There is no "because" (or "therefore") stated or implied in this statement. The conjunction is "and", directly attributing CL/CLOS's speedups to both machine speedups (which have benefitted all languages, of course), as well as inherently faster implementations, both of CL and of CLOS.
On May 8, 10:52 pm, Jon Harrop <j...@ffconsultancy.com> wrote:
> Andy Freeman wrote: > > Wrong answer - I don't use it because it's more expensive.
> For what definition of "expensive"?
The "costs more" definition.
> > In practice, languages with standard pattern matchers "encourage" > > people to bend their problem to the pattern matcher. In other words, > > it becomes a constraint.
> Not in the presence of macros or active patterns.
Coulda, woulda, shoulda, but doesn't happen often enough to count.
> > Maybe he'll answer a different question - why should anyone care what > > he thinks about what should be in lisp, a language that he doesn't > > like, won't use, etc?
> To me, the Lisp approach to destructuring data structures is so primitive > and tedious that I feel I might as well be writing assembler.
Harrop "forgets" that he doesn't write Lisp, he writes F#, OCaml, etc. There's no reason for him to write Lisp, and he doesn't. Given that, why does he care?
After all, if he's correct, his choice gives him an advantage over the heathens.
Note that Fortran folks also believe that Lisp is inferior. You don't see them wandering through here. Instead, they sit back and let the bucks roll in.
> I think these solutions speak for themselves.
Not really, but it would be an improvement if we only heard from the solutions.
It's interesting that Harrop confuses "pattern matching can be used for" with "pattern matching is the right approach to".
On May 8, 10:43 pm, Jon Harrop <j...@ffconsultancy.com> wrote: here:
> F# provides run-time type > information yet pattern matching remains ubiquituous, precisely because it > is so useful and cannot be represented using dynamic dispatch and types.
If pattern matching is the preferred mechanism in F#, it's because PM is the easiest way to do things that one wants to do in F#. That's a statement about F#.
> I have no experience
That's different then - you're clearly an expert. You're wasting your time here.
On 2007-05-09 12:01:04 -0400, Andy Freeman <ana...@earthlink.net> said:
>> I think these solutions speak for themselves.
> Not really, but it would be an improvement if we only heard from the > solutions.
> It's interesting that Harrop confuses "pattern matching can be used > for" with "pattern matching is the right approach to".
He also coveniently ignores the existence of several pattern matching libraries for those who want pattern matching, while putting forth examples from usenet posts which explicitly say "how far could we go if we limited ourselves to ..." as if these were the only possibilities for pattern matching in Common Lisp.
Perl compatible regular expressions are not part of common lisp either. But I don't care, and neither does any lisper with a lick of sense because all I have to do to get perl compatible regular expressions is:
(asdf :cl-ppcre)
and Edi Weitz's cl-ppcre is loaded and ready to use.
Similarly, if I needed pattern matching I'd just load one of the existing pattern matching libraries and use it.
Note also that these provide a *variety* of solutions using different algorithms since, as even Harrop admits, "pattern matching" is not one well defined, stable, cut and dried thing. Different projects want different types of pattern matching. Common Lisp has a number of libraries.
F# has what's built in so you're encouraged to conform your thinking about the problem to the tool that F# gives you. The lisp approach is just the opposite - you're encouraged to conform the language to the way you think about the problem. Numerous useful libraries facilitate this process.
Big picture:
- Common Lisp has plenty of libraries for things which are not in the base langauge such as pattern matching.
- Harrop is just stirring up a tempest in a teapot here in order to troll for business for his flying frog consultancy.
Finally to anticipate Harrop's usual comback (for nubz, we've been on this merry-go-round with Harrop before), when you ask us to implement your current favorite OCaml/F# toy example using an existing Common Lisp pattern matching library realize that a thread just a week or two ago already gave examples using these libraries:
I suggest you try to implement your toy example du jour using one of these libraries *yourself* and post your versions here for suggestions and corrections. You might actually <gasp!> post something that is *on topic* for comp.lang.lisp.
You may have the ignorant fooled, but people who actually use common lisp know that not having pattern matching built into the language is a non-issue. Your rants here are little different than a Perl programmer showing up claiming that perl is better than Common Lisp because Perl has regexes built in. No one who actually knows Common Lisp cares, because we know we can always reach for an existing library for this when we need it.
Now either go away and shill Microsoft junk on comp.lang.basic.visual.misc where they're predisposed to like this stuff, or code up one of your F# examples using one or more of the above Common Lisp libraries and post something on topic. But stop hawking F# on a newsgroup specifically devoted to Common Lisp.
On May 8, 10:43 pm, Jon Harrop <j...@ffconsultancy.com> wrote:
> I have no experience from CLOS but, from what I have heard, it is extremely > slow.
May I suggest that you either research something or gain experience in it before making assertions?
@article{ queinnec97fast, author = "Christian Queinnec", title = "Fast and Compact Dispatching for Dynamic Object-Oriented Languages", journal = "Information Processing Letters", volume = "64", number = "6", pages = "315-321", year = "1997", url = "citeseer.ist.psu.edu/queinnec97fast.html" }
@article{ dujardin98fast, author = "Eric Dujardin and Eric Amiel and Eric Simon", title = "Fast Algorithms for Compressed Multimethod Dispatch Table Generation", journal = "ACM Transactions on Programming Languages and Systems", volume = "20", number = "1", month = "January", publisher = "ACM Press", pages = "116--165", year = "1998", url = "citeseer.ist.psu.edu/article/dujardin96fast.html" }
These will get you started. Optimization multi-method dispatch has been a topic of research for many, many years, and there is much published literature on it. An informed opinion is much more valuable than hearsay.
On May 8, 10:52 pm, Jon Harrop <j...@ffconsultancy.com> wrote:
> The Lisper's rebuttal will be "pattern matching is only good for symbolic > simplification which is widely admitted to be a real weak-point of Lisp".
Um, yeah. That's the first thing that came to *my* mind...
Pattern matching is not a single, monolithic paradigm, but a description that encompasses a number of data-driven programming techniques from simple type dispatch to unification to frame-based knowledge representation and more. As it happens, the first programming language to have tree-based pattern matching was Lisp (Fred McBride's thesis). Pattern matching, especially type-related pattern matching, is a broad and rich area of research.
Because of this, there is no one-size-fits-all pattern matcher in Common Lisp. There are, however, many pattern matching libraries. I'm sorry you find that `most of them suck', but perhaps you could enumerate the ones that you tried and we can see if anyone in the group has an idea for one that is more appropriate for your uses.