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

Additional syntax for safe navigation #114

Open
EliteMasterEric opened this issue Jan 7, 2024 · 8 comments
Open

Additional syntax for safe navigation #114

EliteMasterEric opened this issue Jan 7, 2024 · 8 comments

Comments

@EliteMasterEric
Copy link

I wanted to make a new home for a topic of discussion that is somewhat dispersed right now.

Safe navigation was implemented for object field access in 4.3.0, however it was not implemented for array access or function calls, despite being included in the original proposal #89 .

Array Access

The original proposal dictated that accessing nullArray?.[0] would return null, as opposed to nullArray[0] which currently throws a TypeError.

This syntax is notably useful in cases where you are working with nested arrays, like nullArray?.[i]?.[j]?.[k]. The fallback behavior is also very clear.

The syntax of ?.[] is used over ?[] since the latter is indistinguishable from a ternary operator by the parser. This syntax would notably be shared with Javascript and no other language.

Function Calls

The original proposal dictated that accessing nullFunction?.() would return null, as opposed to nullFunction() which currently throws a TypeError.

This syntax is notably useful primarily in the case where you are performing multiple object chaining operations in one line, such as object1?.object2?.myFunction?.(). It also may be used in the case where you use an optional callback function.

The main reasoning behind these not being implemented with the original proposal, as stated here: https://www.youtube.com/watch?v=lkpoTcHKjSE&t=454s

We didn't add the other variants, you sometimes see ?[ or ?( to make calls. We weren't sure if this was really needed, to me it looks kind of... it looks a bit awkward, and there might be use cases for it, but for now we decided to just stick to this one.

@EliteMasterEric
Copy link
Author

Some more niche concerns:

Null-only assignment

While I was reading through the Python proposal linked in #89, I found an additional piece of syntax they specify. The ??= syntax evaluates the left-hand side, determines if its value is null, and only performs an assignment if it is.

// Before
if (value == null) value = 4;
// Note this line is not technically equivalent, as it calls set_value if value is a property.
value = value ?? 4;
// After
value ??= 4;

I personally find this syntax to pretty redundant, as unlike with Python, Haxe can represent the expression in a single line. Thus, it would only reduce the readability of code, at least for those who were not aware of the syntax. Wanted to bring it up just in case someone else thinks there's a worthy case to be made for it.

Null coalescing overloading

It was mentioned during the main implementation of safe navigation that overloads of the == operator are ignored when evaluating safe navigation. This made me realize that handling of operator overloads for safe navigation and null coalescing might not be implemented.

Thankfully, @:op(a?.b) is considered valid, however when attempting to assign @:op(a ?? b), the compiler outputs this message:

Null coalescing overloading is not supported

I think support for this should be considered in the future, since as stated, the == overload is not used when evaluating ??.

@RblSb
Copy link
Member

RblSb commented Jan 7, 2024

Currently object1?.object2?.myFunction() should work if myFunction is non-nullable in object2 (just a reminder). Nullable array access is a more common case, i guess.

??= is already implemented with null coalescing op, not sure if you suggested to remove it.

I think ?? overloading was not implemented because this operator is right-associative, so it can create problems if you don't know about it when overloading.

@Simn
Copy link
Member

Simn commented Jan 7, 2024

Note that when I called the syntax awkward, I was referring to the ?[] version. The ?.[] version looks even more ridiculous to me, the point where I prefer to not see it in the language unless there's a very convincing use-case. IMO if you're working with nested nullable arrays then you went wrong somewhere in your design/life.

@RblSb
Copy link
Member

RblSb commented Jan 7, 2024

For example, Dart also doesn't have ?(/?[ syntaxes and uses fn?.call() or arr?.get(0) / set() methods. This is already can be implemented for arrays in user static extensions, doesn't look much worse than ?.[ for me.

@Simn
Copy link
Member

Simn commented Jan 7, 2024

But fn?.call() means (fn != null) ? fn.call() : null.

@back2dos
Copy link
Member

back2dos commented Jan 7, 2024

For deeply nested nullable arrays I would suggest creating an abstract over Null<Array<T>> and defining null safe array access therein.

@EliteMasterEric
Copy link
Author

For deeply nested nullable arrays I would suggest creating an abstract over Null<Array<T>> and defining null safe array access therein.

Does this work on, say, arrays provided by JSON?

@back2dos
Copy link
Member

back2dos commented Jan 8, 2024

Sure. Abstracts are a compile time only feature.

Here is an example:

class Test {
  static function main() {
    var x:{?board:NullableArray<NullableArray<Int>>} = haxe.Json.parse("{}");
    trace(x?.board[1][2] == null);// true
  }
}

abstract NullableArray<T>(Array<T>) {
  #if (js && js_es == 6)
  @:op([]) inline function get(i:Int):Null<T>
    return js.Syntax.code('{0}?.[{1}]', this, i);  
  #else
  @:op([]) function get(i:Int):Null<T>
    return 
      if (this == null) null
      else this[i];
  #end
}

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

No branches or pull requests

4 participants