haskell / cabal

Official upstream development repository for Cabal and cabal-install
https://haskell.org/cabal
Other
1.62k stars 697 forks source link

Beginner friendly cabal-install CLI #5696

Open m-renaud opened 6 years ago

m-renaud commented 6 years ago

Overview

Provide the most beginner friendly cabal-install CLI possible. This means that the cabal commands by default (read: no arguments) should do the thing that beginners expect to get them to do, from zero to a Haskell program they can run.

Note: This is related, but an expansion in scope from https://github.com/haskell/cabal/issues/5661

Example “Hello World”

Here are the commands that a beginner should need to run to get a working “Hello World” Haskell program that will build and run now, and will always build in the future, regardless of global system changes (like ghc version updates):

mkdir myfirstproject && cd myfirstproject
cabal init
cabal run

That’s it. No interactive prompt asking them a bunch of questions about licences; if they want a library, executable, or library and executable; if they want a separate src directory; base language; or anything else.

Whose developer experience does this improve?

Beginners. Let’s make it as easy as possible for someone new to the language to get started and hacking on a Haskell program. The easier the onboarding, the better the adoption rate. You could argue that you could invoke ghc directly but you quickly need to start using cabal when you want to start using other packages, so let’s not make them learn something new twice.

Casual users. I often find myself with some idea I want to play with and just want the simplest way to get a basic project up and running. The fewer commands and CLI arguments I need to remember the better.

”But what about the power users?”

I’ll make the same point I made in #5661, if you’re a power user, you know your way around cabal anyways. If you want to create a package interactively you can run cabal init -i (or --interactive), or maybe just specify the specific arguments you want to cabal init yourself. We should make the simplest experience the easiest in order to appeal to the widest audience possible.

This old default behaviour can also be re-enabled by setting interactive-init: True (module bikeshed) in ~/.cabal/config for those that want the old interactive-by-default workflow.

Changes

Let me first present a summary of the changes, I’ll explain the rational of them below.

Default to non-interactive

When someone gets started with Haskell and initialize a project they’re confronted with a large list of questions about what licence they want; what category their package falls into; if they want a binary, executable, or both; if they want their main as a .hs or .lhs file; where they want their source directory to be, and what “base language” they want.

To a beginner, the majority (IMHO, all) of these questions are confusing at best, all they really want is to start hacking on a Haskell file. By defaulting to non-interactive, the base command gives them what they expect, a minimally configured package that they can run, edit a file, and run it again.

The existing interactive behaviour would still be accessible via an --interactive, -i flag. If you’re already familiar with cabal then learning one new flag for interactively creating a package is incredible low overhead. Sure, you may need to change your workflow slightly, but I think that’s a pretty low cost to pay for making a beginner's experience better.

Additionally, the old default behaviour can also be re-enabled by setting interactive-init: True (module bikeshed) in ~/.cabal/config for those that want the old interactive-by-default workflow.

(alternative) "Would you like to initialize a simple project?" as first questions

Note: This was implemented in #5707.

From the suggestion from @gbaz below, another option is to keep --interactive as the default, but move the "What type of project?" question to be the first question, and add a new choice "Executable (Simple Project)" as the default which will skip the rest of the interactive menus and create a bare bones executable.

Default to executable (with library)

Once again, to offer a simple default initialization behaviour what we really want is a binary. This is actually inline with what Rust switched to in version 1.25 (blog, see “Cargo Features” section), and they made an incredibly good observation:

”Cargo’s CLI has one really important change this release: cargo new will now default to generating a binary, rather than a library… If you don’t pass one of these flags, in previous versions of Cargo, it would default to --lib. We made this decision because each binary (often) depends on many libraries, and so the library case is more common. However, this is incorrect; each library is depended upon by many binaries. Furthermore, when getting started, what you often want is a program you can run and play around with. It’s not just new Rustaceans though; even very long-time community members have said that they find this default surprising. As such, we’re changing it.”

~Since its good practice to place all of the actually code in a library and have a "dumb main" to ease testing and allow for usage outside of your executable, we initialize a library and executable.~

Initializing both a library and executable with reasonable defaults and directory structure is now handled by the "simple project" initialization, cabal init --simple.

cabal init pins the ghc version for executables

For the full discussion see #5661.

In summary, not pinning the ghc version can result in a working build one day failing to build another day due to a change in global system state (such as the ghc on PATH).

cabal run is cabal v2/new-run

In summary, we want the “new build” to be the default for all the reasons outlined here. This is planned for the cabal-install 3.0 release.

(minor) --exe and --lib as shorthands

This obviously isn’t critical, the current versions are just really verbose and not inline with other tools.

Backwards Compatibility

Default to non-interactive

In theory this is not backwards compatible since previously running cabal init with no arguments would drop you into interactive mode, but after the change it would select sane defaults for you.

In practice, this mode would not be used in scripts anyways so this is unlikely to cause issues for any automation, since those scripts would explicitly spell out the arguments to cabal init.

If in the past you relied on the default interactive behaviour when manually creating a package you can either use the new --interactive (or -i) flag, or just edit the .cabal file manually.

Default to executable

Technically not backwards compatible. The current cabal init -n behaviour creates a library, so anyone who previously ran the command in non-interactive mode without manually specifying an --is-xyz flag would get different behaviour. I would imagine this isn’t a common case, and those that were relying on the behaviour could slightly adjust their workflows.

cabal init pins ghc-version

See discussion in #5661.

cabal run is cabal v2/new-run

I don’t think there’s anyone who doesn’t want this. The backwards compatibility of that switch is out of scope here.

(minor) --exe and --lib as shorthands

Simply introducing new aliases, nothing breaking here.

Timeline / Milestone

I would like to target the cabal-install 3.0 release since this will already include substantial changes (namely, defaulting to new-build). If we're going to make "breaking" changes to the CLI it makes sense to go out in the next major version.

Edit History

2018-11-20 9:15PST

2018-11-20 17:30PST

2019-01-20 11:40PST

/cc @23Skidoo @hvr

gbaz commented 6 years ago

I think that there's a much simpler approach. Just have the first question be "What type of project do you want to initialize" and give the current 3 options, but first the option "executable (simple project)" or the like. And if they pick option 1, then it just initializes everything noninteractively.

Then we don't need different modes or flags or anything, but beginners also still get a very streamlined workflow.

m-renaud commented 6 years ago

@gbaz I like that idea as well, although if we anticipate that case to be the most common why have the interactive prompt the default at all? Also, doesnt automatically initializing everything non-interactively for executables removes the ability to, well, interactively initialize an executable? 😜 You would still need to manually edit the .cabal file. I think the interactive initialization by default is something that I've only seen with cabal-install, do you know the reason why this was chosen as the default?

gbaz commented 6 years ago

I don't anticipate that case to be the most common. I think it is only common for beginners. Also we would keep the ability to initialize an executable interactively -- it's on the "simple project" option that would be a new option that wouldn't have it.

I think it was chosen as the standard way to do things because it is nicer than editing in all the details manually, and I personally always like having that control over initialization.

m-renaud commented 6 years ago

@gbaz That makes sense, thanks for the clarification about the separate option :) What would you think about having a --simple flag to emulate that behaviour as well? I feel like anything that is selectable from an interactive menu should have a CLI option as well.

Edit: words

m-renaud commented 5 years ago

Actually, to be more consistent with the other options (and assuming --exe and --lib shorthanded we're added) then they option could be --simple-exe.

cabal init --simple-exe

Edit: Final flag name is --simple.

erewok commented 5 years ago

Hello,

I commented on the reddit thread, but there are two opinions I have about this proposal:

I would love to see these changes make into cabal along with promoting all the new-* style commands to replace their originals they supersede.

23Skidoo commented 5 years ago

/cc @byorgey

byorgey commented 5 years ago

@23Skidoo thanks for the cc. I think defaulting to non-interactive makes a lot of sense.

hvr commented 5 years ago

As a general comment: I've seen this in lots of areas (most prominently in Haskell is the "beginner Preludes" debate -- and luckily we didn't give in there) and I disagree with the idea that we should dumb down tools for the sake of beginners. I want tooling to have defaults optimized for the majority of users, and that's certainly not beginners. And I disagree with the often cited argument that power-users already know their way around a tool and it's ok to make things inconvenient for those, as this effectively means that you're penalizing power-users which is a state with a much higher TTL than the state of a beginner, which is expected to last only a relatively short amount. So from the POV of an utilitarian metric it's a lot more sensible to optimize for the larger population group which will be dealing with the tool for eternity rather than give more privilege to a smaller population group which is transient by nature and will soon grow into the former group. In short, I strongly disagree with the premise that cabal's CLI ought to do what ephemeral beginners expect, cause if we go down that road cabal's UI will become maladjusted to everyone else who'll have to deal with cabal long after they stopped being beginners.

Specific to the issue at hand: IMO cabal init's distinguishing feature compared to other UIs I've seen is the interactive questionnaire mode which I consider to be also beneficial to beginners since being interactive offers additional ways to educate new users about features of the cabal spec. If we stop defaulting to it, we're basically making it less discoverable and implicitly stating that we don't consider it a good enough UI to place it front and center. And if that's the case, maybe we should rip it out from cabal init altogether.

m-renaud commented 5 years ago

Thanks for the thorough and thoughtful reply @hvr! I'd like to break down your comment a little bit, and since the comments have been split between here and the PR (#5902) I'll bring in a few comments so future readers can get the full picture without jumping around.

hvr: I disagree with the idea that we should dumb down tools for the sake of beginners ...the often cited argument that power-users already know their way around a tool and it's ok to make things inconvenient for those

I completely agree, if the the addition/change of a new feature to benefit one group permanently makes the experience worse for another group then that decision should not be taken lightly (or be done at all). That being said, I have a hard time believing that defaulting to non-interactive init falls into this category though, since changing one line in your config file gets you back the exact behaviour that existed before.

hvr: the premise that cabal's CLI ought to do what ephemeral beginners expect

This change (although framed specifically in this issues regarding beginners) does not only affect beginners, any time you want to create a package as a playground for some idea all that you care about is having a package initialized so you can start hacking on code.

hvr: IMO cabal init's distinguishing feature compared to other UIs I've seen is the interactive questionnaire mode which I consider to be also beneficial to beginners

I agree that the interactive init does to some degree serve a purpose for education, but the benefit you get from that is only occasional since you may run it once the first time you encounter cabal, and then many other times you're likely going to want to skip it anyways. I think a viable alternative is to encourage users to run cabal interactively if they want an interactive tutorial of the features of the cabal spec (more on this later).

Also, the questions that are asked in the interactive init aren't exactly "educational" either, take even the first question: "Please choose the Cabal specification to use", followed my mentions of internal sub-libs, Backpack, redundant commas, SPDX, etc. the majority of which will do nothing but introduce confusion (and the default is 1.10 which they probably isn't the best to suggest). Many of the other questions assume that the thing you're starting on is going to be going on hackage (License, author/maintainer name, email, homepage URL, project category, etc.) which is in the minority of all packages that are initialized.

So, this is to say that the idea of "interactive cabal init as an educational tool" isn't very convincing to me (at least in its current implemented form). This is obviously only anecdotal, but ~every non-Haskeller I've showed the interactive init prompt to found it unnecessarily complicated and inconsistent with tools of other languages.

To address the comments from @harpocrates and @gbaz in the PR:

harpocrates: Beginners don't usually start by making packages

I don't think this is true, its almost impossible to do anything in Haskell without pulling in a few packages, and the easiest way to do that is to create a project with an executable, add the packages you want to build-depends and then invoke cabal v2-run. IMHO this is one of the lowest friction ways to get up and running with any language I've used. If all you're doing is invoking ghc directly then you don't need a package, but then you also don't need cabal at all either ;P

harpocrates: the current interactive mode isn't that scary

Agreed, I don't think scary is how I would describe it either, but it introduces additional friction/steps to get from nothing to a binary that you can hack on. See my reply above about confusion introduced by some of the questions.

gbaz: I think that it might be nice to more clearly highlight as part of interactive mode that there is a flag to skip it in the future

That's a great idea. I think if the conclusion that is finally reached is that interactive remains the default then this should be implemented before 3.0 is released.

Another thing I'd like to mention is I didn't plan on this change silently going out. I was hoping to publish something when cabal 3.0 becomes widely available to outline the changes to the CLI and provide info about how to get back the old behaviour, why users would still want the interactive version, etc. I'd also update the Getting Started section in the Cabal User Guide as well as the Cabal website to include this new information (and a suggestion to run interactive init to get a sense of what other options are available).

It may also be beneficial to run a poll which asks the wider community which default behaviour they would expect/desire. Obviously this wouldn't be the deciding factor but its always helpful to have more concrete data to make a decision. I can set one up if folks think that would be beneficial.

byorgey commented 5 years ago

I can see the arguments for both sides, but overall I think I come down on the side of wanting non-interactive to be the default. I'm pretty sure I don't count as a beginner =) but I think most of the time I would want that instead of the interactive version. Often I find myself typing cabal init but then hitting Enter a bunch of times repeatedly because I don't really care, I just want to spin up a little throwaway project. I don't have much to add in the way of general arguments but I think @m-renaud has given some convincing, well-thought-out reasons and responses to the (also thoughtful) objections.

However, I'd also like to make the meta-point that the specific choice made here probably does not matter all that much in the long run, and given that, even if I disagreed slightly, I'd still prefer to go along with the vision of someone like @m-renaud who has put a lot of thought into it and seems obviously committed to improving the tool and the entire experience, documentation, etc. surrounding it. It would be very different if someone just threw up a random PR and then disappeared.

gbaz commented 5 years ago

@byorgey are you usually spinning up throwaway exes or libs? one of the reasons i'm a bit leery is for me the common case is libs, not exes, which I know isn't necessarily common for everyone, but...

Additionally, your arguments seem to be making the case more strongly for non-beginners wanting non-interactive, which I agree is stronger. My concern is that the default is motivated as being the beginner-friendly one -- and for that, I think that interactive, which actually teaches beginners things, is clearly better. I.e. the first time someone goes through the whole process its good that they see what the options are, and only after they know them well will they just find themselves "hitting Enter a bunch of times repeatedly."

m-renaud commented 5 years ago

is for me the common case is libs

cabal init --lib is pretty short and in line with almost every other tool that I've ever used. Also, if you're currently using the interactive prompt to create a throwaway lib, that is much more work than this is.

your arguments seem to be making the case more strongly for non-beginners wanting non-interactive

I think there's a strong case for non-interactive being a better default for both beginners and non-beginners. I originally presented it as being more "beginner friendly" (which I still think it is), but even personally I would prefer non-interactive by default as a more seasoned user of cabal.

I think that interactive, which actually teaches beginners things, is clearly better

IMHO there are better avenues to teaching these things (better intro/quickstart docs for example) than having it be the default behaviour of a tool which in arguably the wrong default for the majority of users. I'd also wager that many of the things that it "teaches" are irrelevant for what they're attempting to do if they're just getting started with Haskell and just want a file to hack on. Questions about the licence, maintainer email, project homepage, Hackage category, base language (why would they want Haskell98?), etc. aren't relevant at this point in their Haskell learning experience. Of course the docs could encourage new users to run cabal init --interactive/-i to see what options are available so they know its there in the future.

Having to blindly "hit Enter a bunch of times" seems like a workflow that should never be the one presented to users. Is it a show-stopper? No, but the ergonomics are really bad.

In any case, the PR to make init non-interactive has been open for almost a month now, how can we get a decision made on this?

phadej commented 5 years ago

So it's not forgotten: non interactive input should not populate license field.

phadej commented 5 years ago

Let's solve this as we solve everyhing else: The defaults have to be configurable in ~/.cabal/config.

E.g. I'd want an option that cabal init would ask me for a license, and nothing else.

m-renaud commented 5 years ago

non interactive input should not populate license field.

Acknowledged, it does not :)

I'd want an option that cabal init would ask me for a license, and nothing else.

That's what cabal init -i is for, if you want to be asked questions, otherwise everything is taken from the config file. You can also specify the licence in the init section of ~/.cabal/config:

init
  interactive: False
  cabal-version: 2.4
  license: BSD3
  language: Haskell2010

Then when you run cabal init (non-interactive) it will create a BSD3 LICENCE file.

phadej commented 5 years ago

No, I want that per project cabal always asks me the single question: which license.

I don't have "default" license. It really depends.

m-renaud commented 5 years ago

No, I want that per project cabal always asks me the single question: which license.

:/ That's... quite a bit departed from anything that's available now. Current non-interactive init doesn't ask any questions (as it shouldn't), and interactive init asks all questions. I guess you could add a configuration option where you list all the questions that you want to be asked, but that's a pretty complicated configuration for project initialization. There's already the -l/--licence flag to specify which license that you want to use from the command line. In any case, this is pretty far outside the scope of this issue and PR.

tom-audm commented 5 years ago

non interactive input should not populate license field.

Acknowledged, it does not :)

@m-renaud can you confirm this is the case? It looks to me (though I could very well be looking in the wrong place) that it defaults to BSD3: https://github.com/haskell/cabal/blob/6318eea53ee577c05a2e5ae222ef68cbd84f206f/cabal-install/Distribution/Client/Config.hs#L826

masaeedu commented 5 years ago

@hvr I'm by no means a power user, but the reason I want this isn't really related to expertise or lack thereof. It's mostly that I like using Haskell for tinkering with stuff, and so I frequently want to just cd into a folder and make a throwaway project that I can quickly start experimenting in. I don't want to think about licensing and build mechanisms and whatever else, I just want to add the lens package and play around with some snippet of arcane lens magic or something.

The Spago project in PureScript does a great job with this; I can just cd $(mktemp -d) && spago init && vim src/Main.purs and I'm off to the races.

Maybe this isn't the responsibility of cabal, and should be part of some other project that gives you a barebones setup to start hacking in, that's fine too. I'm just trying to clarify that at least for some people the need here isn't driven by "I don't yet have the knowledge to answer these questions", but rather that sometimes "none of the answers to these questions are important".

m-renaud commented 5 years ago

@tom-audm sorry for the super late reply. That configuration is for the default (but not automatic) licence type when going through the interactive prompt.

$ cabal init --interactive
<snip>
Please choose a license:
    1) ...
    ...
    N) BSD-3-Clause
<snip>

This configuration was already here before I started making changes to cabal init. The way that init is implemented for license is to leave the field entry in the .cabal file unless one is explicitly given.

See here for impl: https://github.com/haskell/cabal/blob/master/cabal-install/Distribution/Client/Init.hs#L273

That configuration defines how the ~/.cabal/config file is populated if it doesn't exist. Specifically in the init subsection you will get:

init
  -- interactive: False
  -- cabal-version: 1.10
  -- license: BSD3
  -- tests:
  -- test-dir:
  -- language: Haskell2010
  -- application-dir:
  -- source-dir:

Note that it is commented out, but it mirrors the "default" you will get if you simply hold down <ENTER> through the interactive init.

georgefst commented 4 years ago

Could we have cabal init add ghc-options: -Wall by default?

I'm constantly having to point out to beginners that they're missing all kinds of useful feedback from the compiler without this.

ulysses4ever commented 1 year ago

Should this be closed? It has 4/5 tasks checked, and the remaining one has to do with cabal.project:

cabal init pins the ghc version via with-compiler in cabal.project for executables

which seems rather orthogonal: there needs to be a separate discussion weather we want to involve init with project files (and I'm not so sure it's so good of an idea to pin the compiler by default).

The discussion here is very insightful, so I put the historical label on it: it will be easy to find even when closed.

ParetoOptimalDev commented 1 year ago

Could we have cabal init add ghc-options: -Wall by default?

That sounds like a good idea, but I bet it would be more likely to happen as a separate tiny issue that could be tagged beginner-friendly. Well, actually now that I think about it even though it's a simple change it might be a rather contentious one rehashing a lot of beginner vs power user experience arguments.

ulysses4ever commented 1 year ago

@ParetoOptimalDev init already puts -Wall in the cabal file it produces.

georgefst commented 1 year ago

@ParetoOptimalDev init already puts -Wall in the cabal file it produces.

Oh, neat, I missed this, but looks like init has been massively improved.

ulysses4ever commented 1 year ago

@georgefst yes, and not small part of it came from the discussion here.