Proposal: result-forwarding ternary operator

Andrea Giammarchi andrea.giammarchi at gmail.com
Wed Sep 20 09:08:33 UTC 2017


gotta admit an `if (let y = fn())` would be a very nice feature to have.
Only the `for(...)` lets us declare block variables, the `let` expression
would solve/simplify this case and many others.

```js
const newFoo = (let x = getSomething()) ?
  (doSomethingFirst(), x.doSomething()) :
  doSomethingElse();
```

On Wed, Sep 20, 2017 at 10:41 AM, Isiah Meadows <isiahmeadows at gmail.com>
wrote:

> I'll just note that the only two languages I know of with a feature like
> this is Haskell with its `maybe fn orElse m`* function in `Data.Maybe` and
> Scala's `Option[T].mapOrElse(fn, orElse)`. Here's what many other languages
> do:
>
> Several languages use some form of `if let`, including Rust, Scala, and
> Swift:
>
> ```swift
> // In Swift
> let newFoo
> if let x = getSomething() {
>     doSomethingFirst()
>     newFoo = x.doSomething()
> } else {
>     newFoo = doSomethingElse()
> }
> ```
>
> Clojure offers the macro `(if-let)`, which does mostly the same thing
> (Common Lisp has a similar macro):
>
> ```clj
> ;; Top-level declaration
> (def new-foo
>   (if-let x (get-something)
>     (do
>       (do-something-first)
>       (do-something x))
>     :else (do-something-else)))
> ```
>
> OCaml uses pattern matching, and C/C++, most of its derivatives (like
> Python), and Kotlin just do `if (x != NULL) ... else ...` or similar.
> (Kotlin has flow-sensitive typing like TypeScript, which helps avoid
> mistakes.)
>
> * I might have gotten the argument order wrong - I'm not a regular Haskell
> user, and it's not commonly used.
>
> On Tue, Sep 19, 2017, 09:36 Michael Rosefield <rosyatrandom at gmail.com>
> wrote:
>
>> We still have to explicitly create a variable (x), either in the do block
>> or before that ternary, and the bracket-enclosed comma-separated
>> expressions are... not to my taste.
>>
>> This was always about syntactic sugar and concision, as there are always
>> other ways to go about it; as I commented in my reddit post, both operators
>> can be done functionally:
>>
>> const $equivFn = (cond, ifTruthy, otherwise) => cond ? ifTruthy(cond) : otherwise(),
>>       foo = $equivFn(getSomething(), x => doSomething(x), () => doSomething()),
>>       equivFoo = getSomething() ?! x => doSomething(x) : doSomethingElse();
>>
>> // normal ternary
>> const $ternary = (cond, ifTruthy, otherwise) => cond ? ifTruthy() : otherwise(),
>>       foo = $ternary(checkSomething(), () => doSomething(), () => doSomething()),
>>       equivFoo = checkSomething() ? doSomething() : doSomethingElse();
>>
>>
>> ... but it's not elegant.
>>
>> And I appreciate ?! was a bad choice, but can easily be substituted by
>> anything else.
>>
>> On Tue, 19 Sep 2017 at 14:06 Andrea Giammarchi <
>> andrea.giammarchi at gmail.com> wrote:
>>
>>> I don't think `do` is "much longer" than your last example, however, it
>>> can be shorter
>>>
>>> ```js
>>> const newFoo = do {
>>>   let x = getSomething();
>>>   x ?
>>>     (doSomethingFirst(), x.doSomething()) :
>>>     doSomethingElse();
>>> };
>>>
>>> On Tue, Sep 19, 2017 at 3:00 PM, Sebastian Malton <sebastian at malton.name
>>> > wrote:
>>>
>>>> I don't think that talking about the syntax is relevant now since it is
>>>> not important when talking about the reasonability of a suggestion. Saying
>>>> that the syntax could be `?|`
>>>>
>>>> The `do` is much longer than the example.
>>>>
>>>> I think that this a reasonable idea.
>>>>
>>>> *From:* isiahmeadows at gmail.com
>>>> *Sent:* September 19, 2017 8:57 AM
>>>> *To:* rosyatrandom at gmail.com; es-discuss at mozilla.org
>>>> *Subject:* Re: Proposal: result-forwarding ternary operator
>>>>
>>>> Few issues:
>>>>
>>>> 1. This is already technically valid code: `cond?!fn:orElse` is
>>>> equivalent to `cond ? !fn : orElse`
>>>> 2. Have you considered `do` expressions (stage 1 proposal)? They work a
>>>> lot like IIFEs, but allow easy definition of computed constants.
>>>> 3. Have you considered using in-condition assignment or just factoring
>>>> out the computed condition into a separate variable? Sometimes, a little
>>>> verbosity helps.
>>>>
>>>> Using `do` expressions, your second code sample would look like this:
>>>>
>>>> ```js
>>>> const newFoo = do {
>>>>     let x = getSomething();
>>>>     if (x) {
>>>>         doSomethingFirst();
>>>>         x.doSomething();
>>>>     } else {
>>>>         doSomethingElse();
>>>>     }
>>>> };
>>>> ```
>>>>
>>>> On Tue, Sep 19, 2017, 08:33 Michael Rosefield <rosyatrandom at gmail.com>
>>>> wrote:
>>>>
>>>>> (I've also put this on reddit
>>>>> <https://www.reddit.com/r/javascript/comments/7129tn/proposal_resultforwarding_ternary_operator/>,
>>>>> which I've copied this from. Hope the formatting doesn't go haywire...)
>>>>>
>>>>> First course of action for this proposal is, obviously, to
>>>>> come up with a better name for it....
>>>>>
>>>>>
>>>>> Motivation
>>>>>
>>>>> As with the 'optional chaining' proposal for tc39
>>>>> <https://github.com/tc39/proposal-optional-chaining>, this operator
>>>>> is a way to avoid excess and annoying code from safety-checking.
>>>>>
>>>>> The optional chaining proposal, above, follows a chain and
>>>>> short-circuits it upon acting on a null object, returning a safe '
>>>>> *undefined*' result; it can be thought of as an extended '*if*'
>>>>> sequence. It looks like this:
>>>>>
>>>>> // safeVal = result of someProp, or undefined if looking for props on null obj
>>>>> const safeVal = blah?.someMethod()?.someProp;
>>>>>
>>>>> This proposal provides for an '*else'* scenario, particularly in
>>>>> situations where chaining isn't appropriate, by forwarding the result of a
>>>>> truthy conditional check to a single-parameter function.
>>>>>
>>>>>
>>>>> Syntax
>>>>>
>>>>> *condition ?! fn : expr*
>>>>>
>>>>> Parameters
>>>>>
>>>>>    - condition: any condition, *identical to use in standard ternary*
>>>>>    - fn: function taking single parameter, which is the result of
>>>>>    evaluating *condition*
>>>>>    - expr: any expression, *identical to use in standard ternary*
>>>>>
>>>>>
>>>>> Usage Example
>>>>>
>>>>> // temporary variable
>>>>> const temp = getSomething(),
>>>>>       foo = temp ? doSomething(temp) : doSomethingElse();
>>>>>
>>>>> // repeated code, possible side-effects
>>>>> const foo2 = getSomething() ? doSomething(getSomething()) : doSomethingElse();
>>>>>
>>>>> // proposal, no chaining
>>>>> const newFoo = getSomething() ?! x => doSomething(x) : doSomethingElse();
>>>>>
>>>>> // proposal, chaining
>>>>> const newFoo = getSomething() ?!
>>>>>       x => {
>>>>>         doSomethingFirst();
>>>>>         return x.doSomething();
>>>>>       } :
>>>>>       doSomethingElse();
>>>>>
>>>>>
>>>>> Notes
>>>>>
>>>>>    -
>>>>>
>>>>>    The choice of '?!' is entirely arbitrary and not a core part of
>>>>>    the proposal.
>>>>>    -
>>>>>
>>>>>    The result of the conditional check is not passed on to the falsey
>>>>>    path, because it seems pointless to do so.
>>>>>
>>>>> _______________________________________________
>>>>> es-discuss mailing list
>>>>> es-discuss at mozilla.org
>>>>> https://mail.mozilla.org/listinfo/es-discuss
>>>>>
>>>>
>>>> _______________________________________________
>>>> es-discuss mailing list
>>>> es-discuss at mozilla.org
>>>> https://mail.mozilla.org/listinfo/es-discuss
>>>>
>>>>
>>> _______________________________________________
>>> es-discuss mailing list
>>> es-discuss at mozilla.org
>>> https://mail.mozilla.org/listinfo/es-discuss
>>>
>> _______________________________________________
>> es-discuss mailing list
>> es-discuss at mozilla.org
>> https://mail.mozilla.org/listinfo/es-discuss
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20170920/03a3ca2b/attachment-0001.html>


More information about the es-discuss mailing list