From bc7e8fc53d0c55d0c00c216eb28a5b0d7c3bb831 Mon Sep 17 00:00:00 2001 From: malteneuss Date: Tue, 24 Oct 2023 16:29:44 +0200 Subject: [PATCH] Restructure Getting Started documentation to be more compact and beginner friendly (#9212) * Rewrite installation section of getting-started.rst * Rephrase creating a package documentation section * Length align titles in getting-started.rst * Fix length alignment in getting-started.rst * Update doc/getting-started.rst Co-authored-by: Artem Pelenitsyn * Update getting-started.rst * Update doc/getting-started.rst Co-authored-by: brandon s allbery kf8nh * Align package and script example in getting-started.rst * Align example package versions in getting-started.rst * Update doc/getting-started.rst Co-authored-by: Artem Pelenitsyn * Update doc/getting-started.rst Co-authored-by: Artem Pelenitsyn * Update doc/getting-started.rst Co-authored-by: Artem Pelenitsyn * Update doc/getting-started.rst Co-authored-by: Artem Pelenitsyn * Update doc/getting-started.rst Co-authored-by: Artem Pelenitsyn * Update doc/getting-started.rst Co-authored-by: Artem Pelenitsyn * Update doc/getting-started.rst Co-authored-by: Artem Pelenitsyn * Update doc/getting-started.rst Co-authored-by: Artem Pelenitsyn * Added review suggestions * Update doc/getting-started.rst Co-authored-by: Artem Pelenitsyn * Update doc/getting-started.rst Co-authored-by: Artem Pelenitsyn * Update doc/getting-started.rst Co-authored-by: Artem Pelenitsyn * Update doc/getting-started.rst Co-authored-by: Artem Pelenitsyn * Update doc/getting-started.rst Co-authored-by: Artem Pelenitsyn * Update doc/getting-started.rst Co-authored-by: Artem Pelenitsyn * Update doc/getting-started.rst Co-authored-by: Artem Pelenitsyn * Update doc/getting-started.rst Co-authored-by: Artem Pelenitsyn * Update doc/getting-started.rst Co-authored-by: Artem Pelenitsyn * Update getting-started.rst Reflow sentence to trigger CI * Update getting-started.rst * Update doc/getting-started.rst --------- Co-authored-by: Artem Pelenitsyn Co-authored-by: brandon s allbery kf8nh Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- doc/getting-started.rst | 175 ++++++++++++++++++++++------------------ 1 file changed, 96 insertions(+), 79 deletions(-) diff --git a/doc/getting-started.rst b/doc/getting-started.rst index 416a5dd77ae..4d5ebfe810f 100644 --- a/doc/getting-started.rst +++ b/doc/getting-started.rst @@ -1,90 +1,104 @@ -Getting Started with Haskell and Cabal -====================================== +Getting Started +=============== -Installing the Haskell toolchain --------------------------------- - -To install the Haskell toolchain follow the `ghcup instructions -`__. +Installing Cabal +---------------- +The easiest and recommended way to install the ``cabal`` command-line tool +on Linux, macOS, FreeBSD or Windows is through `ghcup `__. +It installs the “Haskell toolchain”, which includes Cabal, +the Haskell compiler `GHC `__ +and optionally other useful Haskell tools. Creating a new application -------------------------- -Let's start by creating a simple Haskell application from scratch where we'll -learn about a Haskell package's directory structure, how to run the executable, -and how to add external dependencies. +We create a minimal Haskell application to get a quick overview +of the ``cabal`` command-line tool: +1. How to initialize a Haskell package. +2. How files are organized inside a package. +3. How to compile Haskell files and run a resulting executable. +4. How to manage external dependencies. -Initializing the application -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Initializing an application +^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Start by initialising our ``myfirstapp`` project, these instructions work in -unix shells and PowerShell (if you're on Windows). +To initialize a new Haskell application, run .. code-block:: console - $ cabal init myfirstapp -n - -.. note:: ``myfirstapp`` stands for the directory (or path) where the project - will reside in, if omitted, ``cabal init`` will do its proceedings - in the directory it's called in. + $ cabal init myapp --non-interactive -.. note:: ``-n`` stands for ``--non-interactive``, which means that cabal will try to guess - how to set up the project for you and use the default settings, which will serve us - well for the purpose of this tutorial. - When setting up your projects in the future, you will likely want to omit ``-n`` - and do just ``cabal init``, so that cabal will interactively ask you - for the details on how the project should be set up - (while still offering reasonable defaults on every step). - Also, you can run ``cabal init --help`` to get more info on how ``cabal init`` can be used. - -This will generate the following files: +in a terminal. This generates the following files in a new ``myapp`` directory: .. code-block:: console $ tree . - └── myfirstapp + └── myapp ├── app │   └── Main.hs ├── CHANGELOG.md - └── myfirstapp.cabal + └── myapp.cabal -``app/Main.hs`` is where your package's code lives. +The ``myapp.cabal`` file is a package description file, commonly referred to as a “Cabal file”: -``myfirstapp.cabal`` is Cabal's metadata file which describes your package, -how it is built and its dependencies. We'll be updating this file in a -little bit when we add an external dependency to our package. +.. code-block:: cabal + cabal-version: 3.0 + name: myapp + version: 0.1.0.0 + -- ... -Running the application -^^^^^^^^^^^^^^^^^^^^^^^ + executable myapp + import: warnings + main-is: Main.hs + build-depends: base ^>=4.19.0.0 + hs-source-dirs: app + default-language: Haskell2010 -When we ran ``cabal init myfirstapp -n`` above, it generated a package with a single -executable named same as the package (in this case ``myfirstapp``) that prints -``"Hello, Haskell!"`` to the terminal. To run the executable enter the project's -directory and run it, by inputting the following commands: +It contains metadata (package name and version, author name, license, etc.) and sections +to define package components. Components can be used to split large codebases into smaller, +more managable building blocks. +A component can be of one of several types (executable, library, etc.) and describes, +among other things, the location of source files and its dependencies. +The ``myapp.cabal`` file above defines a single component named ``myapp`` of the executable type. +Inside the ``executable`` section, the ``build-depends`` field lists the dependencies of this component. -.. code-block:: console - cd myfirstapp - cabal run myfirstapp +The ``app/Main.hs`` file is where your executable's code lives: + +.. code-block:: haskell -You should see the following output in the terminal: + module Main where + + main :: IO () + main = putStrLn "Hello, Haskell!" + + +To run the executable, switch into the application directory with ``cd myapp`` and run .. code-block:: console - $ cabal run myfirstapp + $ cabal run myapp ... Hello, Haskell! -Notice that we didn't need to run a `build` command before we ran ``cabal run``. -This is because ``cabal run`` automatically determines if the code needs to be (re)built -before running the executable. -If you just want to build a target without running it, you can do so with ``cabal build``: +This command automatically determines if the executable needs to be (re)built +before running the executable. With only one executable component in the package, +``cabal run`` (without a component name) is smart enough to infer it, so the name can be omitted. + +If you just want to build the executable without running it, run: + +.. code-block:: console -``cabal build myfirstapp`` + $ cabal build + Resolving dependencies... + ... + Building executable 'myapp' for myapp-0.1.0.0.. + [1 of 1] Compiling Main ( app/Main.hs, /home/.../myapp/dist-newstyle/build/.../myapp-tmp/Main.o ) + Linking /home/.../myapp/dist-newstyle/build/.../myapp Adding dependencies @@ -103,16 +117,16 @@ terminal with some embellishment. need to update the package index, you can do this by running ``cabal update``. -In our ``myfirstapp.cabal`` file we'll update the ``build-depends`` attribute of -the ``executable myfirstapp`` section to include ``haskell-say``: +In our ``myapp.cabal`` file, we will update the ``build-depends`` field of +the executable section to include ``haskell-say``: .. code-block:: cabal - executable myfirstapp + executable myapp import: warnings main-is: Main.hs build-depends: - base ^>=4.14.3.0, + base ^>=4.19.0.0, haskell-say ^>=1.0.0.0 hs-source-dirs: app default-language: Haskell2010 @@ -132,8 +146,7 @@ Next we'll update ``app/Main.hs`` to use the ``HaskellSay`` library: import HaskellSay (haskellSay) main :: IO () - main = - haskellSay "Hello, Haskell! You're using a function from another package!" + main = haskellSay "Hello, Haskell!" ``import HaskellSay (haskellSay)`` brings the ``haskellSay`` function from the module named ``HaskellSay`` into scope. The ``HaskellSay`` module is defined in @@ -143,11 +156,10 @@ Now you can build and re-run your code to see the new output: .. code-block:: console - $ cabal run + $ cabal run myapp ________________________________________________________ / \ - | Hello, Haskell! You're using a function from another | - | package! | + | Hello, Haskell! | \____ _____________________________________________/ \ / \ / @@ -166,42 +178,47 @@ Now you can build and re-run your code to see the new output: / / / / \ \ /____/ /____/ \____\ -Run a single-file Haskell script --------------------------------- +Running a single-file Haskell script +------------------------------------ -Cabal also enables us to run single-file Haskell scripts -without creating a project directory or ``.cabal`` file. -The cabal directives are placed in the file within a comment. +Cabal also supports running single-file Haskell scripts like +the following file named ``myscript``: .. code-block:: haskell - + #!/usr/bin/env cabal {- cabal: - build-depends: base, split + build-depends: + base ^>=4.19.0.0, + haskell-say ^>=1.0.0.0 -} - import Data.List.Split (chunksOf) + import HaskellSay (haskellSay) main :: IO () - main = getLine >>= print . chunksOf 3 + main = haskellSay "Hello, Haskell!" -This can be run using ``cabal run myscript``. -On Unix-like systems this can be run directly with execute permission. +The necessary sections of a ``.cabal`` file are placed +directly into the script as a comment. + +Use the familiar ``cabal run`` command to execute this script: .. code-block:: console $ cabal run myscript - $ chmod +x myscript - $ ./myscript - -Project metadata can also be included: +On Unix-like systems, a Haskell script starting with ``#!/usr/bin/env cabal``, like the one above, +can be run directly after setting the execute permission (+x): -.. code-block:: haskell +.. code-block:: console - {- project: - with-compiler: ghc-8.10.7 - -} + $ chmod +x myscript + $ ./myscript + ________________________________________________________ + / \ + | Hello, Haskell! | + \____ ____________________________________________/ + \ ... / See more in the documentation for :ref:`cabal run`.