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

let #747

Closed
gavinking opened this issue Aug 14, 2013 · 31 comments
Closed

let #747

gavinking opened this issue Aug 14, 2013 · 31 comments

Comments

@gavinking
Copy link
Member

As Ceylon is gradually moving toward being more expression-oriented and less statement-oriented, let expressions are becoming more and more useful. There's been some explicit discussion of them in #377 and #744. I'm opening this issue because this is actually of course a separate, independently useful construct.

The syntax would be something like this:

let (dist = sqrt(x^2+y^2)) [x/dist,y/dist]

Of perhaps, in order to avoid growing a new keyword, like this:

given (dist = sqrt(x^2+y^2)) [x/dist,y/dist]

Note that this feature might let us simplify the syntax of if expressions in #503, since you could write stuff like this:

let (first = sequence.first) if (exists first) first+1;

I think we should make this one a priority for 1.1.

@gavinking
Copy link
Member Author

P.S. If we're considering introducing a new keyword, then that is a decision we should make now before 1.0 goes final!

@tombentley
Copy link
Member

that is a decision we should make now before 1.0 goes final!

It's got to be worth reviewing the other features we'd like for 1.1 in case there are other words we'd like to reserve before it's too late.

@gavinking
Copy link
Member Author

@tombentley Don't worry, I'm on top of that ;-)

@oehme
Copy link

oehme commented Oct 21, 2013

+1

With let I could refactor this:

Complex times(Complex other) => Complex(a*other.a - b*other.b, a*other.b + b*other.a);

To a more Wikipedia-looking definition:

Complex times(Complex other) => let(c = other.a, d = other.b) Complex(a*c - b*d, a*d + b*c);

Without making it a block and introducing two values, which would make it 4 lines of code.

Of course even better would be:

Complex times(Complex(c, d) other) => Complex(a*c - b*d, a*d + b*c);

@gavinking
Copy link
Member Author

So shall I reserve let? Speak up, please?

@RossTate
Copy link
Member

Given that given is already a keyword, I suggest first polling whether that makes sense to use here.

@quintesse
Copy link
Member

Although let is by far the more common keyword for these kind of situations I would be fine with given. On the other hand I don't think let is a name that gets used very often as a variable or method so it wouldn't necessarily be missed. If i would have to give precedence I'd probably go for given.

@oehme
Copy link

oehme commented Oct 22, 2013

I would be fine with "given", too.

@gavinking
Copy link
Member Author

I very weakly incline toward let, because:

  • it's at least slightly confusing to newcomers if the same keyword means two totally different things,
  • everybody knows what let means, and
  • I have never ever wanted to name a function or value "let".

But honestly I could go either way.

@lucaswerkmeister
Copy link
Member

I’m not sure I understand the purpose of this. Could you use let in comprehensions like this?

value something = {
    for (datum in data)
        let (result1 = op1(datum), result2 = op2(datum))
            [opX(result1), opY(result2), opZ(result1, result2)]
};

That would certainly be useful.

EDIT: or is that #377? I’ve skimmed through the conversation, but it seems you discussed a whole lot of other things there as well that’s mostly unrelated…

gavinking added a commit that referenced this issue Oct 26, 2014
@gavinking
Copy link
Member Author

I have implemented let in the branch named let. It still needs a spec, tests, and an implementation in the backends.

@FroMage
Copy link
Member

FroMage commented Oct 27, 2014

So what's the syntax? I suppose it's a let* which means each new binding sees the previous ones, so it's evaluated in order? As opposed to a standard Scheme let where all new bindings are evaluated in the enclosing scope only, and letrec where each new binding can see every new binding, including the ones after it.

@quintesse
Copy link
Member

Yes, this is the one that confuses me the most. What does this do anyway:

let (dist = sqrt(x^2+y^2)) [x/dist,y/dist]

It makes a tuple of two elements using the dist binding? It's just that all the examples given seem to be like this, so it almost seems that the [ ] is part of the syntax.

@gavinking
Copy link
Member Author

So what's the syntax?

let (x="hello", y="world") x + " " + y

I suppose it's a let* which means each new binding sees the previous ones, so it's evaluated in order?

Yes, of course.

It makes a tuple of two elements using the dist binding?

Of course.

@quintesse
Copy link
Member

Ok, so it's something like "let" "(" variable-assignment* ")" expression , where the parens are required even if you don't bind anything.
Edit: it's variable-assignment+ not variable-assignment*, I misinterpreted the parser code.

@FroMage
Copy link
Member

FroMage commented Oct 27, 2014

Why, is it allowed to have no bindings? What's the use-case?

@quintesse
Copy link
Member

Sorry @FroMage , my mistake, it's actually: "let" "(" variable-assignment+ ")" expression

@gavinking
Copy link
Member Author

Why, is it allowed to have no bindings?

Of course not.

@FroMage
Copy link
Member

FroMage commented Oct 27, 2014

OK, thanks.

@FroMage
Copy link
Member

FroMage commented Nov 13, 2014

Should I do this one for the JVM then?

@quintesse
Copy link
Member

Ok, with me, I'm not going very fast at the moment with my current issue :/

@tombentley
Copy link
Member

Go on then, I'll let you have this one.

@FroMage
Copy link
Member

FroMage commented Nov 13, 2014

Haha.

@FroMage
Copy link
Member

FroMage commented Nov 14, 2014

Done on the JVM in the let branch.

@chochos
Copy link
Member

chochos commented Nov 15, 2014

Done on JS in the let branch as well. But I wanted to add some tests along with the impl and it turns out that the declarations don't see each other... I thought each new declaration would see the previous one like @FroMage mentioned earlier: let (e="K"->1, k=e.key, v=e.item) [v,k] but k and v don't see e.

@gavinking
Copy link
Member Author

I suppose it's a let* which means each new binding sees the previous ones, so it's evaluated in order?

In fact this is not currently working.

@gavinking
Copy link
Member Author

@chochos Fixed, I think.

@chochos
Copy link
Member

chochos commented Nov 15, 2014

Yes, working now! This is why I think it's better to have some of these tests in the language module, even if they're not directly related to the language module itself; those tests are run on both backends.

@gavinking gavinking modified the milestones: 1.1.5, 1.5 Nov 24, 2014
@gavinking
Copy link
Member Author

Branched merged. Done!

@gavinking
Copy link
Member Author

Oh, wait, still need a spec!

@gavinking gavinking reopened this Nov 24, 2014
gavinking added a commit that referenced this issue Nov 25, 2014
@gavinking
Copy link
Member Author

Spec done, closing!

@gavinking gavinking self-assigned this Nov 25, 2014
gavinking added a commit that referenced this issue Nov 25, 2014
lucaswerkmeister added a commit to lucaswerkmeister/ceylon-spec that referenced this issue Feb 1, 2015
The spec used to define some operators via pseudocode let expressions
like this:

    let x=y in e

which, with the introduction of actual let expressions into the
language (ceylon#747), can instead be written as:

    let (x=y) e

rendering the previous syntax not only obsolete, but also confusing; see
for example @PhiLhoSoft on the ceylon-users mailing list:
https://groups.google.com/d/msg/ceylon-users/N3idC2CZQxI/N_-BaNdydzgJ
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

8 participants