How do I disable version parsing in cabal or stack? - haskell

I am using alternative version numbering approach for my projects. I have encountered strange behavior by cabal and stack that does not allow me to fully enjoy benefits of this approach. Both cabal and stack enforce version to be of format Int.Int.Int, which does not cover the case of another version format I use for branches (0.x.x, 1.x.x, 1.0.x, etc).
If I have line version: 0.x.x in my .cabal file, I am getting Parse of field 'version' failed. error when running cabal build or Unable to parse cabal file {PROJECT_NAME}.cabal: NoParse "version" 5 when running stack init.
Is there a way to disable version parsing on cabal and stack commands? Is there a flag for it? Or do I have to request this kind of change (adding flags, disabling version parsing) from the developers of cabal and stack?
Why is there any parsing at all? How does it help with building a package? Does cabal or stack automatically increment build numbers on some event? If yes, where could I read more about this? How could I influence the way version numbering incrementation gets implemented in cabal and stack? I want developers of haskell packages take into account the possibility of alternative version numbering approaches.
PS. For all interested folks, I want to quickly summarize the idea behind "weird" version numbers, such as 0.x.x, 1.x.x, 1.0.x. I use the version numbers with x's to describe streamlines of development that allow code changes while such version numbers as 1.0.0, 1.1.0, 2.35.46 are used to describe frozen states of development (to be precise, they are used for released versions of software). Note that such version numbers as 0.x.0, 1.x.15, 2.x.23 are also possible (used for snapshots/builds of software) and they mean that codebase has been inherited from branches with version numbers 0.x.x, 1.x.x and 2.x.x correspondingly.
Why do I need such version numbers as 0.x.x, 1.x.x and 2.x.x at all? In brief, different number of x's mean branches of different types. For example, version number pattern N.x.x is used for support branches, while pattern N.M.x is used for release branches. Idea behind support branches is that they get created due to incompatibility of the corresponding codebases. Release branches get created due to feature freeze in corresponding codebase. For example, branches 1.0.x, 1.1.x, 1.2.x, ... get created as a result of feature freezes (or releases) in branch 1.x.x.
I know this is all confusing, but I worked hard to establish this version numbering approach and I continue working on awareness about the inconsistencies of version numbering through my presentations and other projects. This all makes sense once you think more about the pitfalls of semver approach (you can find detailed slideshare presentation on the matter following the link). But I do not want to defend it for now. For the time being, I just want cabal and stack to stop enforcing their, as I perceive them, unjustified rules to my project. Hope you can help me with that.

You can't. The version will be parsed to Version, which is:
data Version = PV0 {-# UNPACK #-} !Word64
| PV1 !Int [Int]
Stack uses Cabal as a library but has its own Version type:
newtype Version =
Version {unVersion :: Vector Word}
deriving (Eq,Ord,Typeable,Data,Generic,Store,NFData)
Neither cabal nor stack have a way to customize the parsing. You have to write your own variant of those programs if you want to use another version type. But then again, you're not winning anything at that point: neither Hackage nor Stackage will recognize your package's version.
So the 1.x.x isn't possible at the moment. You could exchange x with 99999999 or something similar to mitigate the problem. That being said, it's not clear what cabal install should then install. The 99999999 version? Or the latest stable variant?
If you can express the semantics, a discussion on the mailing list as well as a feature request might change the behaviour in the (far away) future, but for now, you either have to patch the programs yourself or use another numbering scheme.

Is there a way to disable version parsing on cabal and stack commands? Is there a flag for it?
No.
Or do I have to request this kind of change (adding flags, disabling version parsing) from the developers of cabal and stack?
You can of course ask, but there are so many outstanding issues that you are unlikely to get any traction. You will have to be very convincing -- convincing enough to overturn more than 20 years of experience that says the current versioning scheme is basically workable. Realistically, if you want this to happen you'll probably have to maintain a fork of these tools yourself, and provide an alternative place to host packages using this scheme.
Why is there any parsing at all? How does it help with building a package?
Packages specify dependencies, and for each dependency, specify what version ranges they work with. The build tools then use a constraint solver to choose a coherent set of package/version pairs to satisfy all the (transitive) dependencies. To do this, they must at a minimum be able to check whether a given version is in a given range -- which requires parsing the version number at least a little bit.
Does cabal or stack automatically increment build numbers on some event? If yes, where could I read more about this?
There is nothing automatic. But you should take a look at the Package Version Policy, which serves as a social contract between package maintainers. It lets one package maintainer say, "I am using bytestring version 0.10.0.1 and it seems to work. I'm being careful about qualifying all my bytestring imports; therefore I can specify a range like >=0.10 && <0.11 and be sure that things will just work, while giving the bytestring maintainer the ability to push security and efficiency updates to my users." without having to pore through the full documentation of bytestring and hope its maintainer had written about what his version numbers mean.
How could I influence the way version numbering incrementation gets implemented in cabal and stack?
As with your previous question about changing the way the community does things, I think modifications to the Package Versioning Policy are going to be quite difficult, especially changes as radical as you seem to be proposing here. The more radical the change, the more carefully motivated it will have to be to gain traction.
I honestly don't know what a reasonable place to take such motivation and discussion would be; perhaps the haskell-cafe mailing list or similar.

Related

How do indicate that a Haskell package is in either an alpha/beta/release candidate stage?

Let us say that I have worked on a haskell library and am now ready to release a beta version of the software to hackage/make repo public on github etc.
Possible Solutions and why they do not work for me
Use packagename-0.0.0.1-alpha or similar.
The problem here is quite simple: The Haskell PVP Specification does not allow it: (bold is me)
The components of the version number MUST be numbers! Historically Cabal supported version numbers with string tags at the end, e.g. 1.0-beta This proved not to work well because the ordering for tags was not well defined. Version tags are no longer supported and mostly ignored, however some tools will fail in some circumstances if they encounter them.
Just use packagename-0.* until it is out of alpha/beta (and then use packagename-1.*).
The problem here is twofold:
This method would not work for describing relase candidates which are post version 1.
Programmers from other ecosystems, such as that of rust, where it is quite common to have a stable library in 0.*, might wrongly assume that this library is stable. (Of course, it could be mitigated somewhat with a warning in the README, but I would prefer a better solution still.)
So, what is the best (and most conventional in haskell) way to indicate that the library version is in alpha/beta stage of development or is a release candidate?
As far as I know, there is not a package-wide way to say this. However, you can add a module description that describes the stability of the module to each of your modules' documentation.
{-|
Stability: experimental
-}
module PackageName.ModuleName where

Can I somehow use a package with a later version of base than what the package specifies?

I'm quite new to the Haskell stack, so I might be misunderstanding how things are intended to work here, but I have a problem that I've seen a few times and am wondering if I'm going about the wrong way.
In short, I sometimes want to use a package with a version spec that is capped at a lower version of base than what my Stack resolver includes.
For example, I can't use the lts-12.14 resolver with PSQueue-1.1, because the former includes base-4.11.1 and the latter requires base >=4 && <4.11.
The two ways I have found to resolve that has been to either
add a specific version of base to my extra-deps, making sure that the pinned version is within the range my package allows; or
choose a different resolver (using e.g. https://www.stackage.org/diff/ to figure out which one is the latest resolver with an early enough version of base)
Both of these feel suboptimal, especially since I might want to use packages which have non-overlapping ranges (e.g. one >=3 && <4.11 and one >=4.11). I realize that using such a combination toghether might fail, especially if they are locked on different major-versions of base (assuming base uses semver), but so far I'm only writing pretty small programs, so if they seem to work I'm quite happy even if there are other, non-exercised code paths that will fail on the specific combination of packages and versions. In other words: I know the risk - but I can't chance it, because I don't know how to.
Is there a way to force Stack to allow a newer version of base than the one specified in the requirements of a dependency?
I think you are looking for allow-newer - https://docs.haskellstack.org/en/stable/yaml_configuration/#allow-newer
Ignore version bounds in .cabal files. Default is false.

What is Cabal Hell?

I am a little bit confused while reading about Cabal Hell, as the term is overloaded. I guess originally Cabal Hell referred to the diamond dependency problem, which was solved by restricting the build plan to have only a single version of any package in each build plan (two different versions of a package can't exist in a single build plan) as explained in this answer.
However, the term is also used in various other contexts. Such as destructive re-installations, incorrect package dependency boundaries (lower/upper version bounds), inconsistent environments ... (or any other error reported by Cabal).
Particular among these, I am confused about 1) destructive re-installations and 2) inconsistent environments? What do they mean, and how cabal new-build solves these problems (is it just sandboxing like cabal sandbox)? And what role ghc-pkg has to play here?
Any references or a simple example where these problems could be reproduced would be very appreciated.
Regarding "destructive re-installations": If I am not wrong, GHC has a package manager of itself (ghc-pkg), and the packages are installed as dynamically linkable libraries i.e: base depends on ghc-prim, so if ghc-prim is removed it will break base, am I right? And since GHC only allows one instance of a package with the same version, cabal install might register a newer build of the same (package, version) such that it breaks the dependents of the unregistered package. If the above understanding regarding "destructive re-installations" are correct; how does cabal new-build help here?
The only meaningful use of the term is the one given in the linked answer. Related are the follow-on problems from having lots of different packages in the global database, which can make encountering diamond dependencies more common, requiring destructive reinstalls to resolve, etc.
The other usages of the term are not helpful and just mean "problems somehow involving cabal."
That said, let me answer your other questions.
1) ghc-pkg is not a package manager, but rather a tool for managing ghc package databases. It is used by cabal to register packages into databases, and can be used by end-users to inspect the contents of the databases. Think of it as part of the underlying substrate provided by ghc, not a competing tool.
2) new-build eliminates and replaces the standard notion of a packagedb entirely. Instead of saying that a db consists of packages and versions, with at most one of each pair, instead a db consists of potentially many copies of packages at any given version, each with potentially different versions of its dependencies, all of which are managed in part by hash-addressing, so marked by a unique "fingerprint". This is called the store. When you new-build, cabal calculates a build plan irrespective of any previously installed dependencies, from scratch. If a particular fingerprint (consisting of a package, version, and the versions of all its dependencies, certain flags, etc) already exists in the store, then it makes use of it. If it does not, it calculates it.
As such, the only "diamond dependencies" that can occur are the truly insoluble ones, and not the ones occasioned by having fixed too-early (due to already-installed deps) some portion of the dependency tree.
tldr; you write "since GHC only allows one instance of a package with the same version" but new-build partially lifts this restriction in the store which allows the solver to produce better, more reproducible plans more often.

Haskell Environment

I recently finished the book "learnyouahaskell" and now I would like to apply my acquired knowledge by building a yesod application.
However I am not sure about how to get started.
There seems to be two options on how to set up a yesod project. One is with Stack, and the other is with cabal sandboxes.
But what are the differences (If any?) and the similarities between them? Does one count as best practice whereas the other doesn't?
The yesod quickstart suggests using stack, is this fine or should I use cabal sanbox?
There are actually three different packages that are being talked about here.
cabal-install is the current stable binary to build your applications.
stack was just released to the public recently. I believe it is trying to replace cabal-install as a better, more convenient tool. At the very least, it is showing the Haskell community a different way of something things.
Cabal is the library that both cabal-install and stack are based off of.
As for the differences between the first two tools.
cabal-install is a mature application used nearly everywhere within the Haskell community (at least in open source, I have no idea what people are doing behind closed doors).
stack is still a new (at least to the public) application used in some newer projects. Some more information can be found here. But some of the highlights are:
running stack build in a projects directory will install GHC (Haskell compiler) as well as the needed dependencies for the project.
stack, by default, runs off of stackage. Which is a curated version of hackage. Meaning you can expect the different packages to play nicely with each other. Leading to reproducible builds.
You can still fall back on hackage should you choose to.
The great thing about these two applications is that they can be used by different people for the same project. If you decide you want to use cabal-install with sandboxes, and someone comes along and wants to help with your project, they can just add the files that stack needs and they can use stack while you continue to use cabal-install. Or vice-versa.
here is one persons experience after using stack for the first time. They claim that it is a little bit easier to get started because there are a couple less steps required to get started. If nothing else, people highlight the pros and cons of each tool.
Note: I'm still fairly new to Haskell, and have never actually used stack. I've actually been told to stay away from it unless building something in yesod.
Edit: As stated in a comment under this answer, I believe I have mis-represented what people have told me about stack. The comments people have given me when I asked if I should switch over to stack were more along the lines of, If you are comfortable enough using cabal sandboxes, there is no reason to switch over to stack unless you are having issues.
The lead developer of Yesod (Michael Snoyman) is actively involved with the Stack tool. So, I would recommend you to set up Yesod with Stack. Also, Yesod has a quite a complex set of dependencies and using Stackage as the default curated source, helps very much in the installation process (which Stack takes care of by default).
Also read this post for understanding the differences of Stack from cabal.

Are there tools that would be suitable for maintaining a changelog for a Cabal Haskell package?

I'm working fast and furiously on a new Haskell package for compiler writers. I'm going through many minor version numbers daily, and the Haskell packaging system, Cabal, doesn't seem to offer any tools for updating version numbers or for maintaining a change log. (Logs are going into git but that's not visible to anyone using the package.) I would kill for something equivalent to Debian's uupdate or dch/debchange tools.
Does anyone know of general-purpose tools that could be used to increment version numbers automatically and add an entry to a change log?
I use a very simple scheme to generate my CHANGELOG. I just ask darcs for it and include it in the extra-files section of my package's .cabal file. Though, this seems too simplistic for what you are asking. =)
That said, you can go quite a bit farther and use a custom cabal Setup.(hs|lhs) that builds the CHANGELOG during cabal sdist out of your darcs or git repository's commit info (or out of whatever system you decide to use to track it)
The Setup.lhs used by darcs does something very similar to include information on version numbers and number of applied patches since the last version. Look at the sdistHook and generateVersionModule machinery in Setup.lhs to get an idea of how this can be done.
To non-answer your question, I'm not aware of anything. This sounds like a good match for posting in the Haskell Proposals subreddit, since it seems like a pretty useful idea.

Resources