ceylon / ceylon-spec

DEPRECATED
Apache License 2.0
108 stars 34 forks source link

syntax for qualified types #410

Closed gavinking closed 11 years ago

gavinking commented 12 years ago

Belatedly, I've noticed a potential inconsistency in the syntax for qualified types and metamodel references.

In principal, if we defined the metamodel type Class to have a member type, Iterator, for example, then the following phenomenon would occur:

This is not an actual ambiguity as such, but it's certainly undesirable. Of course, it's a phenomenon that will never occur in practice, if we simply don't define any member classes of the metamodel types.

A second related observation is that with the introduction of the syntax Iterable::Iterator in #403 we have gained a second redundant way to express a qualified type. This is also undesirable, I believe.

There's three ways to handle this that I can see:

  1. Do nothing: this is certainly a wrinkle, but since we're the only ones defining the metamodel, we can make sure it never manifests in practice. Java (and C#) developers are used to writing Iterable.Iterator, and . is much easier on the eyes than :: or @. It's not especially strange that type-level operators have slightly different semantics to value-level operators—just look at | and &. Hell, we didn't even notice this wrinkle until now, so the chance of anyone else actually noticing it is somewhere in the range -1p..+1p.
  2. Use :: for qualifying types: we now have a totally reasonable and unambiguous syntax for writing qualified types. The semantics of #403's Foo::Bar is a simply much better and more natural fit here than the semantics of .. We're be adopting a really strained second interpretation of ., just because we think it looks slightly prettier, and because we're used to it. Why have two totally synonymous ways to qualify a type name?
  3. Always qualify metamodel references with @: we already need that for attribute references. Why not force you to write, for example @Iterator.Iterable(), making it completely unambiguous that you're talking about the metamodel here. Then Iterator.Iterable could mean exactly the same thing wherever it appears.

I'm quite uncertain which option I prefer. Option 2, perhaps.

quintesse commented 11 years ago

Well I meant to use them only when necessary, so you keep @Person but add @<Integer|Float>

gavinking commented 11 years ago

well that doesn't really fit into the scheme I outlined above. How would you handle Map.Entry.key?

Sent from my iPhone

On 15/05/2013, at 12:38 PM, Tako Schotanus notifications@github.com wrote:

Well I meant to use them only when necessary, so you keep @Person but add @<Integer|Float>

— Reply to this email directly or view it on GitHub.

gavinking commented 11 years ago

and stuff like String|Map.Entry?

Sent from my iPhone

On 15/05/2013, at 12:38 PM, Tako Schotanus notifications@github.com wrote:

Well I meant to use them only when necessary, so you keep @Person but add @<Integer|Float>

— Reply to this email directly or view it on GitHub.

quintesse commented 11 years ago

Well couldn't you still do the following?

@<String|Map.Entry>

Or combine the suggestions?

@`String|Map.Entry`

That way you keep the short version for many cases and only use the grouping when necessary. I suggested the < > only because it's already used with type grouping, but of course in this case it's not only used with types (as seen with Map.Entry.key), so maybe we could use @ and @( ) and not have to use the last free symbol we have :)

gavinking commented 11 years ago

The two approaches are quite different. I don't see how they mix, except confusingly.

Sent from my iPhone

On 15/05/2013, at 1:22 PM, Tako Schotanus notifications@github.com wrote:

Well couldn't you still do the following?

@<String|Map.Entry> Or combine the suggestions?

@String|Map.Entry That way you keep the short version for many cases and only use the grouping when necessary. I suggested the < > only because it's already used with type grouping, but of course in this case it's not only used with types (as seen with Map.Entry.key), so maybe we could use @ and @( ) and not have to use the last free symbol we have :)

— Reply to this email directly or view it on GitHub.

quintesse commented 11 years ago

I mean the approach of using backticks for meta model references of your suggestion with the optional grouping of my suggestion. But I don't care about the character used for grouping, I'm just suggesting it could be optional. In my idea the @ remains the character for access to the meta model.

gavinking commented 11 years ago

I think you're missing the point.

Would you let people write both:

Map@Entry
Map.Entry@key

And

@<Map.Entry>
@<Map.Entry.key>

That be very confusing, right?

gavinking commented 11 years ago

Anyway, it seems to me that best combination of readability and power comes from using backticks. Then we wind up with different syntax for metamodel references and function references:

Person

is an expression of type Person(String).

`Person`

is an expression of type Class<Person,[String]>.

Person.name

is an expression of type String(Person).

`Person.name`

is an expression of type Member<Person,Value<String>>.

Person.say

is an expression of type Void(String)(Person).

`Person.say`

is an expression of type Member<Person,Function<Void,[String]>>.

The grammar for a metamodel reference would be:

"`" Type | (QualifiedType|GroupedType) "." MemberName TypeArguments? "`"

Which works out very nicely because it let's us write things as interesting as:

`<Integer|Float>.negativeValue`

Best of all, we don't need any kind of "reference" types or "metatype constructors".

gavinking commented 11 years ago

Oh I almost forgot there is also the question of how to get a reference to a value. For toplevels/locals, the following works:

`process`

which is of type Value<Process>.

But what about for attributes? I suppose the following is workable:

`process.arguments`

which would be of type Value<String[]>. But I preferred process@arguments which is what would be natural with the infix @ syntax.

The issue is that the infix @ lets me write shit like (1+1)@negativeValue whereas that sort of thing would probably not work with the backtick-quoted syntax.

gavinking commented 11 years ago

I suppose this is an option:

process.`arguments`

But, well, now it's getting a bit messy...

quintesse commented 11 years ago

I still think @< > is an option, as long as you only use it for grouping of types, so you can use it for @<Integer|Float> and @<String|Map.Entry> but you can simply use process@arguments (although if you want you could write process@<arguments> which would mean the same but is unnecessary). It's not that confusing, is it?

FroMage commented 11 years ago

Person.name is an expression of type String(Person).

Mmmm, how do that work out for variables then?

gavinking commented 11 years ago

WDYM?

Sent from my iPhone

On 15/05/2013, at 3:32 PM, Stéphane Épardaud notifications@github.com wrote:

Person.name is an expression of type String(Person).

Mmmm, how do that work out for variables then?

— Reply to this email directly or view it on GitHub.

FroMage commented 11 years ago

Well, references would only be read-only. We used to have the notion of Gettable/Settable at some point.

gavinking commented 11 years ago

Well sure, we still have Value and MutableValue.

Sent from my iPhone

On 15/05/2013, at 3:39 PM, Stéphane Épardaud notifications@github.com wrote:

Well, references would only be read-only. We used to have the notion of Gettable/Settable at some point.

— Reply to this email directly or view it on GitHub.

FroMage commented 11 years ago

Oh, I suppose I meant person.name rather than Person.name.

BTW, whatever the syntax, will we be able to do List<Person>@method<Foo>?

gavinking commented 11 years ago

Oh, I suppose I meant person.name rather than Person.name.

According to this comment you could write:

MutableValue<String> name = `person.name`;

BTW, whatever the syntax, will we be able to do List<Person>@method<Foo>?

According to the grammar above, you would be able to write:

Member<List<Person>,Function<Foo,[Foo]>> method = `List<Person>.method<Foo>`;
FroMage commented 11 years ago

Cool.

FroMage commented 11 years ago

Is a keyword model(List<Person>) out of the question?

Will we get package and module literals too?

gavinking commented 11 years ago

Well, there's 2 issues with that:

  1. The keyword doesn't really help a whole lot because kw operators need to have a low precedence, and so it would almost always be a keyword + punctuation, as you've shown. What I mean is, the keyword can't do anything @ can't do, and it's natural precedence is less convenient. I guess you can argue that model(some.stuff) (or more likely (model some.stuff)) is easier on the eyes than @(some.stuff), but I don't find it an improvement over backticks.
  2. This works out to be rather nasty-looking in annotation lists see (model(Foo), model(Bar)) doesn't read nicely at all.
gavinking commented 11 years ago

Will we get package and module literals too?

If we can decide what the syntax would be. Unfortunately we can't just use this:

`ceylon.language`

We're going to need something weird like, perhaps:

`module ceylon.language`
FroMage commented 11 years ago

Unfortunately we can't just use ceylon.language

Why not?

Perhaps ceylon.language is a metamodel ref to the package ceylon.language and ceylon.language/0.5 to the module?

Ideally I'd also have a metamodel ref to the current module and current package, but we don't have anything like that for the current type. Though in the case of the current type, we have a way to access it via model(this). Well, perhaps module.this and package.this would work? I suppose it goes against the outer keyword, but that one already has an issue with not being able to obtain the outer's outer so perhaps that keyword is flawed too.

FroMage commented 11 years ago

I've just pushed support for type literals (not type members yet). Aw fuck this issue is closed and not even about type literals. Where's the issue about type literals then?