Discussion about Io and Zig

This is why the brush-clearing in std.posix concerns me. I don’t want to link in libc (by hand, mind you) in order to “just open a file”.

I keep bringing it up because 0.16 isn’t released yet. Putting that stuff back, so that there’s an alternative to Io, would be easy. Releasing a shiny new feature doesn’t have to mean deleting every other way to do similar things, in the same point release. I would argue, in fact, that in this case at least, it should not mean that.

100%. Whatever the response should be to 'the weirdest struct in the standard library", it should not involve radical changes to how structs work. Even if those radical changes are fully justified (they are not), that’s not the way to justify them.

All of them need all 106 pointers, I encourage you to read the entire list. This is a bigger deal than your statement suggests.

This is how I see it as well (“juicy” main, etc).

There’s a bit of the Questing Beast to it also. Zig’s original ‘colorless’ async was a really big deal, it was touted as a core innovation of the language, and it… didn’t quite work. It was regressed, Andrew seemed to think it could be resurrected in a release or two, and it wasn’t.

This is quite admirable. It takes strength to pull a tentpole feature like that, instead of just limping along with something that mostly works. I understand the focus on sticking the landing, and I don’t mind if it regresses some use cases in the process. 0.16 is not going to be the last Zig edition, it’s not even going to be the last Zig edition which starts with a naught.

Ok, here we go…

See, uh. This is the crux of it. As messy and tempestuous as async Rust has been, in Rust, async is an abstraction.

In Zig? Io is 106 function pointers wearing a trenchcoat.

I think we’re finally reaching the limits of that approach, right here, with Io. Zig is missing an affordance, it’s time to admit it and figure out how it should work.

Or, almost time. 0.16 isn’t out yet, so let the man cook! For what my opinion is worth, I think the approach to the problem, the API, the [drumroll] interface of Io, is very much the right thing.

It’s perfectly alright that the first release of that interface is built with what Zig can already provide. It’s actually a good thing, or it can be if the right decisions are made, because features should be implemented toward a goal, and the un-trenchcoating of the Io raccoon-pile is a great goal to guide that feature.

Just, pretty please, put posix.open back? I’ll still use Io, I promise!

17 Likes

Not necessarily. Io.Threaded has a “basic io” which sets all of the network stuff to undefined. Libraries can specificy what they need and, if you need to write your own Io, you can implement just what’s needed.

1 Like

Implementing networking as a segmentation fault is not necessarily an argument against the point I’m raising.

5 Likes

100%, it takes time to go from being “bad at the good thing” to being “good at the good thing” and the process is a struggle and it is necessary.

I am confident zig will thread the needle

6 Likes

I did not write off the language.

I do not think this is a good approach to solve problems, in general, or ever.

That is fair, but I didn’t suggest that. I said, in agreement with a comment made in the previous thread that Zig programmers are systems programmers who know how stuff works. Right now, most people try to actively avoid using the standard library. Bun does not use it at all, if I remember correctly. The standard library, like I have said, is not usable right now. That is my point. You seem to be missing that. I’m saying, expose the usable parts, into a tight, self contained core, for now.

Some of the suggestions involve modifying the language. I think that’s a step in the wrong direction. There shouldn’t be a bending of a programming language’s semantics to fit a library, at this stage, especially if it’s expressible in Zig’s current syntax. A solution put forward by @LucasSantos91 moved much of the work to comptime, which is definitely not what Zig is trying to achieve right now. The current approach involves pulling in 106 function pointers. How has anyone made that work?

1 Like

I understand your reservation about the std, and the breaking changes, but the people who are working on the std, are the same great people behind the language you enjoy, so have a little bit of faith :smiley:

The new Io is in my opinion a great improvement, and something I really enjoy and would benefit from, And It’s going to help me rewrite a firmware at work and bring the performance where I know it should be, but doing so in C is extremely fragile and tedious.

I disagree with your stance, that the std should be kept at a minimal, because you end up becoming C or Rust, C is a bit miserable at time, when you really need to do something a certain way, and there isn’t any code on Github that you can leverage. Because it’s opinionated, uses a certain pattern that wouldn’t work for you.

If you go the Rust way, than the ecosystem starts to dictate the language, and some crates become cornerstone, as if they were part of the language yet, they are not, and it does feel weird.

Or you can go the C++ way, where white paper people, and big tech politics and rivalry will shove everything but useless stuff nobody asked them to put. Making boost basically a necessity to do anything remotely useful in the professional world.

I think Zig is striking a good middle ground, and one that has been painfully missing in the programming space, it’s not bare bone, in fact it very much feels like a batteries included library, and I hope it stays and keep improving.

A decent std should abstract the platform, while giving you the tools to go a step down if you need to. A great standard library, gives you that plus a lot of common data structures, to help you experiment, and try different approach.

I think that Io as an interface is just marvelous, it’s so easy, so convenient, and if every other library starts to use it, than suddenly every library becomes customizable without needing the maintainer to do any work.

Just recently I was working on my firmware in C, and needed to add a few endpoint in the python backend, but the part I needed to connect, was using aiohttp, and suddenly everything had to become or be contaminated by async, and there was nothing I could really do about it, I justd had to accept it, which sucks because my code doesn’t really need that, and it probably makes things worse.

Now if the backend was in Zig, than suddenly it wouldn’t really matter, I could just write my code and not really have to care about whether it was async or not I could run it single threaded and still benefit from aiohttp code.

I think it’s too early to judge the new Io, let’s give it a few month to simmer, let them cook for a bit, and of course feel free to share any pain point, they are always open to suggestion and criticism, and we should all be, that’s how we can make good progress.

But we should avoid to settle down for “good enough” when greatness is not far away.

13 Likes

The problem with this approach is that you miss the opportunity to use your favorite beta testers (your very diligent users). A great “solid and well documented” standard library doesn’t grow on trees, it’s built by having many people experiment, try things and report what works and what doesn’t that’s the benefit of a community we can share our experience, and collectively benefit from the discoveries of others. So yeah it definitely sucks to do zigup master and zig build and be welcomed with a wall of things that don’t compile anymore, for no apparent reason, and it definitely sucks to spend 1h trying to fix a bug which actually comes from the std, at the same time i don’t see a glaring solution to making a great std, except the way it is now.

4 Likes

I don’t have anything concrete to offer in this thread but just want to let everyone know that I’m reading and paying attention.

I noticed the ReleaseSmall binary size regression a while ago, and I generally share the same concerns that have been expressed in this thread. In a way, it’s heartening to see that users of Zig are experiencing exactly the same worries that I am while working through the design of the language and standard library. I recognize however that without being in a position to make decisions it’s a lot more frustrating.

Keep the constructive criticism flowing.

66 Likes

I agree this is the potential and I am also excited about it, but I want to add that it remains to be seen how well it works. I don’t think this has ever been done successfully before, so it’s a little experimental. There are definitely cases where, to really work well with a library that expects async (or vice-versa), you need to make sure your code is actually async (not falling back to sync) as well. I am optimistic that Zig Io is going to be better than anything available, but I don’t think it will make every combination of io code magically work well together. I guess everyone knows this, I just felt it was worth saying so as not to mislead anyone.

2 Likes

I have expressed my doubts on the feasibility of the current I/O situation in Zig. After further research, I can appreciate better what @andrewrk is trying to achieve. However, I think we should still be able to note the limitations of the current approach, while hoping for better things down the line.

I will reiterate that right now, I don’t think that using the standard library offered by Zig is a wise choice as of v0.15.2. This is not necessarily a bad thing. Zig on its own is a wonderful programming language, and the FFI is more than good enough for anyone to interop with most reasonably implemented C libraries. I have expressed that I would prefer that the standard library tend towards being small and clean, whatever that shape turns out to be in the future. The Zig team can figure it out.

If a different direction is chosen (in the name of innovation, whatever), I would still write Zig. It’s frankly way too cool to dump over two or three hook-ups. That is my position right now.

3 Likes

also just for context :slight_smile:

~/w/tt ❱ cat hello.cpp
#include <print>

int main() {
        std::println("Hello World");
}
~/w/tt ❱ clang++-21 hello.cpp -o hello \
               -std=c++26 -stdlib=libc++ \
               -Os -DNDEBUG \
               -fno-exceptions -fno-rtti \
               -fvisibility=hidden -fvisibility-inlines-hidden \
               -ffunction-sections -fdata-sections \
               -fomit-frame-pointer \
               -fno-unwind-tables -fno-asynchronous-unwind-tables \
               -fno-stack-protector \
               -fno-ident \
               -flto=thin \
               -fuse-ld=lld \
               -Wl,--gc-sections \
               -Wl,--icf=safe \
               -Wl,--strip-all \
               -Wl,--as-needed \
               -Wl,-z,now -Wl,-z,relro

~/w/tt 2.1s ❱ cat main.zig
const std = @import("std");

pub fn main() void {
    std.debug.print("Hello World\n", .{});
}
~/w/tt ❱ zig build-exe -fllvm -flld -OReleaseSmall \
               -fsingle-threaded \
               -fno-stack-check -fno-stack-protector -fomit-frame-pointer \
               -fno-error-tracing -fno-unwind-tables \
               -fdata-sections \
               -fstrip \
               -Mroot=/home/pollivie/workspace/tt/main.zig \
               --name zhello

~/w/tt 1.8s ❱ ls
Permissions Size User     Date Modified Name
.rwxrwxr-x   51k pollivie 22 Jan 23:06   hello
.rw-rw-r--    70 pollivie 22 Jan 23:06   hello.cpp
.rw-rw-r--    95 pollivie 22 Jan 23:05   main.zig
.rwxrwxr-x   56k pollivie 22 Jan 23:07   zhello
~/w/tt ❱

As you can see even trying to compare modern languages, zig is not that “fat”

Only C manages to go at 14k on my system.

2 Likes

zig 0.15.2 beats that with 8K on my linux system (aarch64)

5 Likes

My point is that you can have constructive criticism of the language and Io, but you are being knee jerky about features that aren’t even finalized or gone through a period of optimization. It’s fine to note all of the problems with IO in it’s current form, I encourage it. But your first quote isn’t helpful, and your second quote is just you going back on what you came into this thread with.

What you suggested was that Crypto and Http be done in the community, nothing is stopping community members from doing their own implementations that is perfectly fine, but I don’t think that it is a valid substitute for a battle tested and batteries included std for what are critical functions. It’s easier for the language and the community to have an implementation that covers 90% of use-case is well tested and safe especially in crypto. My point was that alternatively in the js ecosystem critical function were offloaded onto the community and it ended up coming back and needing to be implemented in the language itself.

I see it as the opposite quite frankly, first Io isn’t a library it’s an interface there is a huge difference, we are abstracting away implementation not providing a specific one. Additionally if we are going to modify the language lets break everything and lets do it now. Lets try radically different things that others haven’t tried before, lets introduce a keyword or two that signals to the compiler that a data type is Special such as @Vector or @Matrix, @Vtable probably isn’t what we want, but I think that “at this stage” is relative to what you want out of zig. I want a language that gives me control, and I don’t have a time limit on when that comes as long as it done right. I put my whole faith in the Zig team, that they will take the input of people in this thread, and implement something that address’s everyone’s concerns to the best of their ability. It may not happen in 0.16.x, maybe 0.17.x, who knows, but I think this:

Is unhelpful, offers no constructive criticism or technical insight, and just seems like doom posting.

I’ve been on Master sense Io was introduced, it broke all my code, nothing is documented, paradigms have changed in real time, and I’m elated with the whole journey. What you see as something that can’t be made practically workable, I see as the foundations for something radically different that within a few iterations will likely solve much of the same problems all other languages have faced with Async/concurrency/async control flow…etc. This is the teams 3rd or 4th stab at this problem, it’s not an easy one to solve, so let’s let the team cook.

4 Likes

Oh, man… I understand the point you’re trying to make, but I think you’ve proven the opposite. Beating C++ should be a given. In ReleaseSmall, this should have been a landslide for Zig, and it used to be, as @cryptocode showed.

4 Likes

In addition to that, the reason why the C++ version there is so big is because while using the new formatting functions (with std::println being one of them), you get quite a lot of unicode stuff compiled in for some reason.

That isn’t the case with Zig.

4 Likes

Oh yeah I’m not saying that’s good because Zig did slightly worse than C++ (believe me I’m not in any way shape or form saying this is the target) But even in it’s “worse” shape it’s still not completely insane. I mean 60k is probably 40/50 k more than I’d expect, but it’s still usable, like if you are concerned about 50k, you are working on a very tiny controller where you won’t be using the std anyway, and the only Io you will do will be through volatile gpio/uart/spi/i2c magic address, It’s still concerning don’t get me wrong, but it is not completely insane either

2 Likes

True this is also why C printf is only 14k.

Putting it here as I felt like it was a good idea from @Lowell

It would help even maybe having only certain vtables be copied to the binary. I feel like it would improve anyway on readability and ease of implementation as well.

Full credit to @Lowell ! It just seems like that comment was interesting enough to be shared here.

7 Likes

I see where you’re coming from, but this leads to exactly the problems that Io is trying to solve; multiple stdlibs lead to wildly different and mostly incompatible programming paradigms. We saw this with 0.15 and we see it now with 0.16. This causes the following real-world problems:

  1. Onboarding to a zig codebase becomes a lot harder. Every stdlib makes the language its own dialect and simple things like how you test are different for each one.
  2. Libraries become incompatible. Any library that does I/O in any way either needs to decide which stdlib to use, which reduces the potential number of users, or build its own abstraction, which is cumbersome, error-prone and costly.
  3. Libraries are less portable. If each stdlib is designed for a different platform (embedded vs web vs posix) and has a different I/O paradigm, you won’t be able to use the libraries you’re used to when switching to a different platform, you need to find (or write) new solutions for everything.

These issues have all occurred before and are painful every time: LISPs are so general that every codebase works like it’s own language, C++ has a huge standard library that you can’t use on embedded, so Arduino had to re-implement everything, Rust only solved async at the language level, causing tokio to become a de-facto standard that many libraries require.

Io is a solution to all of this. It’s a common interface to the system your code runs on, a compile-time VM, if you wish. If you want to run zig code on a new platform (heck, even a garbage-collected runtime!), you only need to implement (part of) this interface, and you’re golden. You can mock networks, filesystems, threads in tests without a need for virtualization or complicated workarounds. It’s a level of abstraction no other native language (C, C++, Rust, etc.) provides or can feasibly provide; only languages with a runtime (Lua, Go, Java, Python) can hope to achieve something similar, and even for them it’s quite costly to do so because cross-compiling the whole runtime to a different platform comes with many challenges, except for something as simple as Lua.

However, Io can only ever deliver on this promise if it is THE way people write libraries and applications in Zig. The vast majority of Zig code must use Io for 100% of its I/O, otherwise many of its advantages are invalidated.

Of course it shouldn’t be prevented to just link libc manually and call functions in there directly, but it must be much less convenient than whatever the stdlib provides.

22 Likes

Great post and perspective, thank you. I have experienced some of the problems you’re describing (multiple “standard” libs in OCaml, dependencies on Tokio in Rust) but I hadn’t thought about Io in Zig as the solution to these problems until now.

1 Like