Proposal: deprecate ternary

It does look less bad when the condition isn’t (and shouldn’t be) a single letter:

let count =
  1 + (if redactedA > 0 {1} : {0}) + (if redactedB > 0 {1} : {0})
<CallToActionLink
  href={link.href ++ (if queryString === "" {""} {"?" ++ queryString})}
  title={link.title}
/>

I feel that’s not too bad?

Definitely helps here… though we’re not gonna go back to that form. A different can of worm for other usage cases.

1 Like

Sure it’s not too bad, but I find it a bit harder to “humanly” parse on single line contexts like this.

1 Like

I agree here, I never use if in my code, switch is both more readable and more powerful to the point if is almost a code smell to me.

So I’d rather remove if and keep the ternary operator that I use for brevity.

1 Like

Eeeh we can keep the discussion going but we’re not removing if in this language… If we’re debating over familiarity then I don’t see how removing if helps at all.

1 Like

I’m not saying we should remove if. I’m just trying to make my case for why the ternary operator should stay (and should not be replaced by if).

On a scale of usefulness the order for me would be

  1. switch
  2. ternary
  3. if

In my relatively small ReScript project I have: 71 switch statements, 13 usages of the ternary operator, 8 if statements

There’s 3 ternary operators I could rewrite as switch with minimal improvement. There’s one where I would consider using an if with no else.

let onScrollEnd = React.useCallback3(
      () => hasPreviousPage ? setBefore(_ => startCursor) : (),
      (setBefore, startCursor, hasPreviousPage)
    );

Although I’m so used of reading the variable name with a question mark and seeing what happens that it’d probably slow me down more to have if (hasPreviousPage) { setBefore(..) } than it’d help me.

In the other cases I don’t think replacing the ternary would be a positive improvement.

Of the 8 if-statements, 1 should’ve been a switch-statement, 1 should’ve been incorporated into the arm of the switch it’s in. That leaves me with 6 that are in the form where the alternatives are worse, mostly in the form of.

some block of code doing stuff

if (someCondition) {
  perform extra work
}

continue
3 Likes

I wouldn’t mind deprecating ternary. I know for a fact some team mates definitely think otherwise… and I’m still arguing against point free etc every now and then.

Readability is far more important than brevity to me. Although ternary is commonly known i find it easier to read if .. else ... I use switch 99% of the time.
Having a formatter leading away from ternary would be a nice transition in my opinion.

3 Likes

And what would you replace it with? someCondition ? extraWork() : ()?

I wouldn’t replace it. I am arguing for keeping ternary, not arguing for removing if.

2 Likes

Popping back in here… For me, one-line with if/else syntax is a lot harder to parse at a glance than ternary, even with all the syntax/format adjustments proposed. (The only one-line if/else I’ve ever seen and liked is clojure but lisp syntax is not helpful here)

Since it’s more of a stylistic concern, is it too complex to have it be configurable whether the compiler allows ternary? If @chenglou commented on configurability, I missed it.

Given the opportunity, I would happily welcome a hard error on nested ternaries, but still would prefer the option to use shallow ternaries.

1 Like

+1 for keeping ternary.

Reasoning:

[1] If the ReScript philosophy is to provide a JS like syntax that is familiar for JS developers, then keeping the syntax seems consistent with that philosophy (and especially considering its widespread usage).

[2] When evangelising ReScript to JS teams, we will often face resistance to change and each small difference is one more barrier to overcome. So each factor that is the same as JS then it helps to combat “death by a thousand cuts”.

Thanks.

6 Likes

Understood

No we won’t be exposing any printer configuration; that’d be one step forward ten steps back. Lots of funny problems when there’s a config (hierarchical, monorepo, fs, editor, etc.).

For the printer, we try not to have heuristics that are overly opinionated. Hard erroring on an otherwise ok pattern would be such heuristics. We draw the line around nudging you by printing to another format, when we have to.

2 Likes

Makes sense. I haven’t messed much with bsconfig to see how it differs/compares to tsconfig, which allows some settings that are arguably stylistic

In my experience, JS developers tend to use the less verbose syntax especially with ecma6, forbid a lighter syntax even slightly lighter could be unappealing for a JS developer.

In general, I think the verbose solutions could be more readable at the beginning, but when you become more confident with a language/framework/other things you may want to switch to the lighter version to be more concise

1 Like

Interesting, I tend to use the more verbose variable names and language constructs the longer I worked in the language. Using if / else over the tertiary operator most of the time. Nice side-effect: our designer has an easier time reading the code.

For variables’ name, also I, prefer to be verbose and precise, but I simplify it when I have function inline for example:

myArrayWithNameWhoDefineExactlyMyItems.map(item => {
  ...
})

instead of:

myArrayWithNameWhoDefineExactlyMyItems.map(myArrayWithNameWhoDefineExactlyMyItem => {
  ...
})

after the variable’s name gives me the context I prefer to read fast what happens to the item instead of having 3-4 reminders of what kind of item is it in 10 lines of code for example.

For structures, I prefer the shorts version because the short version could hide a little the mechanism the first time you see it, but after few times you immediately associate the mechanism to the structure, and I think such structure highlight more the correlation between information.

For example, I think that a JSON vocabulary is way more readable for me than a switch in JS

Just an insight, in our codebase I realized we use a LOT of ternaries. So if there is not a lightweight alternative, this would make the code quite a bit lengthier and as a side-effect maybe harder to read?

Just some examples:

textColor={colors.dark ? "black" : "white"}
<FFText size={size > 60. ? 11. : 9.} color numberOfLines=1 align=#center>
AppUtils.ios13 ? 45. : 20.
~marginLeft=dp(i != 0 ? -35. : 0.)
<Box opacity={showUnderlay ? 0.7 : 1.}>
10 Likes

Definitely not. Functional programs are often terse - single letter variable names for example. f for a mapping function. And for programmers coming from other languages they might expect it.

  let min = (a, b) => lt(a, b) ? a : b

  let max = (a, b) => if lt(a, b) { b } else { a }

  let max = (a, b) =>
    if lt(a, b) {
      b
    } else {
      a
    }

Two important things come to mind for me here:

  1. there is a decent degree of subjectivity when it comes to ‘readability’
  2. people are gonna find ways to write bad code no matter what features you do/don’t include.

JS is one of the most overly unopinionated languages there are, so I would say that if ReScript wants to maintain appeal to that demographic then better to err on the unopinionated side also, especially when it comes to minor syntax details.

I personally generally find ternaries far more readable than if/else. I prefer them for almost anything that isn’t going to need a block anyway.

let abc = condition
    ? doSomething -> filter(fancyFilterFunction)
    : doSomethingElse -> filter(otherFilterFunction)

If there is any improvement on conditional syntax that I could suggest improving from my newcomer perspective it would definitely be to remove the mandatory braces in if/else statements. Even JS doesn’t make you do that. Part of the appeal of ternaries is lack of pointless noisy ugly braces.

let abc = if (condition)
    then doSomething -> filter(fancyFilterFunction)
    else doSomethingElse -> filter(otherFilterFunction)
6 Likes