-
-
Notifications
You must be signed in to change notification settings - Fork 2.6k
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
Hacking-friendly "--sloppy" mode for Zig. #3320
Comments
A suggestion for option name: --exploratory Sorry for bikeshedding, but the topic came up on Discord a while back and someone said that "Zig is currently not great for exploratory code", or something to that effect, so that's where it comes from. :) Sometimes, you just don't know exactly what you're building, or how you want to do it, so you're "exploring" how to write the code. (Especially true for people new to Zig.) Having to handle for example allocation errors when you "know" they won't happen slows you down more than it helps in this particular stage. |
Related: #68 |
The main issue with this idea (in my mind at least) is that it'll almost certainly result in people forgetting to turn the feature off, and then having to clean up a giant mess of code when they go to e.g. submit to a Zig package repository. I think this idea is definitely workable, but it might need a tad more thought. Another more minor issue: I don't think downgrading things like unreachable code to warnings is the best idea - that I think should show up as an even lower priority, as otherwise too many warnings could stack up quickly, making it hard to find what you're actually looking for. On the other hand, having it show up as a warning means there's a greater incentive to fix it, to clean up the warnings list even in This definitely needs a lot more thought, though I do think it has merit. |
Obviously, in the sloppy mode warning classes should also be ignorable. For non-sloppy-mode usage, warnings should be ignorable on case-by-case basis, when unreachable code is by design due usage of to generic ( |
That's inevitable. It is also inevitable that sloppy-only libs that only live on Github, not on the official repository will apear. But I assume there are many projects that don't come to the phase where it is reasonable to publish them (i.e. experiments). Pre-publishing cleanup (during which README, documentation, tests, etc. may be written) is simply not needed for them. Requiring clean code for everything (including one-off throw-away code) may be suboptimal. |
Jonathan Blow just tweeted a thread (how timely! :) ) related to this: https://twitter.com/Jonathan_Blow/status/1194289482469502976 More of my perspective: I tend to always work in "full" warnings/warnings as error-mode in C/C++, so I appreciate releasing things where the code doesn't have hidden bugs or whatever. But I agree with Blow here, and it probably needs to be more granular than on a project/build-wide setting? Like let's say you're working on a game and you're working on a subsystem that you want to fiddle with a bit before deciding on the final implementation, it makes sense to be able to mark that as sloppy, in some sense. I'm not sure if that can be easily solved in practice though... :) And like @vi said, sometimes you work on something that you never have time to finish as a "proper" project, but that may still be valuable to people. Here's one of mine, https://github.com/Srekel/the-worldinator, it's not done yet and not sure if it ever will be, but I imagine someone might learn something from it. |
Some warnings are not about hidden bugs, but about maintainability. Notably unused code warnings (including unused imports and unused mutability). Those warnings are most often triggered by something being unfinished (although sometimes they point to a bug as well, but rarer). For example, when I start doing things in Rust, I typically add
Nobody forbids publishing unuploadable-to-official-repository code to a github. |
Also related: #335 (proposal to add more errors for unused/unreachable things - these should be reduced to warnings in sloppy mode) |
Instead of a command-line option, I would suggest some type of configuration inside the source code. If code is written in this Allowing each source file to declare this also allows you to compile multiple modules with sloppy mode enabled/disabled on a per-module basis without having to invoke zig multiple times to enable/disable it for each one. |
After reading more about this it actually may make more sense to make it a command-line option. I thought this was a permanent type of mode for zig source code, but it looks like it's just a temporary switch to aid in faster refactoring/development. |
@marler8997 , I think it should primairly command-line switch, but also triggerable from Production-oriented version of Zig compiler may reject both. |
I think that's a good idea. |
Related: #224 |
This very useful for one off things or proof of concept. I suggest to make it as annoying so it is used sparing. I suggest avoid adding features but instead downgrading several errors to warning if they are related to safety or code maintenance. So if want to test a new method to do something as a proof of concept, I could test spend more time to test if the idea works. |
the only material change to the language this proposal proposes is one large barrier making Zig less "hacking-friendly" imo is needing to always having an allocator handy, but I don't think there are any plans to change that. and it's not that big of a mental change to need to add one extra property to your structs. if someone doesn't like the ethos that Zig is going for, I would hypothesize that they are more than welcome to use another language. |
The more I think about this proposal, the less I like it.
The specific issue linked could be solved in userspace. More generally, the idea of adding functionality in such a mode seems quite problematic.
This seems like a bad idea. There's a reason these are errors. My general rule for this is that if something indicates a problem with the code, it should be an error and not a warning.
People are used to ignoring warnings in other languages (especially C). Adding one for this won't accomplish anything.
These two points are contradictory. If it's possible to check, at comptime, whether sloppy mode is enabled, some functionality will be written to depend on it - and quite likely to change behavior based on it. Being able to check for sloppy mode at all seems like a bad idea, given the stated intent. Fundamentally, this proposal means creating a compilation mode which ignores the Zen of Zig. It explicitly is intended to favor writing code over reading code, it replaces compile errors with both runtime crashes and bugs, and it prioritizes short-term developer convenience. Another critical point: we intend to have a language server embedded in stage2 at some point. This should make refactoring painless anyways. I believe any other use cases for a sloppy mode can be addressed similarly - instead of making it possible to disable the language's safety features, we should work on making it so that you won't want to. Overall, this seems to me to provide some short-term benefits at the expense of long-term code quality, and to make matters worse it requires complicating the compiler. Accordingly, I've changed my vote for to a vote against this proposal. |
This is only inevitable if we make it an option.
This is my fundamental issue with this proposal. This proposal will result in bad code being written and published - and, realistically, used as-is.
This is why we don't have those warnings in Zig. I think that if there are places where Zig is currently inconvenient to write, we should focus on fixing that without simply giving up and encouraging bad practices. |
I think that this line of complaint is avoiding the original point, that this is a opt-in flag, which will be added in a way (to be determined) that makes it clear that it's only for use in the thick of development. We're not proposing to "add warnings". And if people are able to post or paste online code that only builds in sloppy mode, who cares? They can also publish code that doesn't compile at all. Or code that only compiles on an old version of Zig. I don't think people are going to take a third party library seriously if it only compiles in sloppy mode. You as the application developer will most likely be using those libraries in source form, and you are the one setting sloppy mode or not. Tabs vs space is a hangup for some people, this is a hangup for me. It's extremely annoying to get unreachable code or unused variable errors when you are trying to debug something and commenting out 10 different regions of code in turn. (And I don't agree with having to rely on IDE features to smooth this out.) And I don't think invoking "Zen of Zig" is valid in itself. If it says that Zig is supposed to favor readability over writeability, you can interpret that to any degree you want. Why are we going to have a live reloading incremental compiler? That's pure writeability. Disclaimer: I might be arguing for a smaller subset of the sloppy feature than was originally proposed here. |
They can also publish code that doesn't compile at all. Or code that
only compiles on an old version of Zig.
Code that doesn't compile won't be given attention. "Old versions of zig"
becomes irrelevant post-1.0, sloppy code doesn't.
I don't think people are going to take a third party library seriously
if it only compiles in sloppy mode. You as the application developer will
most likely be using those libraries in source form, and you are the one
setting sloppy mode or not.
Sloppy libraries will probably result in the application developer
turning on sloppy code too.
Tabs vs space is a hangup for some people, this is a hangup for me.
It's extremely annoying to get unreachable code or unused variable errors
when you are trying to debug something and commenting out 10 different
regions of code. (And I don't agree with having to rely on IDE features
to smooth this out.)
Refactoring wouldn't be an IDE feature, it'd be built into the compiler.
And I don't think invoking "Zen of Zig" is valid in itself. If it says
that Zig is supposed to favor readability over writeability, you can
interpret that to any degree you want. Why are we going to have a live
reloading incremental compiler? That's pure writeability.
Incremental compilation has no affect on readability, and is not favoring
writability over readability as a resuly. Sloppy code is less readable as
a direct cost of the improved writability, which does.
Disclaimer: I might be arguing for a smaller subset of the sloppy
feature as originally proposed here.
A smaller subset I'd likely be fine with, I'm against the proposal as
outlined in the OP.
|
Ok, rereading the issue there is a lot more going on here than I had in mind. To me, sloppy mode should just suppress lint-like errors like unreachable code, unused variables, using var where you could have used const, declarations being required to be in a certain order, etc. That's it. I definitely don't support the idea of letting code check if sloppy mode is enabled, adding sloppy-only library functions, and stuff like that. |
This pushes the language into Java-esque IDE-only mode and detracts from text editor-based flow. |
I don't thing that would happen in masse if typical Zig user values good code (and I expect average Zig user mindset to be different compared to baseline). |
IMO it's different because Zig aims to be a language that doesn't change often. As I understand it, Rust's nightly contains features which are planned for addition to the language but may not be available yet or may change, so beware. This seems like the primary reason that CIs tend not to allow nightly - it's likely to suddenly stop working at some point. But with Zig, the language doesn't change. So we have two separate dialects that are just as stable as each other, but one is more permissive than the other. Making a codebase compliant with the strict subset of Zig will be thought about the same way we think about converting html into xhtml. It's great if you are super strict about it but not worth it in almost all cases. |
Nobody asked for it, but here's a fun idea for making absolutely sure
(Yes, you can write a script, but you can also just patch out errors from the compiler, and it's easy enough to adjust by hand.) |
Trying to write the code that contains no unused things that are caught by Zig's "errors" is like trying to build a house while keeping construction site tidy and clean at all times. Construction sites are typically messy and not pleasant to be in (at least in clean clothing without a hard hat). Cleaning up and removing all the garbage (in programming language case: unused things and dead code) typically happens near the end of construction process. |
Maybe a better idea is to allow only one Zig source file/module to be sloppy at a time: |
I think you may be overestimating what Zig will count as a compile error. Unused or unreachable code can't possibly be standard compile errors. The entire conditional compilation system is built with the assumption that there will be very large amounts of both of these. The only issue this proposal mentions that is actually accepted is #224, and even that is susceptible to major conditional compilation problems. The other two, #2304, and #335, can only possibly be validated with multibuilds (#3028), which will be optional. The only thing left is removing the requirement that switches handle all cases. But I don't think it's worth a whole separate compilation mode to avoid typing |
Is multi-build being stricter than regular build a design choice or just an implementation detail that may change as Zig compiler gets more clever (i.e. detecting that a thing is obviously unused regardless of various flags because of it is mentioned in the source code exactly once)? Will there be "open" multi-build mode where Zig should check things for specified list of targets, but not assume that the specified set is complete (so some things can still dangle around)? In presense of multi-build, will simpler modes like |
My assumption was that multibuilds will act more like a linter than a compiler, but I might be wrong about that.
Before 1.0 there will be a language spec that defines exactly what the boundaries are for what does and does not compile. It will err on the side of simplicity, so "clever" checks that are difficult to describe in a specification will likely not be allowed to cause compilation failure, unless they are deemed to be really valuable (like maybe some cases of returning pointer to stack var).
Some form of this must certainly exist. The standard library contains code that only compiles on windows, but not all projects which import the standard library will have windows as a target. It would be ridiculous for this to be a compile error. That said, it could be that the plan is to only run multibuild on the "root" package, and not validate external packages. In that case there would be a simple workaround would be to make your root package a stub that references "sloppy" code, but that's a simple enough hole that it will probably make sense to make an open build mode. Additionally, multi-builds inherently require a decent amount of configuration, so I think there will probably always be a simple alternative way to say "build this zig file into an exe and don't do the multibuild validation". All that said, I haven't actually talked to Andrew about his specific plans for multibuilds, so I might be wrong about the plan here. |
What I don't want is the Go problem, in which the programmer has to write dead code that the compiler allows, so that way the compiler doesn't flag the dead code that it doesn't allow as dead code: package main
import "fmt"
func main() {
_=fmt.Println // important
} |
Got here via: https://www.reddit.com/r/Zig/comments/ooknzg/lament_for_the_unused_parameter/ What if --sloppy mode would compile (parse, syntax check, etc) but not write code to disk? Well, might have to be a bit more clever to allow 'zig run' to work: write it to a tempfile, run it, then immediately delete it. The point is: if the worry is about (accidentally or otherwise) putting code built with --sloppy enabled into production - just never allow that code to hit (accessible) disk. Or maybe there's a way to get the language server to suggest workarounds as potential fixes? eg. "Error: unused parameter 'foo' in function 'bar'. Remove it or add 'var __foo = foo;' to the top of the function." That would at least let people copy/paste fixes. |
Here's a way to think about unused locals and unused parameter errors: The Zig language is designed to prevent bugs by making strategic use of friction. For example this is why coercing a Likewise, Zig supports unused parameters and unused locals. However such things are indeed correlated with a higher chance of there being a bug, so you have to confirm that the non-use is intentional with additional syntax, just like with Friction is, by nature, uncomfortable, and people seek comfort, meaning that people seek to program in a way the language does not induce friction. Making use of all variables and parameters even while experimenting corresponds with the principle of YAGNI, which is a programming style unfamiliar to some. To those who already have taken YAGNI to heart, it will feel like the language is guiding you down the path you already wanted to go. For those who do not program this style, it will feel like the language is fighting you. For young programmers, it will shape their preferences as they develop their own programming style. The Zig language is opinionated. There is one canonical way to do things. There will be no sloppy mode, and no divergence of compile errors from debug to release. For those who find their own programming style to be in conflict with the language, I do have a suggestion, which is tooling. It would be reasonable to make a command line tool that you run and it "fixes" all unused parameter errors by adding Final point I want to make, the first priority of the Zig language design goals is the ability for a person to quickly and comprehensively peruse an unfamiliar codebase, and really understand it completely, holding it all in their head, confidently able to maintain it without worrying about unknown unknowns. Not allowing shadowing, not allowing unused variables, and requiring the use of const are all part of achieving this goal. Making writing code from scratch "fun" was never a goal of the Zig language. Personally, I think it's fun to make great software, but what's fun has to do with the application and end user experience, rather than minutiae about the language. A carpenter might have fun building something out of wood using a hammer and nails, but they don't use a "fun hammer". They just want an effective hammer, and the fun part is the part where you build something. |
If this is some principled position then maybe respective entry should be added to
Shall "annoyed by some language detail - write a tool to handle" be official position? Shall zig toolchain include means for making such tools relatively stable and simple enough? |
Ignoring programmer's comfort too much can lead to workarounds that are even uglier, as demonstrated by Go example in a comment above. Such kludges are also sometimes seen in Java code (e.g. to evade unreachable code detection). So completely ignoring code writer's needs for advancing code reader's needs can backfire. Some helpers should be available, be it inside Zig language/compiler or as a separate (but more or less popular and/or officialy supported) tool. |
This sounds like a false dichotomy. I don't want to remove unused var errors, they are good and I think most people agree. We can have unused var errors and still make it easy to write/triage/debug code in Zig. The unused vars case is interesting because it demonstrates a case where we have Zig code that can be compiled but that the compiler thinks could be a bug so it refuses to do so. The compiler is taking on multiple roles, that of a compiler and a static code analyzer. This is a great feature because it helps find more bugs at compile-time, but this particular use case is at odds with code as it is being developed since there are times when vars aren't used because the code hasn't been written yet, or its been commented out for triage/debug. It's at this time where we don't want the static code analyzer, we just want the compiler. Note that this occurs not only when code is being written, but also when it's being triaged/debugged which falls into the "reading code" camp as well. If we think this issue is big enough to address, I think it would be best to classify errors based on whether they affect compilation or whether they just "look like they might be bugs".
A command-line option that tells the compiler to temporarily stop looking for "potential bugs" and just compile the code could solve this issue. I'll add that my confidence that this is an issue that could justify added complexity has increased since experiencing what it's like to write new code with this new analysis enabled. If this is an issue worthy of concern, then I expect everyone else will start to feel the same annoyance and we can talk about potential solutions then. |
This is an incredible *feature* of Zig and raises the standard of all Zig code. I am firmly against there being a sloppy mode or a way to disable certain compile errors. As both a user of Zig code applications, libraries, and maintainer of a few libraries I absolutely love the assurances that we get for everyone by default with these errors. Other languages struggle and go through great lengths to come up with an extensive collection of linting tools. With them being in the compiler itself, you can *know* that if you run That's more fun to me than writing code will ever bring. |
Remember, Zig offers many a great developer features such as comptime, strong but lazy typing, etc, etc, but we (as developers) were never the main priority from the start. Zig's not for when you're writing code. Its for when you or someone else is trying to update or run your code weeks, months, or years in the future.
|
Wow, bikeshedding in this issue is colossal. Just because of that I don't want the flag. |
If all this was about was unused variables, why not just |
There may be similar rule about unused mutability ( |
Sure thing. But Go and Zig seem to be the hammer that makes your hand hurt while using it, which you only ever use when the other hammers you have don't do the job well. Due to opinionated stuff like this (which are bad opinions, in my opinion), I only use Go when a library is only available in that language, and I only use Zig for its C/C++ compiler. Great job on that part of Zig, by the way! I only wish Zig itself would be better to use so I wouldn't have as much of an use for C/C++. Since the lack of a "sloppy" mode actively prevents me from compiling and working, and breaks me out of my workflow to write bugs, Zig is actively trying to not be an effective hammer. If a hammer constantly gives me pain and forces me to readjust my grip, it's gonna be less effective than the hammer that just lets me use it to work.
These "features" make building something hard, frustrating, and seem to be specifically designed to make it not fun. It makes it so that when something finally gets built, it feels like it got built despite the language, not thanks to it. |
Is explicit, documented refusal to implement dev/permissive mode better than endless postponing of it?
Zig development proper may then focus on providing hooks and features for various language tools (not just "sloppificator', but also static analyzers, code tranformations, checking interpreteres, etc.), leaving implementation of convenient editing mode to the community. |
Taking my first steps with zig at the moment, and immediately ran into this. How do people meaningfully iterate on code when the compiler complains any time what I'm writing isn't completed yet? Eg, I wrote this as a start to my program: pub fn main() !void {
var g = Graph.init(std.testing.allocator);
} But my program won't compile. Do I need to delete some of my code (the This is a horrible experience. And for what? To "force" me to "always write good code"? I'm not your wayward child. How utterly patronizing. |
No, you can use the first is done automatically by ZLS' autosave feature, but the second you have to do yourself |
Thanks - I just figured that out. (It took me a lot of searching to realise that was an option - it would be great if the compiler error suggested that as a fix).
.. But, this is the worst of all worlds as far as this error goes. It means that I don't even get a compiler warning when I have unused variables. My code is silently "corrected" regardless of whether or not the fix matches my intent. If you're going to autofix code to suppress this error, zig may as well just turn the error off entirely. |
@josephg Just to clarify, this is the reason why the Zig (core) toolchain has so-far taken a stance against automatic edits. |
This is significantly worse than a compiler warning, but I can see how it ended up this way if zls is community maintained and has a different opinion about this issue than the zig compiler authors. Sorry for temporarily hijacking the thread - and thanks for the advice. I'll give that a whirl. |
As far as I understand, Zig places great emphasis on code maintainability.
Unfortunately, restrictions required for it are at conflict with ease of development, debugging and prototyping.
I suggest to have a special Zig mode that trades code maintainability for short-term ease, ergonimics and speed of hacking. Command-line option may be
--sloppy
or--devmode
or--lax
.Proposed rules for such mode:
--sloppy
as a minimal acceptance test.--sloppy
mode code.dbg!()
from Rust #3296switch
cases and so on.--sloppy
mode, as an eye sore reminding that it is not supposed to be permanently enabled.--sloppy
without modifying source code should not affect program behaviour.@compileError
if it is not enabled. The reverse should not be idiomatic.--sloppy
mode. Master builds may be required.The text was updated successfully, but these errors were encountered: