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

nullable syntax update #1023

Closed
andrewrk opened this issue May 26, 2018 · 20 comments · Fixed by #1096
Closed

nullable syntax update #1023

andrewrk opened this issue May 26, 2018 · 20 comments · Fixed by #1096
Labels
accepted This proposal is planned. proposal This issue suggests modifications. If it also has the "accepted" label then it is planned.
Milestone

Comments

@andrewrk
Copy link
Member

  • Replace a ?? b binary operator with a ifnull b. After this change, all control flow occurs with keywords only.
    • other suggestions instead of ifnull are welcome. ifnull is nice because it is 2 lowercase words squished together so nobody will miss the identifier name.
  • Remove ??x prefix operator. Replace it with x.?. After this change and Pointer Reform #770, this symmetry exists:
    • *T is used to make a pointer type, and x.* derefs
    • ?T is used to make a nullable type, and x.? de-nullifies
    • Both operations have assertions that are potentially unsafe - .* asserts the pointer is valid among other things and .? asserts the value is non-null.

Anecdotally, this will look a lot better syntactically, especially with C-translated code.

@andrewrk andrewrk added proposal This issue suggests modifications. If it also has the "accepted" label then it is planned. accepted This proposal is planned. labels May 26, 2018
@andrewrk andrewrk added this to the 0.3.0 milestone May 26, 2018
@phase
Copy link
Contributor

phase commented May 26, 2018

Will there be anything for null function call chaining like Kotlin’s ?.

ifnull is rather verbose, Kotlin uses ?: which is short and simple.

@thejoshwolfe
Copy link
Contributor

Will there be anything for null function call chaining like Kotlin’s ?.

Feel free to propose something in a separate issue. I don't remember if this one has been proposed so far.

@BraedonWooding
Copy link
Contributor

I really like the x.? change but I'm not so sure about a ifnull b, while I understand the idea of removing keywords from control flow I'm worried that one it reads a little odd, since it does read like do a if b is null, and two it adds a bit of confusion as it is a two word keyword which is generally not done since it isn't as readable (like in this case it is quite verbose) and I'm worried that people will think the whole ifx pattern will be a consistent thing such as elsenull, ifnotnull (etc).

Perhaps something simpler like a.? else b, or like; a.? : b in similarity to the common ternary operator a != null ? a : b. But the second option I guess introduces another symbol which is kind of what is trying to be eliminated here.

I'm not particularly against it but I'm not all for it.

@PavelVozenilek
Copy link

?? is very easy to type and it stands out. if? or if?? could be used if keyword is that needed.

@ghost
Copy link

ghost commented May 27, 2018

What about reusing or? It's short, and a ?? b is pretty similar to a or b:

  • a or b when a and b are bools means "evaluate a and return it if it's true, otherwise evaluate and return b".
  • a ?? b when a is ?T and b is T or ?T means "evaluate a and return it if it's non-null, otherwise evaluate and return b".

@phase
Copy link
Contributor

phase commented May 27, 2018

I like @hcff’s idea but nullable booleans might get confusing.

@tgschultz
Copy link
Contributor

tgschultz commented May 29, 2018

Should x.! then unwrap an error union, equivalent to x catch unreachable?

Also not a huge fan of ifnull. To throw some options out there and maybe inspire some better ones:

a fail b;
a handle b;
a provide b;
a fill b;
a check b;
a coalesce b;
a alt b;
a otherwise b;

@eira-fransham
Copy link

My personal favourite would be a.? else b. It doesn't introduce a new keyword and it's (IMO) very readable. If a.! was introduced then the question is whether a catch b should be a.! else b (removing a keyword is always good when possible in my opinion).

@raulgrell
Copy link
Contributor

Would there be an issue with using a straight a else b?

@thejoshwolfe
Copy link
Contributor

Would there be an issue with using a straight a else b?

yes. it would cause ambiguity in if (a) b else c else d. we can't use a else b for null coalescing.

@raulgrell
Copy link
Contributor

@thejoshwolfe - Damn, I should have seen that.

One of the proposals in #760 mentioned the possibility of removing the parentheses around the condition of if and while. I'm not sure if this implied the use of curly brackets around the body of the conditional/loop would be required - if that's the case, would that be enough to resolve the ambiguity or does another one pop up?

Your example would have to be expressed as either

if a { b } else { c else d }

or

if a { b else c } else { d }

@akavel
Copy link

akavel commented Jun 7, 2018

Another bikeshed proposals (sorry if something conflicts with existing keywords/...):

a ornull b
a fallback b
a default b
a withdefault b

@wtetzner
Copy link

wtetzner commented Jun 7, 2018

If using something like else is a problem because it's used elsewhere, why not:

a orelse b

andrewrk added a commit that referenced this issue Jun 10, 2018
andrewrk added a commit that referenced this issue Jun 10, 2018
See #1023

This also renames Nullable/Maybe to Optional
@andrewrk
Copy link
Member Author

Should x.! then unwrap an error union, equivalent to x catch unreachable?

That would indeed follow the pattern. However, I removed the error
unwrapping syntax in 3c09411 and
I think it has been a positive change, having try be substantially
easier to write than catch unreachable.

I think I would advocate for removing x.? before advocating for
adding x.!. But I'm not suggesting either of these things at this
time.

I'm choosing orelse for these reasons:

  • 2 small words squished together so it does not clobber userspace
    identifier names
  • someone who doesn't know zig could get the gist of it when they
    see it used; it's readable and unsurprising
  • it's reasonable to type on both qwerty and dvorak
  • it's better than status quo, and, like comptime, even if it seems
    awkward at first, once you see it colored in your editor and
    use it a few times, it starts to look normal.

andrewrk added a commit that referenced this issue Jun 10, 2018
See #1023

This also renames Nullable/Maybe to Optional
andrewrk added a commit that referenced this issue Jun 10, 2018
use the `zig-fmt-optional-default` branch to have zig fmt
automatically do the changes.

closes #1023
andrewrk added a commit that referenced this issue Jun 10, 2018
use the `zig-fmt-optional-default` branch to have zig fmt
automatically do the changes.

closes #1023
@eira-fransham
Copy link

Does this also remove prefix ??, replacing it with orelse unreachable?

@tiehuis
Copy link
Member

tiehuis commented Jun 11, 2018

prefix ?? becomes postfix .?. ?? as an infix operator for a default is replaced by orelse.

@E3V3A
Copy link

E3V3A commented Oct 30, 2018

I'm choosing orelse for these reasons:

  • someone who doesn't know zig could get the gist of it when they
    see it used; it's readable and unsurprising

I've never heard about zig until a few minutes ago, and I already like it! 👍

@avrittrohwer
Copy link

I apologize if this is the wrong place to leave this comment, I've searched other issues and haven't found anywhere else more appropriate.

Should orelse allow the left side to be a non-nullable value? For example:

var a: i32 = undefined;
var nullable: ?i32 = null;
var non_nullable: i32 = 20;

a = nullable orelse 1; // works
a = non_nullable orelse 1; // doesn't work

This example results in the following compile error:

$ zig build-exe hello.zig
/path/to/this/file/hello.zig:9:9: error: expected optional type, found 'i32'
    a = non_nullable orelse 1; // doesn't work
        ^

Although perhaps useless, it's still a safe operation.

@hryx
Copy link
Contributor

hryx commented Apr 5, 2019

@avrittrohwer This restriction is surely by design. Zig generally takes a rigid standpoint on syntax. There may be other reasons, but at the very least this is for the sake of some of its mottos: "Only one obvious way to do things" and "Communicate intent precisely". In this case, orelse should only be valid when providing a fallback value an optional, as a non-optional does not need a fallback value.

Just speaking for myself, I've come to love that property — the compiler guides me into making the cleanest and most correct code possible.

@deflock
Copy link

deflock commented Mar 23, 2021

@andrewrk

someone who doesn't know zig could get the gist of it when they see it used; it's readable and unsurprising

For me orelse was the most confusing keyword when I saw it first time. Orelse why? Null? Error? Falsey? And after reading some docs try/catch/bool/optional/orelse/return/unreachable still confuse a bit. Maybe it's a question of some practice. Just my 2c.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
accepted This proposal is planned. proposal This issue suggests modifications. If it also has the "accepted" label then it is planned.
Projects
None yet
Development

Successfully merging a pull request may close this issue.