Skip to content
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

Sugaring #19

Closed
lucaswerkmeister opened this issue May 27, 2014 · 6 comments
Closed

Sugaring #19

lucaswerkmeister opened this issue May 27, 2014 · 6 comments

Comments

@lucaswerkmeister
Copy link
Member

(Moving the discussion here from #1.)

Being able to pass

  • a String instead of an Identifier
  • an Integer instead of a NaturalLiteral
  • etc.

is useful.

However, I don’t think there’s a good way to accommodate the sugar in the class. Since the type of the original attribute is invariant, we can’t change Identifier to Identifier|String. We could declare some sort of Identifier|String identifier_ or sugarIdentifier or something like that, but that doesn’t really appeal to me. Instead, I would rather create functions with the same name, like this:

// Identifier.ceylon
shared alias IdentifierIsh => String|Identifier;

shared class Identifier(text) { ... }

shared Identifier identifier(IdentifierIsh text) extends ... {
    if (is Identifier text) {
        return text;
    }
    // let’s assume we have ceylon/ceylon-spec#536 and text is String here
    return Identifier(text);
}

// Variable.ceylon
import ceylon.ast { parseIdentifier=identifier } // avoid naming conflict with parameter

shared class Variable(..., identifier, ...) extends ... { ... }

shared Variable \ivariable(..., IdentifierIsh identifier, ...)
        => Variable(..., parseIdentifier(identifier), ...);

Or maybe I’m wrong, and there is some syntax that allows something like this?

shared class Variable(..., IdentifierIsh identifier, ...) {
    // ...
    super.identifier {
        if (is Identifier identifier) {
            return identifier;
        }
        return Identifier(identifier);
    }
    // ...
}

(Constructors – ceylon/ceylon-spec#796 – might also be a solution to that problem, but I don’t think there’s a consensus on these yet.)

Later:

I just noticed that my proposed solution for the “sugar” part is quite inconsistent: the identifier function turns an IdentifierIsh into an Identifier, but the \ivariable function takes a whole bunch of arguments (instead of a VariableIsh) and turns them into a Variable. That’s actually two different things – but I can’t think of an example where they would collide.

And:

It’s a lot of work, and the gain isn’t clear – the sugared variants are easier to type, but do they increase or decrease readability?

@lucaswerkmeister
Copy link
Member Author

It’s a lot of work, and the gain isn’t clear – the sugared variants are easier to type, but do they increase or decrease readability?

I now think that they can definitely increase readability, because we now have wrapping nodes for every collection of nodes. This is useful for consuming and operating on ASTs, but horrible for readability: compare

classDefinition {
    name = "MyClass";
    satisfiedTypes = { baseType("Obtainable"), iterableType("Element") };
    typeParameters = { "Element" };

    hashDefinition,
    equalsDefinition
}

with

ClassDefinition {
    name = UIdentifier("MyClass");
    satisfiedTypes = SatisfiedTypes([baseType("Obtainable"), iterableType("Element")]);
    typeParameters = TypeParameters([typeParameter("Element")]);
    body = ClassBody([
            hashDefinition,
            equalsDefinition
        );
}

and keep in mind that the latter still has lots of sugar! This is just making the point about allowing {TypeParameterIsh*} instead of TypeParameters.

@lucaswerkmeister
Copy link
Member Author

I just noticed that my proposed solution for the “sugar” part is quite inconsistent: the identifier function turns an IdentifierIsh into an Identifier, but the \ivariable function takes a whole bunch of arguments (instead of a VariableIsh) and turns them into a Variable. That’s actually two different things – but I can’t think of an example where they would collide.

I think conceptually we can justify this by saying that we have something like

alias ClassDefinitionIsh => IdentifierIsh, ParametersIsh, BodyIsh, ...;

which would mean something like a “spread tuple type”. Then it’s reasonable that the classDefinition function has multiple parameters. (Of course, for these kinds of nodes, we wouldn’t define the alias at all.)

@lucaswerkmeister
Copy link
Member Author

I wonder if perhaps we should have all the utilities for creating ASTs in a separate module…

@lucaswerkmeister
Copy link
Member Author

Moving it into a separate module would also allow me to release it independently of ceylon.ast.core, gaining me some time :D

@lucaswerkmeister
Copy link
Member Author

So what do we call that module?

  • util? Too general.
  • build? Sounds like the ceylon.build script for ceylon.ast.
  • make? Same problem with Makefile.
  • construct? Too long.
  • create? Also feels too long.
  • sugar? Likely to confuse people.

My current favorite, if nothing better turns up, is build.

@lucaswerkmeister
Copy link
Member Author

I’m leaning more towards create now. Sure, it’s a tiny bit longer, but it’s not like you have to spell out the name manually thousands of times, so I don’t think it’s that big of a deal. It also seems to make the most sense.

lucaswerkmeister added a commit that referenced this issue Oct 6, 2014
Two new modules: ceylon.ast.create and test.ceylon.ast.create. All
existing “sugaring” moves to ceylon.ast.create, and the tests (xFunction
in test.ceylon.ast.core) move to test.ceylon.ast.create.
lucaswerkmeister added a commit that referenced this issue Oct 6, 2014
Two new modules: ceylon.ast.create and test.ceylon.ast.create. All
existing “sugaring” moves to ceylon.ast.create, and the tests (xFunction
in test.ceylon.ast.core) move to test.ceylon.ast.create.

For #19.
lucaswerkmeister added a commit that referenced this issue Oct 7, 2014
lucaswerkmeister added a commit that referenced this issue Oct 7, 2014
(And ClassInstantiation, but I might unshare that, it’s really just
auxiliary for the other two – I don’t see why you’d want to use it
directly.)

For #19.
lucaswerkmeister added a commit that referenced this issue Oct 7, 2014
@lucaswerkmeister lucaswerkmeister added this to the Initial release milestone Oct 11, 2014
@lucaswerkmeister lucaswerkmeister self-assigned this Oct 11, 2014
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant