>> (defmethod M1 ((x INTEGER) y &key a b c) >> (print "Fourth")) > ==> Error: Multimethod arglist conflicts with extant multimethod arglist.
>> (M1 5 10) > ==> First
I'm sure Thomas knows you can't do what he presented, it was to make the point. You were asking why not dispatch on number of args. This would presumably allow the selection of definitions above. Now, if they were all defined as presented, then which M1 would be called for (M1 5 10)?
> I'm sure Thomas knows you can't do what he presented, it was to make the > point. You were asking why not dispatch on number of args. This would > presumably allow the selection of definitions above. Now, if they were all > defined as presented, then which M1 would be called for (M1 5 10)?
"presumably allow" isn't presumable here. When you can't construct a method discriminator, it is perfectly reasonable to disallow the addition of the method that creates the conflict. With these semantics, they cannot all be defined as presented.
Alternatively, you could use a more permissive semantics and define the correct behavior as calling *any* matching method - which would select one of the methods presented and call it, possibly a different method on different calls, runs, versions, or implementations. In that case, conflicting definitions becomes bad taste and may generate a warning, but needn't be disallowed.
Either would be more useful than generic methods being fixed in arity.
>> I'm sure Thomas knows you can't do what he presented, it was to make >> the point. You were asking why not dispatch on number of args. This >> would presumably allow the selection of definitions above. Now, if >> they were all defined as presented, then which M1 would be called for >> (M1 5 10)?
> "presumably allow" isn't presumable here. When you can't construct a > method discriminator, it is perfectly reasonable to disallow the > addition of the method that creates the conflict. With these semantics, > they cannot all be defined as presented.
> Alternatively, you could use a more permissive semantics and define > the correct behavior as calling *any* matching method - which would > select one of the methods presented and call it, possibly a different > method on different calls, runs, versions, or implementations. In > that case, conflicting definitions becomes bad taste and may generate > a warning, but needn't be disallowed.
> Either would be more useful than generic methods being fixed in arity.
It's debatable whether fixing the semantics to be non-deterministic is a good idea. ;)
But anyway, your sketch is very far from a specification-quality description of semantics for this.
You may want to take a look at the paper "CLOS in Context: The Shape of the Design Space" at http://www.dreamsongs.com/CLOS.html - I don't think it contains a discussion of how to handle dispatch for non-required args, but it discusses a few other possible extensions that they weren't able to nail down. It may give you an idea of the complexities of designing such language extensions.
Ray Dillinger <b...@sonic.net> writes: > Coby Beck wrote:
> > I'm sure Thomas knows you can't do what he presented, it was to make > > the point. You were asking why not dispatch on number of args. This > > would presumably allow the selection of definitions above. Now, if > > they were all defined as presented, then which M1 would be called for > > (M1 5 10)?
> "presumably allow" isn't presumable here. When you can't construct a > method discriminator, it is perfectly reasonable to disallow the > addition of the method that creates the conflict. With these semantics, > they cannot all be defined as presented.
Well, that really makes things unpredictable, especially when you consider having independent parties defining new methods for generic functions. Who is to say which one creates a conflict? And, in general, since these wouldn't be compiled at the same time, you would have to make this a run or load time error. You really wouldn't be able to tell for certain before then.
So that transforms a rule that is easily understood into one that requires you to know about every other method defined for that generic function. I think that would violate my sense of elegance of design.
It also violates part of the idea of what a generic function is supposed to represent, namely a function which is generically applicable and which then determines its method based on its arguments. Having different numbers of arguments means that it isn't really the same function, in that you are now considering additional information.
> Alternatively, you could use a more permissive semantics and define > the correct behavior as calling *any* matching method - which would > select one of the methods presented and call it, possibly a different > method on different calls, runs, versions, or implementations. In > that case, conflicting definitions becomes bad taste and may generate > a warning, but needn't be disallowed.
Wow! Talk about unpredictable. I like to be able to predict what my software will do when I write it. You are correct in noting that this will discourage people from trying to exploit this. But now we are introducin a mechanism to discourage people from using a feature that you claim you want in the language. This is madness.
> Either would be more useful than generic methods being fixed in arity.
I disagree (obviously).
Initially, I was also disturbed by the congruence requirements of generic functions, but with time and thought have come to appreciate the value of it. This is from practical, philosophical and aesthetic points of view.
It means you always know how to call a generic function without having to know the precise particulars of its argument types. After all, one of the points of using a generic function rather than an ordinary function is that you get (slightly) different behaviors based on the argument types -- but you aren't required to know this when you call it, because the generic dispatch handles that for you.
If you have to know the precise type of the arguments, so that you can decide how to formulate the generic function, I don't see why you are bothering with a generic function in the first place. You want a somewhat different, specialized function. So why not use an ordinary non-generic function in its place? It isn't performing the same operation, since there are a lot more arguments to whatever is being done. That is also why is suggested that methods should really only do slightly different things from one another. If you want to do greatly different things, then you really want a different function. A single function should perform a particular operation, and the different methods on a generic function really should implement semantically similar operations, as appropriate to their arguments.
From a purist and philosophical point of view, that argues for exactly the same argument list in each method. Common Lisp is a bit more flexible than that. Lambda-list congruence applies only to the required arguments. If you want to have other arguments, they can be optional, rest or keyword arguments.
Perhaps the closest to what you seem to want are the keyword arguments, which can be different for each method. Now that possibility has to be planned for in advance(*) when you write the generic function signature and the other methods. Then you can have things like
(defmethod m ((a INTEGER) &key b c &allow-other-keys) ...) (defmethod m ((a SYMBOL) &key x y &allow-other-keys) ...) (defmethod m ((a CLASS-C) &key c y j &allow-other-keys) ...)
And then you can actually call things like
(m 3 :b 8 :c 'fred) (m 'george :y 10)
But you also still have some functionality by not having to know exactly which of the keywords are there:
But I think that this is getting a bit out of hand, as far as figuring out what the semantics of M is supposed to be. I favor a cleaner semantics.
(*) OK, you don't really have to plan it in advance, since you can either be really careful about which method gets invoked (raising again the question of why bother with methods if you have to already know exactly which one is used), or you use :allow-other-keys t in the method call. But I would call that bad design.
-- Thomas A. Russ, USC/Information Sciences Institute
Ray Dillinger <b...@sonic.net> writes: > Thomas A. Russ wrote: > > Ray Dillinger <b...@sonic.net> writes:
> >>This is something about CLOS generic functions I don't get; why don't > >>they dispatch on number of arguments?? > > (defmethod M1 ((x INTEGER) &optional y)
> > (defmethod M1 ((x INTEGER) y &key a b c) > > (print "Fourth")) > ==> Error: Multimethod arglist conflicts with extant multimethod arglist.
> > (M1 5 10)
> ==> First
Sorry, I guess I was a bit too cryptic in my post.
It wasn't meant to demonstrate the Common Lisp DOES this type of dispatch, but rather to be a thought experiment showing one reason why the designers may have decided NOT to support such dispatch.
It's clear that CL doesn't allow these methods, and the examples were meant to be an argument for why that isn't done.
If you assume the various method definitions were legal, could you come up with a principled answer to what the dispatcher should do?
-- Thomas A. Russ, USC/Information Sciences Institute
Thomas A. Russ wrote: > Ray Dillinger <b...@sonic.net> writes:
>>Coby Beck wrote:
>>>I'm sure Thomas knows you can't do what he presented, it was to make >>>the point. You were asking why not dispatch on number of args. This >>>would presumably allow the selection of definitions above. Now, if >>>they were all defined as presented, then which M1 would be called for >>>(M1 5 10)?
>>"presumably allow" isn't presumable here. When you can't construct a >>method discriminator, it is perfectly reasonable to disallow the >>addition of the method that creates the conflict. With these semantics, >>they cannot all be defined as presented.
> Well, that really makes things unpredictable, especially when you > consider having independent parties defining new methods for generic > functions. Who is to say which one creates a conflict? And, in > general, since these wouldn't be compiled at the same time, you would > have to make this a run or load time error. You really wouldn't be able > to tell for certain before then.
> So that transforms a rule that is easily understood into one that > requires you to know about every other method defined for that generic > function. I think that would violate my sense of elegance of design. >
And this is different from the current design how? I mean seriously, Who says now which definition creates a conflict? If two different people specialize the same generic function for integer first arguments, they're gonna conflict, even at the same arity. Arguing against dispatch-on-arity on this ground without also arguing against the status quo seems silly, doesn't it?
> Initially, I was also disturbed by the congruence requirements of > generic functions, but with time and thought have come to appreciate the > value of it. This is from practical, philosophical and aesthetic points > of view.
> It means you always know how to call a generic function without having > to know the precise particulars of its argument types. After all, one > of the points of using a generic function rather than an ordinary > function is that you get (slightly) different behaviors based on the > argument types -- but you aren't required to know this when you call it, > because the generic dispatch handles that for you.
I have grown wary of all arguments of the form "It's good to follow this rule so we'll disallow all other ways of doing it..." Surely if it's good to follow a rule you can go on following it even if other options are available. I assume coders are smart and have good taste. If something doesn't work with their design, they should know it and not use it. And I'm chary of our ability to guess in advance that there is only one way of doing things that must perforce work with all designs.
> If you have to know the precise type of the arguments, so that you can > decide how to formulate the generic function, I don't see why you are > bothering with a generic function in the first place. You want a > somewhat different, specialized function. So why not use an ordinary > non-generic function in its place? It isn't performing the same > operation, since there are a lot more arguments to whatever is being > done.
I disagree. Consider print methods that take an variable-size list of things to print or have the output port as an optional argument. Consider eval methods with a lexical environment as an optional argument. Consider HTML formatters that take varying numbers of HTML fields and options via keyword args... All of these families are "the same function" conceptually but have different arities.
> Perhaps the closest to what you seem to want are the keyword arguments, > which can be different for each method. Now that possibility has to be > planned for in advance(*) when you write the generic function signature > and the other methods. Then you can have things like
> (defmethod m ((a INTEGER) &key b c &allow-other-keys) ...) > (defmethod m ((a SYMBOL) &key x y &allow-other-keys) ...) > (defmethod m ((a CLASS-C) &key c y j &allow-other-keys) ...)
Ah! Okay, yes, that definitely goes a long way toward answering the need. 'Scuse my ignorance, I didn't realize there was a way to make specialized keyword arguments with lambda-lists that didn't conflict.
Thomas A. Russ wrote: > Ray Dillinger <b...@sonic.net> writes: >>Thomas A. Russ wrote: >>>Ray Dillinger <b...@sonic.net> writes: >>>>why don't[CLOS generic functions] dispatch on number of arguments?? >>>(defmethod M1 ((x INTEGER) &optional y) >>> (print "First")) >>==> T
>>>(defmethod M1 ((x INTEGER) y &key a b c) >>> (print "Fourth")) >>==> Error: Multimethod arglist conflicts with extant multimethod arglist. >>>(M1 5 10) >>==> First > Sorry, I guess I was a bit too cryptic in my post.
> It wasn't meant to demonstrate the Common Lisp DOES this type of > dispatch, but rather to be a thought experiment showing one reason why > the designers may have decided NOT to support such dispatch.
> It's clear that CL doesn't allow these methods, and the examples were > meant to be an argument for why that isn't done.
> If you assume the various method definitions were legal, could you come > up with a principled answer to what the dispatcher should do?
I'm sorry, I gues I was a bit too cryptic in my response. It wasn't meant to demonstrate that Common Lisp DOESN'T allow these method definitions, but rather to be a reminder that conflicting definitions ought to be handled exactly the same way they are now.
It's clear that CL doesn't allow these methods, and the responses were meant to be an argument that the reason for not allowing them is utterly unchanged by having methods of differing arity.
I assume the various method definitions are illegal, because they are conflicting, and my principled answer to what the dispatcher should do is exactly what it does in the case of conflicting definitions now.
>> No, the order is not deterministic. ANSI Common Lisp specifies the >> following error situations:
>> - The number of arguments don't match and/or keyword arguments don't >> match. This is specified in 3.5 of the HyperSpec.
> This is something about CLOS generic functions I don't get; why don't > they dispatch on number of arguments??
Kind of useless, isn't it? (I have not about if it is even possible given the rest of GF dispatch.)
When would I ever want different behavior based on how /many/ arguments I supply to a function? One wants different behavior with different types of arguments, or different argument values as when we do an EQL specialization.
But: (foo 1 2) vs (foo 1 2 3)?
I mean, sure, we want to be able to code that, but not to get different behavior. We want the same behavior for a variable number of values (without having to code up (list 1 2 3) to get it). And of course Lisp has &rest and &optional for that.
Now conceivably one wants:
(foo 'x) (foo 3) (foo "abc")
and then for a list one wants to get into some extended specialization not needed by the other case:
(foo '(1 2) 42) (foo '(1 2) "abc")
But then I think one has probably actually started a new GF, or is collapsing what should be two GFs into one.
>>> No, the order is not deterministic. ANSI Common Lisp specifies the >>> following error situations:
>>> - The number of arguments don't match and/or keyword arguments don't >>> match. This is specified in 3.5 of the HyperSpec.
>> This is something about CLOS generic functions I don't get; why don't >> they dispatch on number of arguments??
> Kind of useless, isn't it? (I have not about if it is even possible > given the rest of GF dispatch.)
> When would I ever want different behavior based on how /many/ arguments > I supply to a function? One wants different behavior with different > types of arguments, or different argument values as when we do an EQL > specialization.
> But: (foo 1 2) vs (foo 1 2 3)?
> I mean, sure, we want to be able to code that, but not to get different > behavior. We want the same behavior for a variable number of values > (without having to code up (list 1 2 3) to get it). And of course Lisp > has &rest and &optional for that.
> Now conceivably one wants:
> (foo 'x) > (foo 3) > (foo "abc")
> and then for a list one wants to get into some extended specialization > not needed by the other case:
> (foo '(1 2) 42) > (foo '(1 2) "abc")
> But then I think one has probably actually started a new GF, or is > collapsing what should be two GFs into one.
> kenny
I should have just said, This is one of the nice things about using a language that (a) we know is generally brilliant and (b) is going on fifty years old and (c) has been through a standrads process; they probably got it right.
Ray Dillinger <b...@sonic.net> writes: > Pascal Costanza wrote: > This is something about CLOS generic functions I don't get; why don't > they dispatch on number of arguments??
Because language design is about choosing a set of designs among possible ones. And some decisions may preclude other decisions.
For example, in the Erlang language, you can have multiple instances (definitions) of the foo functions, differing in their arity. In fact, they are completely different functions: foo/1, foo/2, foo/3 etc.
However, CL has variable length argument lists, optionals and keywords. Choosing to have these [*] precludes going the Erlang way. I bet you won't be able to provide a satisfactory definition for the behaviour of a dispatcher on the number of arguments *in the presence* of &optional, &rest and &keys (not to forget &allow-other-keys).
So what's fine in e.g. Erlang is not applicable in another language.
[*] Old Lisps knew only (arg1 ... argn [ . arg-rest ]) You can still see traces of the explicit dot notation in the macro DESTRUCTURING-BIND. BTW, Scheme also uses this notation.
Regards, Jorg Hohle Telekom/T-Systems Technology Center
(defmethod M1 ((x INTEGER) y &key a b c) (print "Fourth"))
(defmethod M1 ((x INTEGER) y &key a c d) (print "Fifth"))
(M1 10 2 :a 3) ==> ?
Now, it may be possible to specify some precedence relationship amongs &optional, &rest, &key, but can it ever be completely unambiguous?
The current set of rules has the benefit that they don't have any cases where you can't tell. I don't see how you can avoid that with certain combinations of arguments. Would you say that the "Fourth" and "Fifth" versions above are intrinsically ambiguous? Or only if the caller doesn't specify either :b or :c?
And what is the point of having optional arguments to a function if the fact that they are optional may never have any impact. Using the original examples, the only way you could even get to the "First" M1 method when its optional argument is specified is via a (call-next-method) in the "Second" or "Third", etc. methods.
At some point you have to acknowledge that the specification and implementation of all of this is getting so arcane that it isn't worth the effort. And in the final analysis, what does one gain? Where is the compelling example that shows that the CL-style of method dispatch really gets in the way of writing useful, maintainable and understandable code?
Ray Dillinger <b...@sonic.net> writes: > Thomas A. Russ wrote: > > Ray Dillinger <b...@sonic.net> writes:
> >>Coby Beck wrote:
> >>>I'm sure Thomas knows you can't do what he presented, it was to make > >>>the point. You were asking why not dispatch on number of args. This > >>>would presumably allow the selection of definitions above. Now, if > >>>they were all defined as presented, then which M1 would be called for > >>>(M1 5 10)?
> >>"presumably allow" isn't presumable here. When you can't construct a > >>method discriminator, it is perfectly reasonable to disallow the > >>addition of the method that creates the conflict. With these semantics, > >>they cannot all be defined as presented. > > Well, that really makes things unpredictable, especially when you
> > consider having independent parties defining new methods for generic > > functions. Who is to say which one creates a conflict? And, in > > general, since these wouldn't be compiled at the same time, you would > > have to make this a run or load time error. You really wouldn't be able > > to tell for certain before then.
> > So that transforms a rule that is easily understood into one that > > requires you to know about every other method defined for that generic > > function. I think that would violate my sense of elegance of design.
> And this is different from the current design how? I mean seriously, > Who says now which definition creates a conflict? If two different > people specialize the same generic function for integer first > arguments, they're gonna conflict, even at the same arity. Arguing > against dispatch-on-arity on this ground without also arguing against > the status quo seems silly, doesn't it?
This argument is silly. If you redefine EXACTLY the same function, then of course you get a conflict, regardless of whether the function is generic or not. Now, for generic functions, the definition of what it means to be EXACTLY the same is relaxed just a bit: A GF is exactly the same if all of the required arguments are the same and have the same types.
Conflicts caused by redefining the same function are unavoidable. But making the definition of exactly the same more complicated does not, in my opinion improve the software engineering environment.
> > Initially, I was also disturbed by the congruence requirements of > > generic functions, but with time and thought have come to appreciate the > > value of it. This is from practical, philosophical and aesthetic points > > of view. It means you always know how to call a generic function > > without having
> > to know the precise particulars of its argument types. After all, one > > of the points of using a generic function rather than an ordinary > > function is that you get (slightly) different behaviors based on the > > argument types -- but you aren't required to know this when you call it, > > because the generic dispatch handles that for you.
> I have grown wary of all arguments of the form "It's good to follow > this rule so we'll disallow all other ways of doing it..." Surely > if it's good to follow a rule you can go on following it even if > other options are available. I assume coders are smart and have good > taste. If something doesn't work with their design, they should know > it and not use it. And I'm chary of our ability to guess in advance > that there is only one way of doing things that must perforce work with > all designs.
> > If you have to know the precise type of the arguments, so that you can > > decide how to formulate the generic function, I don't see why you are > > bothering with a generic function in the first place. You want a > > somewhat different, specialized function. So why not use an ordinary > > non-generic function in its place? It isn't performing the same > > operation, since there are a lot more arguments to whatever is being > > done.
> I disagree. Consider print methods that take an variable-size > list of things to print or have the output port as an optional > argument. Consider eval methods with a lexical environment as > an optional argument. Consider HTML formatters that take varying > numbers of HTML fields and options via keyword args... All of > these families are "the same function" conceptually but have > different arities.
Yes, but there is nothing to stop you from writing these functions under the CLOS model. And I'm sure that one could conceivably imagine trying to use some sort of dispatch to do things with the variable-size print, but that would seem not to really be something one would want to do. Would you reall want to write something like:
Presumably what one would do in an actual design would be to have the top-level, presumably even non-generic function multi-print take the variable length argument list and then iterate over it calling the generic forms that match up printing of individual argument types.
The general notion is that the &... argument list options provide ways of specifying different argument lists to the same function. But in all of those cases, there is actually only a single function. There doesn't have to be some dispatch decision, so the different shapes of the argument list can be resolved in a reasonable fashion, since the shape of that arglist is known.
You get the same thing with the current CLOS method system, in that once you have resolved the dispatch by looking at the required arguments, you are then given the grammar for parsing the entire argument list. But to have to figure out the dispatch by building a discrimination test that has optional elements to it sounds like an implementation nightmare.
So, the real question why all of those elements should go into the dispatch decision. IMHO it would not be possible to design an unambiguous or even efficient dispatch method for such an open-ended problem. Of course, since this is lisp, anyone who feels strongly the other way can easily write their own method dispatching system and use that one instead. I don't think the MOP is quite flexible enough to do that inside CLOS, [but I'm not certain about this -- I'm not a MOP expert]. But you could use the Macro system to build your own object system.
> > Perhaps the closest to what you seem to want are the keyword arguments, > > which can be different for each method. Now that possibility has to be > > planned for in advance(*) when you write the generic function signature > > and the other methods. Then you can have things like > > (defmethod m ((a INTEGER) &key b c &allow-other-keys) ...)
> > (defmethod m ((a SYMBOL) &key x y &allow-other-keys) ...) > > (defmethod m ((a CLASS-C) &key c y j &allow-other-keys) ...)
> Ah! Okay, yes, that definitely goes a long way toward answering > the need. 'Scuse my ignorance, I didn't realize there was a way > to make specialized keyword arguments with lambda-lists that didn't > conflict.
Ah. The benefits of having a well thought out system.
You can usually manage to achieve what you want within the constraints of the language, because it was designed to be useful and support a reasonable design space.
-- Thomas A. Russ, USC/Information Sciences Institute
For number of arguments in general: required > optional opional > key key > rest
> What about
> (defmethod M1 ((x INTEGER) y &key a b c) > (print "Fourth"))
> (defmethod M1 ((x INTEGER) y &key a c d) > (print "Fifth"))
> (M1 10 2 :a 3) ==> ?
> Now, it may be possible to specify some precedence relationship amongs > &optional, &rest, &key, but can it ever be completely unambiguous?
Yes. If you gave the situation above, one way to solve the problem is to throw an error (at declaration time) and ask the user to decide which method should take precedence. It would be like adding a new method without b or d which could be considered more specific. Then when the generic method is called it will always call the right one in case of a tie.
> The current set of rules has the benefit that they don't have any cases > where you can't tell. I don't see how you can avoid that with certain > combinations of arguments. Would you say that the "Fourth" and "Fifth" > versions above are intrinsically ambiguous? Or only if the caller > doesn't specify either :b or :c?
> And what is the point of having optional arguments to a function if the > fact that they are optional may never have any impact. Using the > original examples, the only way you could even get to the "First" M1 > method when its optional argument is specified is via a > (call-next-method) in the "Second" or "Third", etc. methods.
Lisp is a dynamic language. Maybe one person wrote that as the original function. And then another person created a new method latter to override the default behavior. I don't see a problem if logically, a piece of functionality never gets used in the program.
> At some point you have to acknowledge that the specification and > implementation of all of this is getting so arcane that it isn't worth > the effort. And in the final analysis, what does one gain? Where is > the compelling example that shows that the CL-style of method dispatch > really gets in the way of writing useful, maintainable and > understandable code?
Ultimately, what I would like to see is the application of a function being completely determined by pattern matching, whether it is in the number of arguments or their types.