[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)