eclipse-archived / ceylon

The Ceylon compiler, language module, and command line tools
http://ceylon-lang.org
Apache License 2.0
395 stars 62 forks source link

Add let conditions #7420

Open lucaswerkmeister opened 5 years ago

lucaswerkmeister commented 5 years ago

Occasionally, it can be useful to introduce a new variable in a condition list.

if (is BaseMemberExpression bme = that.primary,
    let text = identifierText(bme.identifier),
    (importMemberAliases[text] else text) in inlineAnnotations) {
    // ...
}

It would be nice if that was legal syntax, instead of having to use workarounds like this:

if (is BaseMemberExpression bme = that.primary,
    exists text = identifierText(bme.identifier) of String?,
    (importMemberAliases[text] else text) in inlineAnnotations) {
    // ...
}

However, there is one problem: let conditions as shown above are very different from let statements, expressions, and (once implemented: see #3483) comprehension clauses – they only introduce one variable, and they aren’t parenthesized. To introduce multiple variables, you would use multiple let conditions instead of one let with multiple patterns. I’m not sure how much that bothers me.

gavinking commented 5 years ago

It seems to me that the natural syntax for this would be:

if (is BaseMemberExpression bme = that.primary,
    text = identifierText(bme.identifier),
    (importMemberAliases[text] else text) in inlineAnnotations) {
    // ...
}

The let keyword would be neither regular, nor necessary here.

lucaswerkmeister commented 5 years ago

Looks okay, but might be confusing/ambiguous if the expression happens to be boolean…

gavinking commented 5 years ago

True. The = would be mistaken for an assignment operator.

gavinking commented 5 years ago

OTOH, I'm pretty sure that assignments in ifs are something we already strongly discourage...

lucaswerkmeister commented 5 years ago

I briefly thought that value might be an alternative keyword to use instead of let, but then I realized that thinking this through most likely means full-featured declarations inside a condition list, either with inferred type (value) or with explicit type. And that starts to feel silly.

xkr47 commented 5 years ago

I guess what you are trying to achieve by

if (is BaseMemberExpression bme = that.primary,
    let text = identifierText(bme.identifier),
    (importMemberAliases[text] else text) in inlineAnnotations) {
    // ...
}

is either

  1. possibility to have a else statement that executes if any of the conditions fail - without having to duplicate the else as in

    if (is BaseMemberExpression bme = that.primary) {
    value text = identifierText(bme.identifier);
    if ((importMemberAliases[text] else text) in inlineAnnotations) {
        // ...
    } else {
        // ...
    }
    } else {
    // duplicate code
    }
  2. minimize indentation

I do have to say that 1. is something that personally annoys me every now and then. I wonder if there could be some construct to avoid duplicating/outsourcing the else blocks even if you have nested if statements with code in between.. Kindof like throwing an exception "blah this didn't work out, fallback behaviour please".. Hmm could it perhaps even be plausible to use the exception construct but not actually throw exception.. i.e. optimize the case when it is caught locally AND exception object never used.. :) Not that I like try blocks very much.. they mess with the otherwise beautiful indentation of code :)