Zig: nullable syntax update

Created on 26 May 2018  Â·  19Comments  Â·  Source: ziglang/zig

  • 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 #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.

accepted proposal

Most helpful comment

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.

All 19 comments

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

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

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.

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.

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

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".

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

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;

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).

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

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.

@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 }
```

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

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

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

a orelse b

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 3c094116aae459b651934663a31981cf09cdb3e4 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.

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

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

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! :+1:

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.

@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.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

andrewrk picture andrewrk  Â·  3Comments

bheads picture bheads  Â·  3Comments

andrewrk picture andrewrk  Â·  3Comments

daurnimator picture daurnimator  Â·  3Comments

jorangreef picture jorangreef  Â·  3Comments