I mean that Zig doesn't "bless" any feature over another. Odin for example has a context feature that passes around common interfaces(mainly allocators and logging). Zig just makes you pass that as a parameter without any special language semantics. Odin has a bunch of types built-in and "blesses" them with special syntax and semantics, while Zig's standard library types just use regular features that you could also reuse for your own types. If Zig ever adds a feature that makes the standard library types more ergonomic to use(e.g allowing dropping the type in method calls), your types also benefit from them
#Please help me understand Zig, I am trying to not use AI
1 messages Β· Page 3 of 1
Actually it's still a bit of a vague idea in my head
that doesn't exactly sound like reusability
No I get you. That makes sense. I'm not sure "reusability" describes it, but it makes sense.
It's funny you bring it up. I listened to Ginger Bill and Jose Valim (creator of Elixir) on The Standup a while back. And Jose said he didn't like building things into the language that users couldn't have access to. He said it felt like "cheating". If I remember right, Bill disagreed, saying the language author should do whatever to make the language better (I am massively paraphrasing here). So it tracks exactly with this
I was thinking a bit about all the interface passing before I realized Odin also does it just implicitly π
In stuff like Odin, Hare, and Jai though, you're more encouraged to just vendor dependencies and modify them if they don't do what you want, though I haven't kept up with how Zig has evolved. Does Zig let you do that kind of stuff?
For example in the Hare compiler, the compiler test suite keeps its own copy of rt(the core runtime library) just to run the tests more easily
A lot of Jai code will also just copy over the standard library and vendor it
To be honest I have mostly used C dependencies in my Zig projects (installed with package manager because I'm lazy), but I don't think it encourages vendoring any less than Odin in general. Easy to just pull code down, keep a copy, and include in your builds.
It's not quite as easy as literally copying though
Git submodules exist 
things engineers do just to avoid curl ... | tar -xzf -
Clone the code, add a few lines to build.zig, and go... So I don't think it makes much of a difference
Unless I'm real worried about keeping up to date on something fast changing I'm going with @halcyon marten on this one. I don't want to deal with submodules unless I have to
This is surprisingly important BTW, and this is something I keep struggling for people to understand when using C3, where functions are preferred and methods should be used as getter/setters mostly.
wait are you the C3 creator?
its funny how often this happens
Well, itβs understandable or?
When did I become nobility??
benefits of using obscure languages
i feel like it's simple enough that if you're using the passive voice to name your functions, something has gone horribly wrong
i would lean more towards .attack, but I think the next best option would be to put it in a common (namespace if there is one) as a normal function instead of a method
oh hey, i've helped in an open-source OOP language, the answer is both!
because the weapon might have special behavior for attacking a monster and the monster might have special behavior for being hit by a weapon
Common Lisp does this :)
you put the methods outside the class
so you end up with "pure" polymorphism without the need for inheritance
This is a super interesting design decision. "Methods" exist in C3, but are really intended for only getters and setters? Can I ask what the motivation behind that was?
ya same as with english, the 2 imply subtly different things
It's kind of a long discussion, I'll try to summarize it:
Or actually let me quote another discussion I had
With methods, there is always the danger of doing subject.verb() kind of code where we're tempted to connect to the whole world.
Consider game_run(&game) vs game.run(). The former ends up making much fewer assumptions about the architecture. In the game_run case, we can move dependencies around and add those to the call. In game.run(), we are generally presupposing that game is the central tree, containing things. So generally each type then tends to connect to everything else. And this architecture is then hard to change, whereas functions tend to be less rigid in terms of architecture. For that reason C3 is generally preferring functions over methods. For example, rather than adding more methods to InStream and OutStream, we call functions such as io::write_fully(stream, bytes) instead of stream.write_fully(bytes). It also turns out that the former is more flexible, as we don't need to strongly associate things with a type.
ββ run test zigb
ββ compile test zigb Debug native 1 errors
src/Cpu.zig:47:46: error: type 'u3' cannot represent integer value '8'
data |= self.memory.read(self.pc) << 8;```
where is it getting u3 from
I take it you're shifting a u8, which has 8 bits, meaning it can be shifted a total of 7 times before semantically overflowing
therefore the rhs of the << for a u8 takes a u3
ok so I had to add some @as casts
fn fetch(self: *Cpu, comptime T: type) T {
assert(T == u8 or T == u16); // Numeric types only
var data: T = @as(T, self.memory.read(self.pc));
self.pc += 1;
if (T == u16) {
data |= @as(T, self.memory.read(self.pc)) << 8;
self.pc += 1;
}
return data;
}
in general, the rhs of the shift operator takes Log2Int(Lhs)
yeah, that'll do it
note: if you have value on the rhs that is < maxInt(Log2Int(Lhs)), but has a larger type, you can do << @intCast(rhs)
idk whats Log2Int
Sort of pseudocode for an integer with as many bits as log2 of the inputs number of bits
std.math.Log2Int (the language isn't actually defined in terms of this stdlib function, but it's the same logic)
but yeah, that ^
ie std.math.Log2Int(u8) = u3
since std.math.maxInt(u3) = 7
That makes sense. But given that viewpoint, why add methods at all?
yee
I see it in practice all of the time
Some things are very naturally expressed as methods, such as vec_a.cross(vec_b) or foo.add(bar)
Bc of creating such a cute (like really nice) language
π
error paths are implicitly marked as unlikely, but not cold. (@setBranchHint can override this.) that was introduced at the same time that @branchHint was added---before then, error paths were never implicitly marked unlikely or cold. implicitly weighting error paths (as unlikely or as cold, i forget what the original idea was) was a very old proposal though, which may be why you thought it was a thing
it would be a bad idea to implicitly weight error paths as cold because many error paths do happen in normal operation and cold basically translates to "make this as slow as you want if it speeds up the non-cold path"
how to do tuple destructing in zig
const result, carry = @addWithOverflow(self.bc.half.b, 1);
const result, const carry = @addWithOverflow(self.bc.half.b, 1);
i am actually glad that zig does that in that way cus, i run into so many problems in JS where i needed one to be mutable
that would work if carry was defined earlier and is var
you can also use var to destructure into mutable variables
how do i fix this error?
test
ββ run test zigb
ββ compile test zigb Debug native 1 errors
src/Cpu.zig:44:29: error: expected type '*u8', found '*align(2:8:2) u8'
0x04 => self.inc_r8(&self.bc.half.hi),
^~~~~~~~~~~~~~~~
src/Cpu.zig:44:29: note: pointer host size '2' cannot cast into pointer host size '0'
src/Cpu.zig:44:29: note: pointer bit offset '8' cannot cast into pointer bit offset '0'
src/Cpu.zig:86:27: note: parameter type declared here
fn inc_r8(self: *Cpu, r8: *u8) u32 {```
adding the align stuff works but then it breaks for other things
pointers to bit fields and normal pointers do not mix
so how should i do it then
First read the pointer to an u8, then pass that
const hi: u8 = self.bs.half.hi;
self.incr_r8(&hi);
sorry guys, I am quitting zig. I will come back once the language has a better lsp and tooling
or am i
(cue vsauce music)
What's wrong with ZLS?
once I do comptime stuff it shits the bed
also doesn't really have good lints, or any lints at all
Why do you need linting? It's a compiled language so any syntax issues are caught by the compiler, and the formatter does the rest.
Not trying to convince you to keep using the language. Just curious since I don't have these issues. But I'm also here for the minimalism so different goals I guess.
What are you migrating
To
linting from the lsp let's me see the errors as I edit the code inside the editor, without having to switch windows and compile. it's just quality of life.
none, I will do some other project in zig, the one i picked was a slog and boring.
although in the meantime I started another rust project
I will continue with zig after I finish it
probably yes
What
Oh rust
i am not "migrating" to rust tho, it's just my comfort language
Yeah that's a good setup
Kinda what I do
You can also take your complains and fome them into nice feedback
Basically what I'm trying to do with the vscode extension and lsp
I mean I'm aware the tooling around the language is bad but I still want it to succeed
Ah. I have nvim setup to compile and display errors with a single keybind. ZLS catches most stuff and anything else is caught by a compile run from the editor. Then I can just jump to the errors
my current project i wanted to work with sqlite, make api calls and get json responses and make a cross-platform gui for it, and that seems very tough on zig so i am just using rusts ecosystem for it
that's a good setup, i use nvim too but i just compile my programs from a normal external terminal... hopefully zls becomes so good in the future you do not need to have a dual setup for looking at errors like this
Yeah different goals, Zigs approach/eco system is a feature here, not a bug. I'm intentionally wanting to write a bunch of stuff from scratch, so not having libraries and what not to easily reach for works for me. When I do need a dep I just use a c lib
yeah I appreciate that, but this is definitely a project I want to iterate fast on, and I definitely don't wanna rewrite some sql wrapper or http requests library, so I will use rust for this.
I think zig would be better suited for projects where you do not want as many external dependencies, like game engines and os, etc
which is what I plan to do with zig in the future
Yeah build on save is what you want
that's a game changer
I did not know ZLS had this. I know what I'm doing later today
Doesn't that happen by default already
Like it builds it for checks?
only if you have a step called check
So it does already
dont think most people have a step called check
also doesnt use incremental by default
π€ I'll have you know one of the minds behind rusts amazing lsp, is a contributor to zig, and writes zig for his current job
zig has plans to improve 3rd party toolings ability to inspect zig, like an lsp
that's good, I hope zig can have a rust-analyzer equivalent someday
made r-a and basically made intellij-rust which is now rustrover and bro doesnt touch zls π
we need a clippy equivalent for zig too
it was a godsend for learning best rust practices
There are zig linters
i should clarify, there wont be an official lsp, rather the zig compiler will have its own protocol that an lsp could be made on top of.
while matklad does not contribute to zls, he has extensive writings on lsp, and speculation on better tooling with mentio of zig. And has been present in zigs discussion for the plans I mentioned
Personally, if I'm gonna use a linter, I'll probably make a bespoke one for the project
We use an in-house linter at work. It's great, there's no configuration nonsense, we just edit the code to tweak things exactly in the way we want, or add features if we need them
Configurable longer with defacto tools helps tho
Until you enter eslint land and need over 300 lines of json to make any decent configuration
just have good defaults 
zig fmt + zig build (and you can have fmt as a build step that gives you errors too)
I mean, deno solves that
meant more like custom AST passes