Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Different default values for different undefined levels #16

Closed
xareelee opened this issue Apr 8, 2017 · 2 comments
Closed

Different default values for different undefined levels #16

xareelee opened this issue Apr 8, 2017 · 2 comments

Comments

@xareelee
Copy link

xareelee commented Apr 8, 2017

Please see the discussion in facebook/idx#5 for the reason about why we need different default values for different undefined levels.

In this example:

const result = props.user.friends[0].friends

We have 5 chances to get undefined:

props == undefined
props.user == undefined 
props.user.friends == undefined 
props.user.friends[0] == undefined 
props.user.friends[0].friends == undefined

We need some symbol to define the default values for different undefined levels.

  • ?(...) will provide a default value if the evaluation returns undefined
  • ?> will try to look up a default value in the optional chain
  • ?(...)! will provide a default value only for this evaluation (it will not be looked up for ?>)
  • ? will not try use a default value

For example:

const defaultValue = []
const result = props?(defaultValue).user?>.friends?>[0]?>.friends?>
                     ^^^^^^^^^^^^^^Provide the default values and it can be looked up

We provide a defaultValue to props if it is undefined. If any following evaluation returns undefined and it use ?>, it will look up the default value in the chain.

props == null
  ? defaultValue  // use `?(defaultValue)`
  : props.user == null 
    ? defaultValue  // use `?>` to look up the default value
    : props.user.friends == null 
      ? defaultValue  // use `?>` to look up the default value
      : props.user.friends[0] == null 
        ? defaultValue  // use `?>` to look up the default value
        : props.user.friends[0].friends
          ? defaultValue  // use `?>` to look up the default value
          : props.user.friends[0].friends  // final result

If we use ?(...)!, for example:

const result = props?(defaultValue)!.user?>.friends?>[0]?>.friends?>
                     ^^^^^^^^^^^^^^^Provide the default values and it can NOT be looked up

the defaultValue can't be used in other optional levels (only for its level):

props == null
  ? defaultValue  // only for this level
  : props.user == null 
    ? undefined
    : props.user.friends == null 
      ? undefined
      : props.user.friends[0] == null 
        ? undefined
        : props.user.friends[0].friends
          ? undefined
          : props.user.friends[0].friends

We could provide different default values for the chain:

const defaultFriends = [{ name: "Pavlos"}]
const result = props?(defaultValue).user?>.friends?>[0]?(defaultFriends).friends?>
props == null
  ? defaultValue
  : props.user == null 
    ? defaultValue
    : props.user.friends == null 
      ? defaultValue
      : props.user.friends[0] == null 
        ? defaultFriends // provide a new default value for this level
        : props.user.friends[0].friends
          ? defaultFriends // look up for the default value
          : props.user.friends[0].friends

We could use just ? to not use a default value

// Only using `?>` will try to look up the default value
const result = props?(defaultValue).user?.friends?[0]?.friends?>
props == null
  ? defaultValue
  : props.user == null 
    ? undefined  // will not look up
    : props.user.friends == null 
      ? undefined  // will not look up
      : props.user.friends[0] == null 
        ? undefined  // will not look up
        : props.user.friends[0].friends
          ? defaultValue
          : props.user.friends[0].friends

How about this proposal?

@claudepache
Copy link
Owner

That seems quite complex for me. The goal of this proposal is to have simple, easy to understand semantics. The only real complexity is the short-circuiting behaviour.

But let see how to handle your use cases, using the current ?. operator plus the null-coalescing ?? operator (which I think should be included, see #10):

const defaultValue = []
const result = props?(defaultValue).user?>.friends?>[0]?>.friends?>
                     ^^^^^^^^^^^^^^Provide the default values and it can be looked up
const result = props?.user?.friends?.[0]?.friends ?? defaultValue
const result = props?(defaultValue)!.user?>.friends?>[0]?>.friends?>
                     ^^^^^^^^^^^^^^^Provide the default values and it can NOT be looked up

For this one, the simplest replacement I can think of is:

let tmp = props
const result = tmp == null ? defaultValue : tmp?.user?.friends?.[0]?.friends

I think that we should stick if possible with the “left-to-right evaluation order plus short-circuiting” principle if possible. For example, let say that ! means “stop the evaluation of this expression” (short-circuit); one could write:

const result = (props ?? defaultValue!).user?.friends?.[0]?.friends
// Only using `?>` will try to look up the default value
const result = props?(defaultValue).user?.friends?[0]?.friends?>

This one is more cumbersome; I wonder however whether that situation happens often.

const result = (() => {
    let tmp = props
    if (tmp == null)
        return defaultValue
    let tmp = tmp?.user?.friends?.[0]
    if (tmp == null)
       return undefined
    return tmp?.friends
    if (tmp == null)
        return defaultValue
    return tmp
})()

With the ! introduced above, it could be written as:

const result = ((props ?? defaultValue!).user?.friends?.[0] ?? undefined!)?.friends ?? defaultValue

But it is not clear to me whether your use cases occurs sufficiently often in real life to balances the burden to learn yet another syntax. Do other languages have solutions for your use cases?

@claudepache
Copy link
Owner

Thanks to your suggestion. Because I want to restrict the scope of my work, I won’t include it. More specifically:

Of course, further improvements may be considered later if there are real needs, although I don’t expect to personally pursue the work in that direction. If you have any suggestion, you should make your case on the es-discuss mailing list.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants