haskell / haskell-ide-engine

The engine for haskell ide-integration. Not an IDE
BSD 3-Clause "New" or "Revised" License
2.38k stars 212 forks source link

Question: relation between HIE and build tools like Cabal and Stack #1667

Closed mouse07410 closed 4 years ago

mouse07410 commented 4 years ago

I'm trying to understand the role HIE plays in the build process (initiated by an IDE), and the importance of matching the build tool (Stack or Cabal) that the IDE would use to actually build the project, to the tool specified in the hie.yaml file. And the consequences of the mismatch: e.g., the IDE would invoke Cabal, but HIE thinks the project uses Stack.

I thought that HIE is basically an assistant to an IDE, pointing the IDE at potential problems with the project sources. So, why does it matter whether HIE makes the same assumption about what build tool to use, as the IDE does? And what would happen if there is a mismatch?

fendor commented 4 years ago

The IDE does nothing to actually build the project. An IDE is the lsp-client, such as vscode that makes use of an lsp-server, in this case HIE. HIE is responsible for producing diagnostics and code actions to fix problems. To produce diagnostics, we essentially have to compile the project ourselves to obtain relevant data, such as locations, type definitions, warnings and compilation errors. The IDE, for example vscode, does not build anything. It receives messages from HIE and displays them to the user and offers some interaction with HIE. Therefore, there can be no disagreement between IDE and HIE regarding the build-tool. The build-tool matters, since each build-tool might build a project differently. So, using the same build-tool as the user is always important, so that the experience matches, since it is possible that compliation fails under stack but succeeds with cabal.

If there s a mismatch between what you use for building and what HIE uses for building, then it might be possible that HIE shows errors in the sources, while on the command line everything seems to work fine.

mouse07410 commented 4 years ago

...So, using the same build-tool as the user is always important, so that the experience matches, since it is possible that compliation fails under stack but succeeds with cabal.

Ah, I understand. Is this the biggest/the only concern? That a project may show errors while the "other" build tool (i.e., invoked via command line) would succeed, or not show errors while the command-line build could fail (because the other build tool may have issues that HIE doesn't receive)?

Thank you!!

fendor commented 4 years ago

There are more differences, how we access dependencies that we need, how to obtain documentation for these modules, etc... In general, project management is very different with these tools. Imagine using package.yaml but HIE selects cabal for building the project, then package.yaml gets updated, but changes in dependencies would never be propagated to HIE. Same issue with compilation flags, e.g. you can specify in stack.yaml flags for dependencies, such as features.

However, if both build tools are supported equally by a project, it becomes a matter of preference. Some people do not want to build with stack, so there is a way to specify to always use cabal for building (and the other way around). For example, I struggle with stack, probably because of nixos, therefore I prefer cabal, even if there is a stack.yaml. With hie.yaml it is possible to tell that HIE in a reliable way.

mouse07410 commented 4 years ago

Perfect, thank you! I think I understand now, even to the point of how to bring HIE and the other project build config in sync. Might need to ask more later, though - Haskel ecosystem is not a simple matter! ;-)

jneira commented 4 years ago

Haskel ecosystem is not a simple matter

It is not, but not so different to other ones: think java has ant, maven and gradle, the c tool chain or javascript. Haskell has one compiler (for better or worse) and stack and cabal share the same formats underneath (hackage, .cabal config files,...).

mouse07410 commented 4 years ago

Haskel ecosystem is not a simple matter

It is not, but not so different to other ones...

To me the biggest huge difference/shock of how Haskell eco differs from most every other one is how fragile/unstable the interfaces are, and how easily a build gets broken unless everything is exactly at the original versions (which isn't very friendly on bug fixes). So, my builds are repeatable only as long as I'm willing to maintain exactly the same toolchain and all the dependent packages at their original (unfixed!) version.

There's no other ecosystem where I can't take a package and recompile it under a new compiler and link it with a bunch of libraries, some of which are new. Sure, sometimes incompatible changes happen. But that's rather an exception than the rule. For example, OpenSSL (a large popular package) made a decision to break compatibility with versions older than 1.1. That was huge - but it was a one-time event, and it's been argued about ad nauseam. In Haskell, it looks like a norm, and I should consider myself very lucky if allow-newer: true still builds a working executable. In other ecosystems (C, Java) I consider myself rather unlucky if a newer version of the same dependent package doesn't blend nicely into my build - and so far it almost never happened. <sorry for the /rant>

fendor commented 4 years ago

I mean, have you seen python? There was quite the hassle because of version 2 and 3.

Moreover, you are rather describing the workflow with stack, it is different with cabal in my experience.

In other ecosystems (C, Java) I consider myself rather unlucky

I had terrible experience with java and maven. Finding a build-plan (if you are not using straight spring-boot-start) manually is close to impossible and very annoying, since it mostly on-startup exception traces. That is something that will never happen with stack because they do it all for you.

mouse07410 commented 4 years ago

I mean, have you seen python? There was quite the hassle because of version 2 and 3.

Ah, yes. I'm not doing a lot there, but yes - you're quite right, it was a mess.

However, that was a one-time breaking change - moving from one huge pile to another. And so far Python-2 is still maintained. With Haskell it's every time something changes. I can't complain enough about how much frustration Network, Network-BSD, andHTTP-4.13(or was it...-3.14` ;) caused me.

I had terrible experience with java and maven. Finding a build-plan (if you are not using straight spring-boot-start) manually is close to impossible and very annoying, since it mostly on-startup exception traces.

I hear you. I don't use Spring myself, so can't comment - but so far everything I pulled from Maven Central just happened to work with whatever code I was writing... So far, at least... ;-)

That is something that will never happen with stack because they do it all for you.

Well, stack would simply refuse to build, unless you allow it to download the entire toolchain of the required version, and all the dependent packages with their dependencies at those versions. And then the build would most likely fail because there's no way to tell stack to pass all the compiler flags all the way down the chain to every dependent package it re-builds. So, if your machine happens to have other package manager(s) running (like Macports, which in most aspects is better than Brew), and they install, e.g., libiconv somewhere (and of course their version of libiconv.dylib differs from /usr/lib/libiconv.dylib) - then the compiler would fail to resolve the symbols it expects there. It was a royal pain to get through this, and quite a few GitHub issues (and IRC blobs) were involved.

But I see that nowhere the grass is truly green. ;-) :-)

mouse07410 commented 4 years ago

I guess I should close this issue - you answered nicely all of my questions (and even tolerated a rant! ;).