Proposal: result-forwarding ternary operator

Isiah Meadows isiahmeadows at gmail.com
Wed Sep 20 08:41:13 UTC 2017


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/4a16be7a/attachment-0001.html>


More information about the es-discuss mailing list