-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Bringing back Variable Arity Methods #130
Comments
Looks good. See embedded comments.
I would like to bring back variable arity methods, with certain restrictions. Here is the proposal, as we discussed it during today's video meeting.
Syntax
In a method declaration, the final parameter of a method can be written paramName ... or paramName:ParamType ...
I have a slight preference for including a comma before the dots: paramName, … or paramName: ParamType, …
I think that looks more normal as we normally separate parameters by commas. (We don’t allow dots as prefix operators or as parts of method names, right?)
Variable arity parameter lists shall be permitted only as the final parameter of a method. So,
method ex (p) why (q...)
is permitted, but
method ex (p...) why (q)
is not permitted.
A method declaration m(p...) shall be treated as declaring methods m(_), m(_,_), m(_,_,_), m(_,_,_,_), and so on, for all methods m(...) with any number of parameters greater than zero. It does not declare a method m with zero parameters. This means that m(p...) overrides inherited declarations of m with any non-zero parameter list.
If m(p...) is inherited, and m(_,_) declared locally, does the declaration of m(_,_) override that of m(p...) with all parameter list lengths, or just that for parameter lists of length 2? Or, is such a local declaration illegal?
I'd make it illegal. I think it is too confusing to have a generic definition as well as specific definitions that override a finite number of pieces. In fact, if there is a method declaration
method m(a, b, …) {…}
then I would not allow a direct definition of m(a,b), m(a,b,c), etc. I.e., this is a slight strengthen of our rules agains static overloading. (I believe this is implicit in what you wrote for 3.) I believe this makes sense as including
method m(a,b) {…}
means there are two conflicting definitions with two arguments.
The semantics seems fine.
Kim
|
Writing this stuff down, especially the overriding rules, and the following discussion, is tending to make me want to withdraw my recantation. In other words, I'm thinking that I should just put list, set, sequence and dictionary with 1, 2, 3, ... 9 parameters into the collections library, and leave it at that. The types are particularly troubling, too. In a method with header
parameter Of course, we don't request the method with an argument of type Dave Ungar suggested that the only reason that I’m suggesting this is that in the year or more since we took out variable arity, I've forgotten just how horrible it was. I think that he has a point. |
there are two sides here.
* requester -
an interface based either on varargs or overloading is cleaner than one based on "lineups"
the distinction between "foo(1,2,3) and "foo [1,2,3]" goes away
we can get rid of lineups (and transition type parameters to single square brackets)
there is still a question about whether "foo" and "foo (1)" should be seem as the same or different requests
* implementer -
having varags is easier for implementers than not.
I'm sure Dave likes multiple methods, it's what you do in Self
(or a doesNotUnderstand handler, and I don't think he went that far)
varargs still needs a built-in implementation for sequences
but it is much less obvious (unless you think of the ... annotation on arguments
as being a very weird indirect form of sequence literal)
the obvious move is:
- remove lineups, design the library as as variadic interface
- implementations could keep varargs as an extension
- if & when we cannot stomach a library with 50 differenve versions of "list(a1),list(a2)" etc
then we put varargs in on the definition side.
looking at e.g. JS and Python and everything else, the temptations with varargs will be
- to make internal varargs list type support a full collections interface.
- to introduce a "spread" operator so you can unpack a list in a request to a (non-variadic) method
J
|
As I understand it you want to put in methods:
list (a1)
list (a1, a2)
list (a1, a2, a3)
…
list (a1, …, a9)
Are you also intending to leave in the list method that takes a lineup? If you also leave that in, then if you are going to leave in a version that takes a lineup (or equivalent) then you need to give it a different name:
listFromCollection(lst)
It would seem odd to leave out the generic conversion from lineup or similar, though obviously it could be programmed by the student.
I think at this point I still lean a bit more toward varargs, as long as we are restrictive about overloading rules.
Kim
P.S. I am hoping to be available for our meeting tomorrow afternoon, but I may have to take my wife to west LA to get a new passport (lost, somehow) for her flight to Africa on Wednesday. I’ll let you know if I can’t get back in time.
… On Jul 29, 2017, at 10:28 AM, Andrew Black ***@***.***> wrote:
Writing this stuff down, especially the overriding rules, and the following discussion, is tending to make me want to withdraw my recantation. In other words, I'm thinking that I should just put list, set, sequence and dictionary with 1, 2, 3, ... 9 parameters into the collections library, and leave it at that.
The types are particularly troubling, too. In a method with header
method m(n:Tn, p:Tp...) {
...
}
parameter n has type Tn, but parameter p does not have type Tp — it has type Sequence⟦Tp⟧. (That's actually the reason for putting the ... immediately after the type annotation — you have to think of the ... as modifying the annotation.
Of course, we don't request the method with an argument of type Sequence⟦Tp⟧ — in fact, it's impossible to do that.
Dave Ungar suggested that the only reason that O'm suggesting this is that in the year or more since we took out variable arity, I've forgotten just how horrible it was. I think that he has a point.
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub <#130 (comment)>, or mute the thread <https://github.com/notifications/unsubscribe-auth/ABuh-vbjuz80QoRi4BxZfnAxaW5Dg_nHks5sS2urgaJpZM4OjQuG>.
|
Are you also intending to leave in the list method that takes a lineup?
whatever happens with varargs, I really hope we can do away with lineups.
If you also leave that in, then if you are going to leave in a version that takes a lineup (or equivalent) then you need to give it a different name:
listFromCollection(lst)
the remaining design question is:
whether "list" should be a zero-arg vararg, and so return an empty list (that's what our earlier vararg design would have done, although it leads to odd syntax in places)
or whether "list" should be a separate zero-arg request (the smalltalk distinction between #list: and #list) that is *not* part of the variadic "request family", but which, e.g., can return a "class".
That class can then offer list.empty[T] and list.from[T]( _ : Collection[T] ), etc
I can see the attraction of the “separate” design - list(x,y,x), list.empty, list.from( c )
But it also seems a big magical to me. list(x,y,x), list, listFrom(c) ??
There's a non-magical but still confusing solution:
* either the empty list, or any list, has a "from( c )" request (or even an empty request)
that makes a brand new list with little reference to the old one.
… I think at this point I still lean a bit more toward varargs, as long as we are restrictive about overloading rules.
|
With regard to this row:
Explicit syntax
Zero-Varargs Syntax
Nonzero-varargs
Explicit class syntax
...
Make a list from another collection (or stream?)
list.from(a,b,c)
listFrom( c )
list.from( c)
list.from(c)
we talked about using `from` in preference to `withAll` in today’s meeting. But there is a reason to prefer the current method name `withAll` over `from`, which I had forgotten. Here it is:
The methods to add a single element and a collection of elements to an existing list are `add(elm)` and `addAll(coll)`.
The methods to create a collection from a single element and from a collection of elements are `with(elm)` and `withAll(coll)`.
I like the symmetry of these names; replacing `withAll` with `from` looses that. So I think that I would be opposed to the change.
Andrew
|
On 2/08/2017, at 17:43PM, Andrew Black ***@***.***> wrote:
I like the symmetry of these names; replacing `withAll` with `from` looses that. So I think that I would be opposed to the change.
sure. minor point. could go to addFrom(x) I guess. Or + or ++ :-)
J
|
|
As far as I recall, the intended resolution of this issue is as follows. Others should correct me.
Oh, I should have added: we will not be re-instituting variable arity methods at this time. The issues with overriding seem too complex. |
On 8th August 2017, in an email, @kjx wrote
|
Andrew's comment above corresponds to my understanding of our agreement. |
I can't remember. This doesn't answer the key question(s) we talked about. Kim asked:
minor points:
Which I'd still like to get rid of - go back to Scala type syntax...
It's possible we could juggle thing to work in just these cases:
At which point we're probably killed quickly by variance. At least we'd need Ceylon-style separate input (contravariant) and output (covariant) interfaces for collections... Dialects, such as beginningStudent, may continue to use list(), list(,_), etc. if they so desire. Oh, I should have added: we will not be re-instituting variable arity methods at this time. The issues with overriding seem too complex. |
As I understand it from Andrew's write-up, we write [1,2,3,4] for a sequence and list.withAll[1,2,3,4] for the list created from the sequence. |
Well that's the question. That or |
this was accepted as per Andrew's design |
I would like to bring back variable arity methods, with certain restrictions. Here is the proposal, as we discussed it during today's video meeting.
Syntax
In a method declaration, the final parameter of a method can be written
paramName ...
orparamName:ParamType ...
Variable arity parameter lists shall be permitted only as the final parameter of a method. So,
is permitted, but
is not permitted.
A method declaration
m(p...)
shall be treated as declaring methodsm(_)
,m(_,_)
,m(_,_,_)
,m(_,_,_,_)
, and so on, for all methodsm(...)
with any number of parameters greater than zero. It does not declare a methodm
with zero parameters. This means thatm(p...)
overrides inherited declarations ofm
with any non-zero parameter list.If
m(p...)
is inherited, andm(_,_)
declared locally, does the declaration ofm(_,_)
override that ofm(p...)
with all parameter list lengths, or just that for parameter lists of length 2? Or, is such a local declaration illegal?Semantics
When a method with a variable arity parameter list is requested, the variable-arity parameter is bound to a
Sequence
of objects of its annotated type. So, if the method header has the formn
is bound to the first argument supplied in the request ofm(...)
,o
is bound to the second, andp
is bound to all of the remaining arguments.p
is treated as having typeSequence⟦Unknown⟧
.If the method declaration has the form
p
is bound to all of the arguments supplied in the request ofm
, except for the first two, which are bound ton
ando
,p
is treated as having typeSequence⟦Tp⟧
. This may generate a type error if any of the 3rd, 4th, 5th ... arguments in the request does not conform toTp
.The text was updated successfully, but these errors were encountered: