[HN Gopher] How a Zig IDE Could Work
___________________________________________________________________
How a Zig IDE Could Work
Author : todsacerdoti
Score : 139 points
Date : 2023-02-10 15:32 UTC (7 hours ago)
(HTM) web link (matklad.github.io)
(TXT) w3m dump (matklad.github.io)
| fb03 wrote:
| Context: Matklad is the main dev behind rust-analyzer
| 3836293648 wrote:
| And a major dev behind intellij rust
| norir wrote:
| > works with incomplete and incorrect code
|
| I know that this is a standard assumption, but I personally
| believe that it is a mistake to support this use case. It is a
| holdover from the batch processing days when getting feedback on
| your program could take minutes or hours. In those conditions, it
| was absolutely essential to try and proceed with a partial
| compilation even in the presence of errors or else iteration
| would have almost been impossible.
|
| Now we live in an era of fast personal computers. With a language
| that is designed from the ground up with IDE support/rapid
| iteration in mind, you can get feedback with every keystroke.
| Everything is easier to design if you abort immediately on the
| first error, whether it is syntactic or semantic. Designing a
| parser with error recovery from syntax errors is a particularly
| dark art. On some level, you have to resort to guessing. It may
| work _most_ of the time, but to me there is nothing worse than a
| tool that is maybe correct.
|
| When you advance past an error all of the code below the error is
| suspect. To give a trivial example, let's say you rename a
| function with 100 call sites without some kind of refactoring
| (automatic or manual). What benefit is there in showing the 100
| new failures when the root cause is exactly where your cursor
| already is? There are cases like this that are even subtler where
| you are bombarded with downstream error messages that are
| confusing and taking you away from the actual root of the problem
| (c++ template errors come to mind). You may as well just gray out
| everything below the first error and reprocess it only when the
| error is fixed.
| hinkley wrote:
| I've had this conversation a few times over beers: I think we
| are collectively Doing It Wrong by not considering code edits
| as deltas and checkpoints.
|
| Diffs often seem to fail to represent the actual change because
| they consider the delta from the last commit, which isn't the
| way we write code most of the time. If I go and insert a bunch
| of closing braces into the middle of a function, it's almost
| always because I'm dividing the function into two functions, or
| adding some missing error handling, or a corner case. So from
| the standpoint of a DAG representing the parse of the file,
| most of the time I expect the functions above and below to
| still be available in autocomplete even if I haven't balanced
| the tokens representing code blocks yet.
|
| If you saw a bunch of functions and I break one, I expect the
| IDE to consider the function broken, not the file.
| IshKebab wrote:
| I think you have misunderstood. He's talking about IDE support.
| IDEs absolutely _must_ be able to understand incomplete or
| incorrect code otherwise code completion will be completely
| useless. You literally only need code completion when your code
| is incomplete. The clue 's in the name.
| norir wrote:
| I am focusing on the incorrect part. Let's say that you are
| adding some source code in the middle:
|
| ...
|
| def add(x: Int, y: Int) = x + y
|
| ...
|
| let x = ad|> I am using |> to denote the cursor location
|
| ...
|
| I agree that where the |> is that the IDE should be able to
| autocomplete `add` for you. What I am saying is that it
| shouldn't process anything below the let statement because
| the code is broken there. Many IDEs and compilers will
| continue processing the file after the first error so that
| they can report all of the findings. That is what I am
| suggesting they not do.
| Denvercoder9 wrote:
| If you stop processing at that point, you can't
| autocomplete any functions that are defined further down in
| the file.
| norir wrote:
| Exactly. That is a feature.
| IshKebab wrote:
| How is that a feature? If someone gave you an IDE that
| could autocomplete in the face of minor errors earlier in
| the file (which they often can) you would say "sorry I
| don't want that feature. Please disable it"??
|
| Why?
| norir wrote:
| Because I think that everything should be declared and/or
| defined before it is used. I don't want the IDE to
| autofill something that is declared below the cursor
| because I wouldn't be able to use it at that location
| anyway.
| junon wrote:
| In some languages this is literally not possible...
| Denvercoder9 wrote:
| Maybe for you, but I'd consider that a pretty annoying
| bug. I like to structure my files/classes/modules in
| decreasing order of abstraction: put the public API and
| high-level algorithms at the top, and then dive into the
| minutiae how the various parts are implemented as you
| scroll down. That also means I almost exclusively call
| functions that are defined further down in the file.
| norir wrote:
| > I like to structure my files/classes/modules in
| decreasing order of abstraction: put the public API and
| high-level algorithms at the top
|
| That makes sense. What I am describing doesn't preclude
| this organizational structure. It depends on the language
| design. You could either support c style forward
| declaration that you put at the top of the file and would
| be available for completion even if the implementation is
| below the first error in the file. Or the IDE could
| provide folding of the implementation so that you can
| scan the api without drilling down into the details.
|
| > That also means I almost exclusively call functions
| that are defined further down in the file.
|
| Again, to clarify are you calling things before they are
| _declared_ or _defined_?
| Denvercoder9 wrote:
| Most languages don't make a distinction between
| declaration and definition, and many don't even care
| where something is defined/declared at all. C/C++ are
| really the exception nowadays, and for good reason:
| having to keep the definition and declaration in sync is
| annoying and unnecessary, even though I sometimes miss
| the easy overview of an API header files give.
| norir wrote:
| Ok. So I'm guessing you're using a Java like language
| with classes and that the upper classes call into the
| lower classes? I understand the appeal of that approach.
| The tradeoff is that you have to make multiple passes of
| the file to compile/do IDE analysis. If the language is
| designed as I've been advocating, one can essentially
| write a one pass compiler. This is simpler to implement
| and will generally be faster. The big tradeoff is that it
| imposes a more rigid organizational structure.
|
| As a compiler author (I have written a self-hosting
| compiler for an unpublished new language), I dramatically
| prefer implementation simplicity to organizational
| flexibility. I respect your preference but believe that
| ultimately the more free-form and flexible a language,
| the more complex and slow the tooling, both of which lead
| to more bugs in the final artifact. But I certainly can't
| prove my point.
| pyrolistical wrote:
| Reminds me of Eclipse days where they explained their Java
| compiler was more sophisticated than javac as they had to
| incrementally compile broken code as that is the main state of
| code during development.
|
| Maybe we need to do the same here
| systems wrote:
| What happened to Eclipse, is it still big for Java developers
| ??
| IshKebab wrote:
| I don't think so (IntelliJ is way more popular).
|
| I think Eclipse is mostly popular these days as a platform
| for companies offering custom tools. For example Team Center
| (which is awful btw). Honestly Eclipse was always pretty
| awful and I have yet to use any Eclipse based software upon
| which that awfulness didn't at least leave a mild stench.
|
| Check out this list:
| https://en.wikipedia.org/wiki/List_of_Eclipse-based_software
| DarkNova6 wrote:
| For the most part, Eclipse has fallen into obscurity. But I
| doubt that it's competitors work any different in that
| regard.
| winrid wrote:
| Yeah, pretty sure Intellij does the same thing. A few bad
| lines rarely affects syntax highlighting/suggestions
| further down in the same file.
| nindalf wrote:
| Fun fact - The author of this post used to work at
| JetBrains on IDEs.
| KronisLV wrote:
| > For the most part, Eclipse has fallen into obscurity.
|
| I guess it depends on the locale/company/environment?
|
| In most conferences, online videos, as well as among the
| people I know personally, JetBrains IDEs (IntelliJ IDEA for
| Java) seem to reign supreme:
| https://www.jetbrains.com/idea/ They have a community
| version, personally I pay for the Ultimate package of all
| the tools. They're slightly sluggish, want a lot of RAM,
| but the actual development experience and features make up
| for that. Hands down, the best set of IDEs that I've used
| from a pure development perspective - refactoring
| enterprise codebases mostly becomes something you can
| actually do with confidence. Running tests is easy.
| Integrating with app servers, containers, package managers
| or container runtimes is easy. Even remote debugging is
| easy to set up, as is doing debugging, or even testing web
| APIs. I'd say that all of the features that should exist do
| exist, which is more than I can say about many other IDEs.
|
| I know that Eclipse is sometimes used more in an
| educational setting, however there are also both some
| specialized tools, as well as customized versions for
| something like working with Spring in the industry:
| https://spring.io/tools In my experience, the idea behind
| the IDE is nice (a platform that you can install whatever
| you want on, entire language support packages, or
| specialized tool packages), but the execution falls short -
| sometimes it's unstable, other times it works slow and so
| on. That said, it's passable.
|
| I would say that personally I'd almost prefer NetBeans to
| Eclipse, even after it was given over to the Apache
| Foundation, which have released a few versions since:
| https://netbeans.apache.org/ It seems to do less than
| either Eclipse or IntelliJ IDEA do, but for general purpose
| Java editing and limited work with other stacks (PHP,
| webdev stuff, some C/C++) it is good and pleasant to use.
| However, if you have projects that get close to half a
| million lines of code, it does just kind of break and gets
| way slower than the alternatives. It still somehow feels
| more coherent than Eclipse to me, would pick it if IntelliJ
| IDEA didn't exist.
|
| Some also try doing something like using Visual Studio Code
| with a Java plugin:
| https://code.visualstudio.com/docs/languages/java That
| said, I only used that briefly when I needed something
| lightweight for a netbook of mine, the experience was
| somewhat underwhelming. The autocomplete or refactoring
| wasn't as good as IntelliJ IDEA and just felt a little bit
| tacked on. Then again, that was a while ago, I don't doubt
| that progress is being made.
| fiddlerwoaroof wrote:
| I've been using Eclipse for Java recently and it's better
| than I remember it being. One thing I particularly like
| is that it has a view that's like the Smalltalk browsers
| in older smalltalk environment.
| Jtsummers wrote:
| That class browser has been part of Eclipse since the
| start or very near it (I first used it circa 2003).
| sebzim4500 wrote:
| While everyone I know moved away from Eclipse years ago, if
| you look at the stats it is still a very widely used IDE.
|
| Maybe universities still get students to use it?
| mdaniel wrote:
| I hope not, that would be malpractice
|
| However, I wanted to make the distinction that while
| @systems asked about Eclipse as a Java IDE, which it is
| objectively terrible at, Eclipse is like NetBeans in that
| it is multi-language[1] and is likely more accessible as
| an open-source C++ IDE for university use than trying to
| get CLion licenses (and, ahem, then explain CMake to
| students :-/ )
|
| 1: yes, I'm acutely aware IDEA is also multi-language but
| the open source distribution only covers Java and Python,
| unlike Eclipse and NetBeans
| Kwpolska wrote:
| CLion licenses are free for people with university email
| addresses, and perhaps you can figure out some basic
| subset of CMake to just compile a few files.
| strbean wrote:
| In my experience, university CS courses are always
| pushing woefully outdated tooling and avoiding any
| remotely modern standard practices like the plague.
|
| I was lucky to have one course in 2018 in which the
| professor actually included the most bare bones `git`
| usage.
|
| I actually never had a class in which an IDE was
| recommended. In 2008 I did have a class on C (first half
| C, second half Java) in which we were restricted to using
| C89 because that was most widely adopted in industry. I
| really missed block-scoped variables in for loops (`for
| (int i = 0...)`).
| nickcw wrote:
| This is very different to most static languages.
|
| From the article:
|
| The whole process is lazy -- only things transitively used from
| main are analyzed. Compiler won't complain about something like
| fn unused() void { 1 + ""; }
|
| This looks perfectly fine at the Zir level, and the compiler will
| not move beyond Zir unless the function is actually called
| somewhere.
| planede wrote:
| Yeah, it looks like Zig is a bit overly lazy.
|
| As a comparison, what C++ does is that it differentiates
| between "dependent" and "non-dependent" expressions, where
| "dependent" means that it depends on some template parameter
| (analogous to comptime parameter here). "non-dependent"
| expressions are type-checked eagerly, while dependent
| expressions are only checked at instantiation time.
|
| The design is certainly more complicated, even more so when you
| consider function overloading and name lookup, which can give
| different results if done late or early. But there are some
| upsides, and zig wouldn't need to deal with the additional
| complications of overloading.
|
| edit: https://eel.is/c++draft/temp.dep
| skywal_l wrote:
| Well it's basically like C macros. The C compiler won't see
| any code that's behind a deactivated macro. Same thing for
| zig here, except you don't have to deal with an unreadable
| macro language full of pitfalls. All is done in the language
| itself.
| stephc_int13 wrote:
| Getting rid of macros (and all of their well documented
| pitfalls) is good.
|
| Getting rid of the preprocessor and conditional compilation
| may not have been necessary. (as the feature is needed
| anyway and have to be implemented in a different manner)
|
| The thing is that comptime, even if better than macros
| because everything is written in the same language, might
| end up not being much better than C macros.
|
| Many of the macro pitfalls still applies, if you think
| about it...
| Kukumber wrote:
| Why should the compiler compile code that is not
| called/exported? that's a waste of processing power and
| therefore a waste of time
|
| At best it should give you a warning that the function is not
| used, that's it
| gavinhoward wrote:
| Because the parser still has to _skip_ it, especially if it
| 's in a file with code that is used. The parser needs to know
| the beginning and the end to do so. This takes at least
| rudimentary parsing.
|
| Oh, and the compiler may not know it's unused until it
| processes all files if it's a public function.
| nickcw wrote:
| I found it a surprise. However it is really useful when using
| comptime and doing those really awkward cross platform things
| (like delving into the innards of a stat_t) - your FreeBSD
| stuff with its different member names will be completely
| ignored on Linux.
| titzer wrote:
| The Virgil compiler will typecheck all code given to it on
| the command-line and then only compile what is reachable from
| main() after running initialization in the internal
| interpreter, both code and data. Unused functions and fields
| and classes will simply never be visited for codegen.
| gavinhoward wrote:
| This is my biggest complaint against Zig's comptime.
|
| I think it's possible to do comptime better, by putting
| comptime into separate files that can then "export" generated
| code.
| junon wrote:
| This is exactly how Rust's proc macros work.
| gavinhoward wrote:
| This is what I suspected.
| mdaniel wrote:
| That is starkraving insanity, and now highlights why there is a
| whole submission about making a Zig IDE. So I guess if one
| wanted to do any refactoring, be sure to go through and
| uncomment every code flow in the app first
|
| And look, I can appreciate lazy evaluation being awesome in
| some circumstance, but `fn wat() void { 1 + ""; }` just spits
| in the face of static typing
| flohofwoe wrote:
| It's pretty much a necessity for conditionally compiled
| platform-specific code if you don't have a text-based
| preprocessor with #ifdef/#endif.
|
| All statically compiled languages need to skip compiling such
| code one way or another. Zig is just thinking this idea to
| the end and doesn't compile _any_ dead code. I guess the
| recommended workflow is to have good test coverage. The test
| basically turns dead code back into live code.
| dilap wrote:
| A nice thing you get in return tho is for separate platforms
| you can disable code just in the language itself; it doesn't
| need to be a preprocessor or or handled magically in the
| build system.
|
| It's also not nearly as bad as a dynamically typed language:
| you might have broken code you won't realize until you try to
| use it, but you'll always realize at compile-time.
|
| In dynamic languages you refactor and don't notice you broke
| stuff until runtime!
| notduncansmith wrote:
| It seems like a nice balance might be a compiler config
| that accepts a list of paths to unconditionally compile, so
| that users can enforce stricter checks in their own project
| without forcing authors of their dependencies to do the
| same.
| lostmsu wrote:
| > compiler config
|
| How is this better than a line in the language itself?
| notduncansmith wrote:
| Even better
| jay-barronville wrote:
| > _A nice thing you get in return tho is for separate
| platforms you can disable code just in the language itself;
| it doesn 't need to be a preprocessor or or handled
| magically in the build system._
|
| I don't know if I consider this a nice thing when thinking
| about it from a systems language lens. When working at the
| low level, I like the immediate clarity and compile-time
| feedback that preprocessor-like checks bring. They're not
| perfect, but I think I prefer that instead of relying on
| laziness.
|
| > _It 's also not nearly as bad as a dynamically typed
| language: you might have broken code you won't realize
| until you try to use it, but you'll always realize at
| compile-time._
|
| This is correct, but to me, the elephant in the room here
| though is that this makes it much easier to introduce
| broken technical debt into a codebase. This likely means
| that when conducting large-scale refactors in a Zig
| codebase, as the amount of potentially broken code
| increases, the count of hair strands remaining in one's
| scalp decreases.
|
| Zig is an awesome language, but frankly, I think compiler
| laziness like this is pretty odd for a statically-typed
| systems language and not the behavior one would intuitively
| expect.
| dleslie wrote:
| Uncommenting a line for a platform-specific feature isn't
| really any more difficult than decorating a function,
| adding a pre-processor gate, or any of the other normal
| ways of doing it.
| dilap wrote:
| more difficult maybe maybe not, but the way zig does it
| is simpler in that it's fewer languages you have to know:
| you don't have to learn the c preprocessor (c/c++) or
| about magic +build tags (Go) or whatever; it's just
| normal conditionals in the same language.
|
| you might [reasonably] say you can't care about that kind
| of simplicity, which is fine; but radical simplicity is
| definitely one of the major charms of zig.
|
| (and you don't use the zig feature by
| commenting/uncommenting, it's via inspection of the
| environment/flags in the comptime code.)
| Kwpolska wrote:
| So how does Zig handle platform-specific stuff that
| cannot be compiled on other platforms? Is it an `if`
| resolved at compile time causing the uncompileable code
| to be unused and disappear after the Zir stage?
|
| You don't need a full preprocessor with macros and other
| evil stuff. Just #define and #ifdef would solve this
| without allowing wrong code that randomly disappears. And
| learning and understanding these two directives requires
| zero mental effort.
| TUSF wrote:
| > Is it an `if` resolved at compile time causing the
| uncompileable code to be unused and disappear after the
| Zir stage?
|
| Yes. It's exactly that, because Zig very aggressively
| evaluates values that are known at compile time. It's as
| simple as: if (builtin.os.tag == .linux)
| { // Everything here only exists if compiling
| for Linux. }
|
| The standard library uses this quite a lot for OS/CPU
| specific things.
| dleslie wrote:
| That's elegant. It's also not so different than a
| preprocessor gate, or a function attribute.
| Kwpolska wrote:
| I don't agree that it's elegant. A preprocessor directive
| or a function attribute makes it more obvious that the
| code it covers may be valid only in some conditions. On
| the other hand, if the language optimizes some `if`
| statements out, you may have invalid code without
| noticing -- and not only in the case of OS-specific code,
| but possibly also regular code whose `if` condition
| became a compile-time constant, even
| unintentionally/temporarily/by mistake.
| wtetzner wrote:
| I think it's elegant, but it seems to me that the check
| shouldn't be evaluated until after type checking. But I'm
| probably missing something about the way Zig works.
| insanitybit wrote:
| Can one guarantee this? It makes me think of constexpr in
| C++, which does something similar and it makes things
| really confusing about when something is, isn't, or
| should be constexpr (is_constant_evaluated/is_const).
| flohofwoe wrote:
| Zig not having a text-based preprocessor is a feature,
| not a bug though. From the pov of a multiplatform coder
| who's used to exclude platform-specific code with
| #ifdef/#endif, Zig's behaviour is completely logical
| (because that's the only way it can work without a
| C-style preprocessor).
| stephc_int13 wrote:
| Yes, this is the logical way of doing it without
| preprocessor.
|
| At the end of the day, this is pretty equivalent. Not
| sure if this is a net gain or not.
|
| Conditional compilation is needed, moving the feature
| from the preprocessor stage to a lazy evaluator is
| probably costlier for the compiler and it might also be
| costlier for the IDE to highlight active/inactive parts
| of the code.
|
| From the user point of view, this is very close, mostly a
| cosmetic change.
| dleslie wrote:
| If you're already concerned with, and developing,
| platform-specific code then the extra knowledge isn't
| really that hard to grasp.
|
| Moreover, I can grep for preprocessor defines,
| attributes, and such by name.
|
| There's no way to grep a project for code commented out
| to prevent execution on some platforms.
| dontlaugh wrote:
| It's not necessarily simpler than an explicit hygienic
| macro system or similar, since comptime is a complex
| feature with surprising consequences. Mostly it moves the
| complexity around.
| throwawaymaths wrote:
| It's kind of not. One way to think of it is roughly as
| follows: Zig is statically typed at runtime. It's dynamically
| typed at compile time.
|
| Anyways it's not entirely crazy to think that maybe this sort
| of thing should be the province of static linters
| nusaru wrote:
| Was hoping the author found a solution to (in my view) Zig's
| biggest shortcoming with generic types: a parameter that accepts
| all instances of a generic type must either (a) be declared as
| "anytype" or (b) also require a comptime parameter for the
| generic type argument. For example, the standard library passes
| around instances of std.io.Writer as anytype, so zls auto-
| completion doesn't work.
|
| Also see: comptime interfaces proposal
| https://github.com/ziglang/zig/issues/1268
| Kukumber wrote:
| I agree with that, the language needs some love in that area
| remorses wrote:
| Interfaces are the most important thing Zig needs to implement
| ASAP
|
| It would also help creating a uniform standard library, for
| example creating a Writer interface instead of having specific
| functions for files, sockets, buffers and all kind of types
___________________________________________________________________
(page generated 2023-02-10 23:00 UTC)