-
Notifications
You must be signed in to change notification settings - Fork 62
let clause in comprehensions #3483
Comments
[@ikasiuk] Nice idea! I have also noticed in the past that something like that could be useful, and we recently had problems in that direction: |
[@RossTate] Snazzy! I think this in combination with the restriction I mentioned in that thread would be awesome. |
[@gavinking] There's actually a second bit of this proposal that @chochos forgot to write down. The idea has two parts:
The second bit would let you write:
Which fixes what tripped us up in the definition of |
[@chochos] Right, forgot about expression between subclauses. Well that's the whole idea right there. |
[@FroMage] First, I'd rather use the consecrated keyword for this and allow multiple vars to be defined:
With a semantics like
And I'd even make that available outside of comprehensions as an expression, and what the hell allow statements in it too:
Mmm, that wouldn't be too useful if we could define inline statements like that:
Which would be equivalent to:
But that brings me to something that's been bothering me about comprehensions for a while:
Now, I love comprehensions but I find it disconcerting and irregular that within them, the meanings of Also withing a comprehension, the procedural rules stop applying: no need for brackets anymore, or semicolons, what looks like statements are expressions and they return values and the absence of Don't take this as just me randomly bashing something: I love comprehensions and I think we should keep them and possibly extend them with local bindings, but I feel the syntax is too alien to the rest of Ceylon and I am afraid we're introducing a language-within-a-language syntax. That statement in particular looks entirely like a different language:
Would it make any sense in allowing Rewriting this statement with a more Ceylon-like syntax:
Which would even be valid when used as a statement:
It's just an idea, but I have a feeling that would be more powerful, intuitive and regular. What's the return type of an expresion block? Its last statement's return type, or that of its 'return' statements. Return type of a Wouldn't something like that work? |
[@gavinking] I'm fine with I also agree that if we introduce this feature in comprehensions, then
|
[@gavinking] > Now, I love comprehensions but I find it disconcerting and irregular that within them, the meanings of FTR, that's not actually how it works under the covers. The idea of mapping to
I spent a lot of time thinking about this, on several occasions, and just never came up with anything satisfying:
|
[@RossTate] > I think of them as actually very similar. For example, As for the curly braces, the sole purpose of curly braces is to disambiguate nesting. With comprehensions we have decided to optimize for what we expect to be the common case: everything nests. That is, everything after a I do find it weird that we get rid of semicolon after nested statements though. In fact, I'm a little worried that'll prevent us from using any syntax that relies on the presence of a semicolon in normal statements to disambiguate. |
[@gavinking] I agree with @RossTate—I don't think it's fair to say they have different semantics at all. |
[@quintesse] Hey, if we allow
why not go all Haskell-y and allow
as well? :) |
[@gavinking] So the tangential discussion on this thread fits very much with the tangential discussion on #3469. Ceylon today, like most mainstream languages, is "statement-oriented" and optimized for methods with multiple statements. Now I personally just love to write methods/getters with just single-expressions wherever that is reasonable, and for that reason I kinda like languages which are more "expression-oriented" in their syntax. Which is why we have stuff like comprehensions and Up until now, I have not been that keen on introducing something like:
because once you try to refactor out common sub-expressions, you need a big change to the syntax (sure, the IDE can do this for you, but still). So it's not a syntax that "scales". It's an abbreviation that winds up getting in the way when you start maintaining the code. On the other hand,
However, if we have both these features, then we have something that makes sense. It becomes possible to write the above in the following "expression-oriented" form:
So then we need to answer the question: is this an improvement over the "statement-oriented" form, and is having two ways to write the same thing a good or a bad thing in this case. I'm interested to know what you guys think. I personally find the definition with |
[@gavinking] @quintesse FTR, I actually prefer the keyword |
[@quintesse] Sure, I wasn't actually being serious, especially because in Haskell it's used to even define localized functions where it makes more sense to put them after the "important" work. PS I'm not sure how I feel about the short-cut syntax yet, but it sure makes me want the other Haskell syntax of being able to do:
hehe |
[@FroMage] > what the fuck should An iterator.
If punctuation for expressions is optional (like for curly braces, then surely for single braces too:
Perhaps you're right, but it's still a language-within a language. Again, perhaps that's not an issue at all, but I wonder if it really isn't, and if we can't do something to unify both. |
[@FroMage] I think the syntax you are pointing to depends on your background. For me it's C, so even in languages like Scheme or JavaScript where both alternatives are possible I find myself writing: function name(){
var n = person.name;
return n.first + " " + n.last;
} Rather than: var name = function(){
var n = person.name;
return n.first + " " + n.last;
} Same in Scheme with: (define (name)
(let ((n person.name))
(append n.first " " n.last))) rather than: (define name (lambda ()
(let ((n person.name))
(append n.first " " n.last)))) Personally I really have trouble making any sense of |
[@gavinking] > If punctuation for expressions is optional (like for curly braces, then surely for single braces too: Eh? I can't even hazard a guess what You're going to need some kind of punctuation to separate the condition from the first expression. So you have a choice between |
[@FroMage] > Eh? I can't even hazard a guess what Hah, yes of course, sorry: |
[@gavinking] @FroMage Again FTR, I have a rather extreme aversion to:
To the extent that I think it's almost unfortunate that Ceylon lets you write this. The fact that this even works is more of an unintended consequence—the intersection of two language features that aren't really designed for use together—than an intentional feature. I suppose that everyone here would agree that what we actually want people to write in this case is just:
So, indeed, this might be the strongest argument yet for supporting the form:
i.e. if we don't support that Dart/Coffeescript-style arrow syntax, then some people are going to want to use the "value = anon function" form like you quite often see them do in the Scala community. (Apparently in the world of Scala, this idiom is not frowned upon.) |
[@FroMage] And |
[@gavinking] @FroMage Right. I guess it's something I could implement in the parser in like 5-10 mins, though I might not do it exactly like that. Supporting |
[@ikasiuk] > So then we need to answer the question: is this an improvement over the "statement-oriented" form, and is having two ways to write the same thing a good or a bad thing in this case. I'm interested to know what you guys think. My opinion: it looks kind of nice, but not soo much nicer than the current syntax. And having two ways to write the same thing as a bad thing in this case. An important goal of Ceylon, according to the home page, is readability. If I interpret this goal correctly then it should lead us to a syntax that is as homogeneous, regular and easy to learn as possible. I think if we take that goal seriously then we can only introduce a new construct into the language if it solves a real, significant new problem. And I just don't see a sufficient benefit in this case.
So we have two ways of expressing the same thing but one of them is not so nice. So we introduce a third possibility to lure people away from the one we want to hide ;-) |
[@gavinking] @ikasiuk I agree with all your points, but note that there are a couple of different factors pushing in this direction:
I don't think that 2+3 on their own would be enough to motivate me to want to make this change. But if other considerations are "pushing" in the same direction—that is, if it just feels like the language naturally want to grow in that direction—well, then that's a different matter...
Can you think of a different syntax for partial application of a function / generic type? If we came up with something natural, then that might be a different approach that would give many of the same advantages. The trouble is, when I think about that problem, I start coming up with stuff like:
which I think is just not at all ceylonic (it's actually just Scala's horrible underscore in disguise), and is actually much less flexible than the fat arrow. |
[@gavinking] > I start coming up with stuff like:
Note that according to the language spec as it exists today, I should be able to write a general-purpose function named
i.e. Now, that's all very cool and powerful but I think it's exactly the kind of thing we don't want people to be tempted to use in "everyday" code. |
[@gavinking] > Hah, yes of course, sorry: This can't be parsed, consider stuff like:
We can't tell where the condition ends, at least not with finite lookahead in a CFG. |
[@ikasiuk] Two remarks: I prefer And one question (I probably just missed that): Why do we need to introduce a new symbol |
[@ikasiuk] > Note that
Ah ok, thanks. I agree that
|
[@ikasiuk] > No need to state That reminds me that the
always feel somewhat redundant. Could we say that an attribute that is initialized with |
[@chochos] I talked about that with @gavinking once. I think the idea is that if you must be sure that you want mutability, that's why you have to type that long keyword even if it seems redundant. That's the part about encouraging immutability - if we only leave |
[@ikasiuk] > I talked about that with @gavinking once. I think the idea is that if you must be sure that you want mutability, that's why you have to type that long keyword even if it seems redundant. That's the part about encouraging immutability - if we only leave I agree for |
[@ikasiuk] I'll try to explain the reasoning behind the proposed syntax. The goals are:
The named argument syntax plays an important role in this context. A named argument list is currently split into two parts in the following way:
The separation between the two parts is given only by the difference in the syntax of their respective elements. I must admit that I always found that a bit confusing, and it turns out that one way to reach the goals is actually to make the separation more explicit:
where
and consequentially
so that we can automatically write the desired
so that we can write:
Note that That's basically how I arrived at that syntax. @gavinking, you asked a question concerning sequence literals which I didn't quite understand. Does that clarify it a bit?
I guess any more explicit separation between the named argument part and the sequenced argument part of a named argument list could achieve the same effect. But neither do I have a better idea at the moment, nor am I conviced yet that this is really a problem. Can you give a code example of where you would find this awkward?
It's surely possible not to use commas and use semicolons instead, as with the current syntax. But it looks like I must make a confession: Since I've first seen it I've always found the named argument syntax in its current form the only part of the Ceylon syntax that feels somewhat awkward and unintuitive. The idea as such is absolutely great but I find it surprisingly hard to read and write Ceylon code that uses named arguments in a non-trivial way. The problem is that when I look at such a piece of code then the structure is not obvious: is this part a normal code block or a named argument list? What function does that line belong to? What is this function nested in? Why is this That makes me whish that parts of the code that serve a different purpose also look different and are clearly separated from each other - that the syntax helps me to intuitively understand the structure of the program. Of course that's only my personal impression and so perhaps it's just my fault that I don't "get it". |
[@gavinking] > It's surely possible not to use commas and use semicolons instead, as with the current syntax. But it looks like I must make a confession: Since I've first seen it I've always found the named argument syntax in its current form the only part of the Ceylon syntax that feels somewhat awkward and unintuitive. The idea as such is absolutely great but I find it surprisingly hard to read and write Ceylon code that uses named arguments in a non-trivial way.
Well, I certainly don't love the commas in the sequenced parameter list either. But I'm not sure that your solution really improves the situation very much. You swap:
For
It's like the same shit just inside a rectange, no? |
[@ikasiuk] Oh, I don't mind the commas, I just think the syntax could be a bit more consistent in this respect: that's why I suggested to simply use commas everywhere in argument lists. But we are wandering off the subject a bit. We were trying to make sequenced parameters and generalized comprehensions work together properly. And I think I finally found a good solution to that: Let's go back to the
This could be used with the following rules:
Thanks to the first rule it can always be decided at compile time whether the argument is a sequenced argument. It might even be a good idea to extend the first rule to "The static type of any argument With these rules the following examples all work as expected:
But An interesting possible extension is to apply the
This is always unambiguous because of the first rule and the definition of |
[@RossTate] Here's something that doesn't work with that:
It doesn't even type check according to your proposal. Note that this also doesn't work in any proposal saying that |
[@ikasiuk] > It doesn't even type check according to your proposal. You mean because of the constraint of the type parameter of
That depends on how the behavior of that |
[@RossTate] So say I'm a programmer who has heard about Ceylon and particularly that it has cool features like generics, first-class functions, and list comprehensions. |
[@ikasiuk] > [...] either I would immediately be faced with the subtleties of Ceylon's type system (i.e. "Why do I have to explicitly declare a type parameter is a subtype of Yes. But as I said: that's a separate problem which is not specific to this particular situation. It's something that can generally occur in Ceylon wherever you use generic types.
Not sure if I agree completely, but maybe you are right. The solution is obvious: the RHS type of the |
[@RossTate] Or make it so that type variables by default can only represent class types. |
@tombentley let me know when you're ready to start implementing |
[@chochos] Comprehensions are a really cool language feature and they would be even more useful if there was a way to declare and initialize values or variables that were internal to the comprehension. The keyword
given
can be used to enclose a declaration which can be used from that moment on:No need to state
value
orvariable
since it can be inferred from the declaration:=
means value,:=
means variable. Local type inference is already done.UPDATE: what we would really do, given all the evolution in the language since this was originally proposed, would be support the following:
[Migrated from ceylon/ceylon-spec#377]
The text was updated successfully, but these errors were encountered: