ChanServ changed the topic of #zig to: zig programming language | ziglang.org | be excellent to each other | channel logs: https://irclog.whitequark.org/zig/
MajorLag2 has joined #zig
MajorLag1 has quit [Ping timeout: 240 seconds]
hio has quit [Quit: Connection closed for inactivity]
fjvallarino has quit [Remote host closed the connection]
fjvallarino has joined #zig
dfff has quit [Ping timeout: 252 seconds]
xtreak has joined #zig
xtreak has quit [Ping timeout: 256 seconds]
fjvallarino has quit [Remote host closed the connection]
fjvallarino has joined #zig
fjvallarino has quit [Ping timeout: 260 seconds]
isolier has quit [Read error: Connection reset by peer]
isolier has joined #zig
fjvallarino has joined #zig
darithorn has joined #zig
Ichorio has joined #zig
xtreak has joined #zig
donlzx has quit [Remote host closed the connection]
andi3 has joined #zig
<andi3>
I just read https://github.com/ziglang/zig/issues/1190 and while I do not find the code that hard to understand, I looked up null/ undefined in the docs and can't quite tell the difference. Undefined is used to have uninitialized variables and null is used in a context of optionals but why is not null used for uninitialized values as well? is there anything more to know about both types?
<andi3>
and is there a clear definition of where |value| can be used? I like that syntax in ruby a lot but in zig it seems a bit magic to me becauseI do not know yet how I could write code that exposes such functionality (accessing an element by |...|) by myself, hope that makes some sense?
<andrewrk>
andi3, null is used only with optionals (the ?T syntax)
<andrewrk>
undefined can be cast to any value, and it means the value could be anything, even something that is nonsense according to the type
<andrewrk>
you can check if something is null, but it doesn't make sense to check if something is undefined
<andrewrk>
undefined can be translated to english as, "not a meaningful value. if I try to use this value it would be a bug. I'll either not use the value, or overwrite it later"
<andrewrk>
I'll expand on this in the docs
<andrewrk>
thanks for speaking up :)
<andrewrk>
|value| syntax is not a generic tool that can be used to create a anonymous function, like in ruby. in zig the |x| syntax is valid only in some places and it has syntactic meaning
<andi3>
The docs on error values were nice btw, read them again yesterday and I think I understand most of it
<andi3>
so take `for (items) |value, i| {` if I have a custom data structure can I make it such that it can be used as items ?
<andi3>
and I do not quite understand the usage in such case (same goes for optional examples) `if (a) |value| {` why do you give a a new name? this feels redundant
<MajorLag1>
`a` is an array (or slice), `value` is the name you're assigning to the element you're iterating over in the loop body.
<MajorLag1>
oh wait, I read that wrong, you used `if` not `for`
<andi3>
yes
<MajorLag1>
but it is similar rationale: `a` is a nullable (optional) or error union. `a` is of type `?T` or `!T`. `value` is the name you're giving to whatever the optional or error union is wrapping for use inside the body of the if statement. `a` refers to the union, `value` to the unwrapped payload.
<andi3>
ok but a inside the if could just be the unwrapped value because if would just unwrap it because inside if it's guaranteed to hold a value
<MajorLag1>
`if(a) { a = null; }` would fail in your scenario, because the unwrapped value is not an optional type (it can't be null).
<MajorLag1>
Not that that is a useful example.
<andi3>
hm yes does not quite convince me why this |..| is useful for those if clauses with optionals TBH
<andi3>
can you maybe answer if I can implement this: "so take `for (items) |value, i| {` if I have a custom data structure can I make it such that it can be used as items ?" ?
<MajorLag1>
Only if it is an array or slice under the hood. See `std.ArrayList`. For only works on slices and arrays. The zig way of doing what you want is probably to use `while`: `while(iterator.next()) |val| { ... }`
noonien has quit [Quit: Connection closed for inactivity]
<andi3>
ok that while with iterator example is not bad but why nut just allow for as well in that case? the return value of next() can be either optional or error union I guess?
<andrewrk>
andi3, I'm not sure I understand your question
<andrewrk>
while and if support these types: bool, E!T, ?T
<MajorLag1>
Yes, though I think the error union case requires an else||{} block to be present too. There's been some debate about using for with iterators, let me see if I can dig up an issue.
<andrewrk>
for supports these types: [N]T, []T
<andrewrk>
that's right - in zig it is syntactically unambiguous which type is expected
<andrewrk>
if (x) {} // must be a bool
<andrewrk>
if (x) |_| {} // must be an optional
<andrewrk>
if (x) |x| {} else |_| {} // must be an error union
<andrewrk>
oops, 2nd x is a typo.
alexnask_ has quit [Ping timeout: 268 seconds]
<andi3>
thanks Andrew I think you understood the question because I understand your answer :D
<andi3>
anything about why if(a) |value| requires the unwrapped value to be assigned to a new name?
<andrewrk>
instead of working like kotlin? it's a design decision that makes it explicitly clear that the new name has a different type
<andrewrk>
there are several places where zig is consistent in this way
<andrewrk>
you can use: if (a) |*value|
<andrewrk>
this makes value a pointer to the non-optional part
xtreak has quit [Remote host closed the connection]
<andi3>
TBH I have not used kotlin yet although I somewhat wanted to but not had the need for it lately
<andi3>
you can use: if (a) |*value|, yes may I ask why I'd do that? I'd like to think that performance is not the reason because behind the scenes zig will not actually copy the value
<andrewrk>
the value in the | | is read-only
<andrewrk>
so if you want to modify `a`, you use the *value syntax and then you can modify a by writing through the pointer
<andrewrk>
you are correct that zig will not copy the value
<andrewrk>
zig does not have hidden references - you can't modify 2 values at once
<andrewrk>
that's not quite true anymore - passing by non-copying-value might be a hidden reference. but it's undefined behavior to observe this happening
<andi3>
ah ok that makes sense
<andi3>
"There's been some debate about using for with iterators," - that is a bit weird for me but as I already like go I can say that having less features is not bad in the 99% case, but it just feels weird that I can not write a data structure with the same ergonomics as builtins
<andrewrk>
I think the while (foo.next()) |item| {} syntax is good enough
<andrewrk>
if anything I would remove for syntax and add a standard library iterator for arrays/slices
<andi3>
can you squeeze in a counter like for (items) |val, I| that way as well? I missed that in for each loops in java/ cpp a lot and liked in ruby
<andrewrk>
the thing is, the counter in a for loop is exposing something we already need to have, where as adding the syntax for while would be bolting a counter on top of something unrelated
<andrewrk>
which you could just do in userland by making a variable
<andrewrk>
andi3, for loops in zig have the counter already
<andi3>
I think this is a place where zig would really need interfaces as a language feature to define such iterators for for loops e.g. and make them implementable by users at least this seems the obvious thing to do for me
<andrewrk>
var i: usize = 0; while (foo.next()) |item| : (i += 1) {} // here's your while loop counter
<andi3>
yes of course it's definitely possible already
fjvallarino has quit [Remote host closed the connection]
<andi3>
https://ziglang.org/documentation/master/#union and switch introduce the concept of e.g. `Foo.String => |*x| blk: {` -- I could not yet figure out what this blk: does, it looks like label/ goto to me but not sure, it's also present in the switch examples `101 => blk: { const c: u64 = 5; break :blk c * 2 + 1; },` is this some sort of goto label?
<andrewrk>
it's an undefined value, not undefined behavior. but zig makes branching on undefined value undefined behavior - that way zig is allowed to detect it and crash (and also optimize around it)
<andrewrk>
andi3, added to my docs todo list. That's labeled blocks and labeled break.
<andrewrk>
you can think of `break :block_name value` as `return value` except instead of returning from the function it returns from the block which has the `block_name` label
<andi3>
ok I do not really get it but looking forward to more docs on it :)
<GitHub141>
zig/master 291afcf Andrew Kelley: fix runtime libc detection depending on locale...
<MajorLag1>
andi3, it's a useful construct for assigning a value with logic. For instance, the following would make x an array of 10 u8s from 0 to 9: `const x = blk: { var a: [10]u8 = undefined; for(a) |*e, i| e.* = @intCast(u8, i); break :blk a; }`.
<MajorLag1>
which is a bad example because an array literal would be shorter in this case.
<andi3>
is blk actually a keyword? or is it just some name?
<MajorLag1>
it's a label, so it can be whatever.
<andrewrk>
your_name_here: { }
<andi3>
I think your example made me understand, it was not clear to me that blk: begins and :blk ends a block (that is how I would phrase it now)
<MajorLag1>
also useful for breaking out of nested loops
<andi3>
could you give another example? I'm interested now MajorLag1
<MajorLag1>
I think there's one in the docs, one sec
<andi3>
yes maybe I missed it I'm reading through again as I type
<MajorLag1>
hmm... maybe not. I'll type up soemthing quick.
<andi3>
`<andrewrk> your_name_here: { }` -- that confused me because I now think it always has to be something like `<andrewrk> your_name_here: { some_code_ break your_name_here: ret_val}`
<MajorLag1>
you could think of it as a much more limited goto, since you can only break to an outer scope, not an arbitrary label.
meena has quit [Remote host closed the connection]
<andrewrk>
it's true though, we had goto until this syntax. then goto became unnecessary since this is just as powerful
<MajorLag1>
Well, there's still no way to do computed jumps.
<andrewrk>
that's true
meena has joined #zig
<MajorLag1>
there's a proposal for explicit @tailCall() out there that might take care of that though.
<andrewrk>
oh I hadn't considered that that is equivalent to computed jumps
<andi3>
I think the examples in switch and union where you return a value are different from goto though
<MajorLag1>
A bit. Normally the value of a block is `void`. `var x = {};` sets x to `void`. C lets you do this: `int x = { 10 };`, which would set x to the result of the expression in the block. Zig did that too way early on, but it doesn't clearly express intent, so now Zig uses labeled break for that: `var x = blk: { break :blk 10; };`
<andi3>
actually I think the syntax without blk: is cleaner but once you get used to things it does not matter so ....
<MajorLag1>
it only looks that way because the examples aren't complicated.
<andrewrk>
my reasoning is: occasionally you need the verbosity of explicit block names. so that syntax has to exist. then the question is whether we have additional syntax for the simpler case. in general I am in favor of not having additional syntax.
very-mediocre has quit [Ping timeout: 252 seconds]
<andi3>
I kind of hate the word blocks though because I just can not remember the difference between blocks and procks in ruby, poisoned my head, so I would agree with one style for both cases (although this does not really translate 1:1 to ruby, anyway)
<andrewrk>
forget everything you know about ruby
<andrewrk>
if there's a programming language whose design decisions are polar opposite of zig, it's ruby
<andi3>
next thing I want to understand is coroutines and async but without allocators I feel a bit lost in there, after that I'd feel about comfortable starting in zig, I know about the concept of allocators in general but am not clear about how they should be used in zig
<andrewrk>
I agree, we need some "intro to memory management" docs
<andi3>
and the async<...> syntax is a bit ... new? I mean there is nothing like it in the language
<MajorLag1>
It was created as a result of the introduction of async. There are plans to replace parentheses in other parts of the language with <>. For instance `union(enum)` => `union<enum>`, but this hasn't been changed yet.
<andi3>
so the first async example is fine by me
<andi3>
and the abcdefg as well, but the third with the `suspend |p| { comptime assert(@typeOf(p) == promise->void); a_promise = p; }` is again some |...| magic where does p come from? how come p is accessible in the async function?
<andi3>
and when you call `resume a_promise;` it is still undefined as far as I read the code (which is wrong so I miss something of course)
<MajorLag1>
The async call begins execution of testSuspendBlock(), which sets a_promise = p and then suspends, returning control to just after the async call. resume a_promise gives control back to testSuspendBlock() just after the suspend block, which then sets result to true and returns control back.
<MajorLag1>
so a_promise is set to p when resume is called.
<andi3>
so if I do `const p = try async<s..` this does only declare a variable and not yet start the async call afai see, but how can you `resume a_promise;` after that?
<andi3>
I mean what does `suspend |p| {` even do if p is not a local of the function
alexnask_ has joined #zig
<MajorLag1>
I think you're getting confused because the same name is used in two separate scopes. The `p` in `const p = try async<s..` will be the promise yeilded by the async function when it suspends. The `p` in `suspend |p| {` is the promise that will be yeilded when the coroutine suspends. They are the same promise in this case, but that is ignored. The code will work the same if you change either name because they are in
<andi3>
I will kook into it again !
<MajorLag1>
`suspend;` alone would also yeild a promise back to the caller. What `suspend |p| {...` does is provide a name for the promise that is going to be yeilded so that it can be referenced in the block.
<andi3>
look into it again
andi3 has quit [Ping timeout: 252 seconds]
davr0s has quit [Quit: My MacBook Pro has gone to sleep. ZZZzzz…]
<MajorLag1>
andrewrk: might that code be a little clearer as `_ = try async<std.debug.global_allocator> testSuspendBlock();` and then `cancel a_promise;`?
alexnask_ has quit [Ping timeout: 240 seconds]
Ichorio has quit [Ping timeout: 240 seconds]
stratact has joined #zig
<stratact>
I'm curious, does Zig have some sort of reference checking like what Rust has? How does Zig do safety?
donlzx has joined #zig
<andrewrk>
stratact: zig is not safe. It's possible to have a dangling pointer
<stratact>
Are there going to be plans to make it safe in the future?
<stratact>
I mean, having the checking would be a really sweet feature, I just don't see why not